web-dev-qa-db-ja.com

APIクラスのコンストラクターを公開する必要がありますか、それともファクトリを常に使用できますか?

私はこれが以前に尋ねられたことがないようであるか、少なくともどこにもそれを見つけることができないことに驚いています。これはもっと哲学的な質問だと思いますが、ファクトリを使用する特別な理由はありますかnot(ファクトリパターンの利点は誰もが知っていると思いますので、ここでは説明しません) )パブリックAPIクラスをインスタンス化するためですか?言い換えれば、そのようなクラスにパブリックコンストラクターを使用する理由は、コーディングが高速であるという事実以外にありますか?これは、本当に単純で小さなAPIに適している可能性があります。

1
rory.ap

シンプルさ。

ファクトリ(抽象ファクトリではなく、最も単純な形式の通常のファクトリ)パターンは、クラスの初期化に複雑なビジネスロジックが必要な場合に役立ちます。このロジックをコンストラクターに配置することは、多くの場合問題外であり、クラス内の静的メソッドにロジックを配置すると、クラスが汚染されます(場合によっては検出可能性が低くなります)。

初期化ロジックが非常に単純な場合は、追加のクラスを作成してこのロジックを複製する必要はありません。そうすることで、LOCが増え、コードの保守性が低下し、全体的な作業が増えますが、統一のメリットは1つだけです(つまり、コードベースのすべてのオブジェクトは対応するファクトリを通じて作成される必要があります)。

例:

  1. ProductクラスコンストラクターはProductId id引数を受け入れ、データベースから追​​加情報をロードします。

    これはファクトリパターンの完璧な候補です。データベースアクセスはコンストラクターで発生するべきではありません。コンストラクターは非常に単純で高速であることが期待されており、例外をスローすることは期待されていません。

  2. Priceクラスコンストラクターはint amountおよびMoney unit引数を取ります。

    これはあなたがしない工場を持っている完璧な例です。なぜあなたは?それが行う唯一のことは、コンストラクターに引数を渡すことです。より複雑で、メリットはありません。

  3. Rebateクラスコンストラクターはint amountおよびAmountUnit unit引数を取ります。金額と単位に応じて、合計金額のパーセンテージ、絶対リベート、または「この製品は無料」(ビジネスの場合、100%リベートとは異なる方法で検討する必要があります)を表すことができます。

    ここで、コンストラクター内でコードの記述を開始できます。コードが大きくなった場合は、ファクトリに移行します。一見すると、ビジネスルールの複雑さが専用のファクトリクラスの作成を正当化するかどうかは不明です。 Rebateクラスのソースコードの存続期間中、チームはコンストラクター内の初期化とファクトリクラスを介した初期化を数回切り替えることがあります。

4

注目に値する1つの例として、Javaコレクションがあります。Collection<T>の下の型階層には、かなりの数のサブインターフェイスと具象型があります。各データ構造には長所と短所があります。上記に追加のプロパティがある場合もあります。また、スレッドの安全性や、他の方法で混乱した構造(一部のマップやセットなど)での挿入順序の維持など、特定のデータ構造の基本を超えています。

これを支援するために、JFCに組み込まれているいくつかのファクトリがあります。 Collectionsクラスは、既存のコレクションを変更できないコレクションにラップできます(コレクション自体は不変であり、コレクションに含まれる要素は不変である場合とそうでない場合があります)。コレクションを同期ラッパーでラップして、基本的なスレッドセーフを提供できます(同時実行パッケージの特殊なコレクションと同様に機能しない場合があります)。シングルトンコレクション内の単一の要素をラップできます(シングルトンパターンではなく、1つの要素を持つコレクションのみ)。

これらはすべてファクトリメソッドです。コンストラクタを直接呼び出すのではなく、オブジェクトを返すメソッドを呼び出します。新しいオブジェクトを明示的に作成することなく、追加の制約(可変性や同時実行性など)を使用して、自分の制御から(フレームワークやライブラリなどで)作成される可能性のあるコレクションを適応させることができるため、便利です。

Guavaはコレクションユーティリティでこれをさらに一歩進めています 。さまざまなプロパティを持つさまざまなタイプの新しいコレクションを作成するための、さらに多くのファクトリメソッドが含まれています。このようにすると、構造がより簡潔で表現力豊かになります。概念レベルでは、JFC Collectionsクラス以外に説明することはありませんが、Googleはこれらのファクトリを構築するためにかなりの努力を払ったことに注意します。値


これは、ファクトリが役立つ可能性がある場所の一例にすぎませんが、常にを使用する必要がありますか?

工場を使用する主な理由は2つあります。

  • 建設は複雑です。コンストラクターは単純である必要があります。追加のデータや処理が必要ですか?たとえば、ファイルまたはデータベースからのデータのロードはコンストラクターで実行するべきではありませんが、オブジェクトを作成するために必要になる場合があります。その余分な処理をファクトリメソッドに入れる方が理にかなっています。

  • 実装は、オブジェクトが作成される場所に関係なく変化する必要があります。将来、別の実装を使用したいですか?プラグインアーキテクチャによって可変でしょうか?テスト用にモックを注入しますか?

多くのAPIクラスはこれらのテストに失敗します。たとえば、文字列の実装は常に同じであり、余分な処理はそれほど必要ありません。ほとんどの言語/ランタイムのコアAPIは、次の2つの理由からかなり単純なはずです。

  • 単純な問題を解決しています。 「文字列内の一連の文字を表します。」

  • 幅広い問題に適用でき、再利用性が高い必要がありますが、これは高度に専門化されたクラスとは相容れません。

この単純さは、ファクトリを使用するための要件と矛盾しています。型が十分に単純で、引数なしまたは引数1のコンストラクターだけが必要であり、実装が常に同じである場合、ファクトリを使用しても意味がありません。

ここでは、coreAPI(文字列、コレクション、ストリーム)について話しているのであって、データベース、GUI、またはその他のより複雑なライブラリのようなものではないことに注意してください。

0
user22815