Nao000のぶろぐ

蝶を追っている少年になりたい

オープン・クローズドの原則をElixirで試す_No1

「多分こういうことだろう」という気持ちで試します。

目次

  • はじめに
  • サンプルお題
  • バージョン情報
  • ディレクトリ構成
  • 流れと各ファイルの説明
  • ソース
  • 実行の様子
  • 参考資料
  • 余談

はじめに

「オープン・クローズドの原則」自体への理解度は「これって要するにストラテジーパターンなのでは?」という程度です。意識して最初から実装したことはありません。

サンプルお題

ファイルに書き込む処理を考えます。textファイル, csvファイル, imageファイルの3種類に書き込む write() を呼び出します

バージョン情報

/home/trial/ocp_file # elixir -v
Erlang/OTP 22 [erts-10.7.1] [source] [64-bit] [smp:2:2] [ds:2:2:10] [async-threads:1]

Elixir 1.9.4 (compiled with Erlang/OTP 22)

ディレクトリ構成

ocp_file
├── lib
│   ├── controller
│   │   └── file_service.ex
│   ├── files
│   │   ├── csv.ex
│   │   ├── image.ex
│   │   ├── myfile.ex
│   │   └── text.ex
│   └── ocp_file.ex

流れと各ファイルの説明

クラス図っぽいものを以下に記載します。

ocp_file.ex ─> file_service.ex ─> myfile.ex <--- text.ex, csv.ex, pdf,ex, image.ex

  • ocp_file.ex
    • 実際にコマンドから実行するモジュール
  • file_service.ex
    • 3種類のモジュールから1つのモジュールを返すモジュール
  • myfile.ex
    • behaviour で振る舞いを定義するモジュール
    • behaviour とは elixir において仕様をモジュールに要求する仕組みです
    • Java, PHP などの interface と似たものです
    • elixir の behaviour は実装を強制しません
  • text.ex, csv.ex, image.ex
    • myfile.ex の振る舞いを実装するモジュールたち

ソース

ocp_file.ex

    defmodule OcpFile do
      def index(type) do
        file = Controller.FileService.newFileModule(type)

        file.write()
      end
    end

file_service.ex

    defmodule Controller.FileService do
        def newFileModule("csv"),   do: Files.Csv
        def newFileModule("text"),  do: Files.Text
        def newFileModule("image"), do: Files.Image
    end

myfile.ex

    defmodule Files.Myfile do
        @callback write() :: String.t()
    end

text.ex

    defmodule Files.Text do
        @behaviour Files.Myfile

        @impl Files.Myfile
        def write() do
            "TextFile"
        end
    end

csv.ex

    defmodule Files.Csv do
        @behaviour Files.Myfile

        @impl Files.Myfile
        def write() do
            "CsvFile"
        end
    end

image.ex

    defmodule Files.Image do
        @behaviour Files.Myfile

        @impl Files.Myfile
        def write() do
            "ImageFile"
        end
    end

実行の様子

    /home/trial/ocp_file # iex -S mix
    Erlang/OTP 22 [erts-10.7.1] [source] [64-bit] [smp:2:2] [ds:2:2:10] [async-threads:1]

    Interactive Elixir (1.9.4) - press Ctrl+C to exit (type h() ENTER for help)
    iex(1)> OcpFile.
    MixProject    index/1
    iex(1)> OcpFile.index("text")
    "TextFile"
    iex(2)> OcpFile.index("csv")
    "CsvFile"
    iex(3)> OcpFile.index("image")
    "ImageFile"
    iex(4)>

OcpFile.index の引数に応じて異なるモジュールの振る舞いを実行できていることが確認できました。

参考資料

余談

書籍「clean architecture 達人に学ぶソフトウェアの構造と設計」のオープンクローズド原則の説明に出てくるクラス図が今の自分には良く分からない。実際のソースを見たい。最終的に Interactor コンポーネントを実行するのかな。