どのパターンを使うべきか
- 引数が不要な場合→コンストラクタ
- Strategyを選ぶときなど。
- 順番に構築する必要がある→Builderを作る
- 例: SQLを構築するビルダ
- それ以外→まずFactory Methodを作る
- その中でBuilderの方が良いと思ったら、Builderに作り変える。
ものすごく単純化していますが、基本的にはこれでいいと思います。
理由
まず、「順番に構築する必要がある」場合は、 Builderで対応する必要があります。 こちらは説明不要だと思うので省きます。
Factory Methodにすると引数が多くなってしまう場合
この場合にまず考えるべきことは、 「引数をValue Objectにできないか」です。 例えば、公開鍵でSFTP接続したいときに以下の引数を渡す場合を考えます。
- 接続先ホスト名
- 接続先ポート番号
- 接続先ユーザ
- 秘密鍵
- 秘密鍵パスフレーズ
- known_hosts
単純に実装すると、Stringが5つ、intが1つ並んだ、以下のようなメソッドができます。 (コンストラクタでも同じ)
Sftp sftp = Sftp.of(host, port, user, privateKeyPath, passPhrase, knownHostsPath);
しかし、1と2、4と5はひとまとまりに考えることができます。
PrivateKey privateKey = PrivateKey.of(privateKeyPath, passPhrase);
Server server = Server.of(host, port);
Sftp sftp = Sftp.of(server, user, privateKey, knownHosts);
さらに、privateKeyとuserを「認証情報」としてひとまとまり、 serverとknownHostsを「接続先情報」としてひとまとまりにすると、 以下のようにできます。
PrivateKey privateKey = PrivateKey.of(privateKeyPath, passPhrase);
Server server = Server.of(host, port);
AuthInfo authInfo = AuthInfo.of(user, privateKey);
SshServer sshServer = SshServer.of(server, knownHosts);
Sftp sftp = Sftp.of(sshServer, authInfo);
引数がたった2つになりました。 しかも、このPrivateKey, Server, AuthInfo, SshServerは他でも 使える可能性が高いクラスです。 AuthInfoのファクトリーメソッドの引数を変えるだけで、 パスワード認証にも対応可能です。
オプショナルな引数が多い場合
これは2つのパターンがあります。
- デフォルト値がだいたい決まっている
- オプショナル項目そのものが多い
後者については、Builderパターンが適している場合もあります。 前者の場合は、引数の省略で対応できる場合が多いです。 先程のSftpクラスだと、通常は、ポート22を使います。 したがって、以下のようにかけます。
Sftp sftp = Sftp.of(host, user, privateKeyPath, passPhrase, knownHostsPath);
Value Objectを使うと、以下のように書けます。 引数が多いと組み合わせが爆発しますが、 Value Objectを使っているため、ほとんど影響がありません。
PrivateKey privateKey = PrivateKey.of(privateKeyPath, passPhrase);
Server server = Server.of(host); // 違うのはここだけ
AuthInfo authInfo = AuthInfo.of(user, privateKey);
SshServer sshServer = SshServer.of(server, knownHosts);
Sftp sftp = Sftp.of(sshServer, authInfo);
最初から完璧さを求めない
これでは不十分で、もっと複雑なパターンを使う必要があるかもしれません。 ただ、それはいつになるのかは分かりません。いわゆるYAGNIの法則です。 必要になったときだけ、追加するので十分だと思います。