web-dev-qa-db-ja.com

標準ファクトリは常に新しいインスタンスを生成する必要がありますか?

factory-methodに関する私の理解によれば、factoryは常にnewインスタンスを返す必要があります。つまり、キャッシュがないことを意味します。したがって、本質的には、ファクトリメソッドが呼び出されるたびに、常にnewインスタンスが返されるはずです。それ以外の場合は、ファクトリメソッドパターンではありません。たとえば、次のコードはファクトリではありません。

_public class UserManagementActionListenerFactory extends AbstractListenerFactory {
    private static final String ENABLE_USER = "enableUser";
    private static final String DISABLE_USER = "disableUser";
    private static final String DELETE_USER = "deleteUser";
    private static final String CONVERT_USER = "convertUser";
    private Map<String,ActionListener> mapper;
    public ActionListenerMapping() {
        mapper = registerListeners();
    }
    private Map<String, ActionListener> registerListeners() {
        Map<String,ActionListener> actionListenerMap = new HashMap<>();
        actionListenerMap.put(ENABLE_USER, new EnableActionListener());
        actionListenerMap.put(DISABLE_USER, new DisableActionListener());
        actionListenerMap.put(DELETE_USER, new DeleteActionListener());
        actionListenerMap.put(CONVERT_USER, new ConvertUserActionListener());
        return actionListenerMap;
    }
    @Override
    ActionListener getActionListener(String s) {
        return mapper.get(s);
    }
}
_

UserManagementActionListenerFactoryは、UserManagementBeanと呼ばれるbeanで使用されます。

_public class UserManagementBean {
    AbstractActionListenerFactory factory;
    @PostConstruct
    public init() {
        factory = new UserManagementActionListenerFactory();
    }
    //... irelevant code
    public void handleActionEvent(ActionEvent e) {
        String componentId = e.getComponent().getId();
        factory.getActionListener(componentId).processAction(e);
    }
}
_

このActionListenerMapperは、構築時にActionListenerのすべての具象サブクラスを初期化したため、getActionListener(s)が呼び出されるたびに、常にキャッシュされたを返しますActionListenerインスタンス。 私の理解が間違っている場合はここに教えてください

ただし、本の最初のitemEffective Java

静的ファクトリメソッドの2番目の利点は、コンストラクターとは異なり、呼び出されるたびに新しい新しいオブジェクトを作成する必要がないことです。

さらに、Effective Javaもこう言っています:

静的なファクトリメソッドは、Design PatternsFactory Methodパターンと同じではないことに注意してください[Gamma95]

質問:通常、ファクトリメソッドは常に_new instance_インスタンスを返す必要がありますか?

3
Rui

他の回答は、ファクトリーとファクトリーパターンの使用に関するほとんどの懸念に適切に対処しますが、クラスは必ずしもファクトリーではないであることを追加したいと思います。つまり、できるまではファクトリーである理由についての適切な理由です。

明らかに、クラス名が_-Factory_で終わるため、ファクトリはファクトリではありません。確かに、あなたの質問と回答は、ファクトリーの予想される動作に関する問題をすでにカバーしていますが、何か(キャッシュされているかどうか)へのアクセスを提供するクラスを作成するだけでは、ファクトリーにはなりません。

あなたのコード(これ以上のコンテキストなし)に基づいて、文字列を受信して​​アクションリスナーを返すクラス/インターフェイスメソッドがファクトリと見なされる理由を理解できません。あなたのクラスと、getActionListenerByID(string)の代わりにgetActionListener(string)を使用したActionListenerRepositoryという名前の同一のクラス(または同じもの)の間に違いはありません。その上、メソッドの呼び出し元(どうやら、コードから)は、返されたインスタンスが作成/追加された方法、時期、または理由を本当に知りません。

パターンは適切であり、パターンを適切に利用する方法を知っているため、優れていますが、semanticsはスキップしないでください。

だから、あなたが明確にするべきことは次のとおりです:なぜこれはあなたとあなたのオブジェクトモデルのファクトリーなのですか

編集する

質問が特定のユースケースを含むように編集された後、コメントでの交換後、回答が更新されます。

コードレビューアの観点から見てみましょう。コーディングはすべて意味およびセマンティクスに関するものです。高レベルのコードを書くときは、誰かに(つまり、コンピュータにではなく)指示を与えると考えてください。工場について言及するとき、factoryが思い浮かびます。工場は物を生産しますが、通常は物を保管しません。それらを生成します。そのため、レビュー担当者が実際には保管場所である工場を見ると、意図した意味と期待した意味の境界が交差しています。 2つを混同すると、驚きを最小限に抑えるという原則に違反します。これは、期待どおりの結果が得られないという「専門用語」です。

「クライアント」が期待することはそれほど重要ではありません。クライアントがファクトリーから何かを取得してそれ自体をキャッシュするのと同じくらい簡単だからです。しかし、直感的な意味で考えてみてください。それを使用する必要があるたびに、工場に何かを要求しますか?

要するに、あなたのファクトリーがファクトリーではない主な理由は、あなたがそのように構築することもないそのようにそれを使用することもないであるということです。ファクトリーの予想される使用法は次のとおりです。

  • 事を要求する時々(「毎回ではない」のように)。

  • 何か新しいことを期待してくださいほとんどの場合

  • 返されたオブジェクトを後で使用するために保持します。

あなたのクラスはまったく逆の方法で使用されます。ファクトリの結果を要求します使用ごとに、とにかく同じオブジェクトを期待します。なぜなら、要求ごとに異なるアクションリスナーを取得したいのですが、それらをクライアントクラス内に保存しないでください。

最後だが大事なことは

cachingについても触れたので、ファクトリーは、思っているように見えるようにキャッシュとペアにされていません。通常、ファクトリは複数の戻り値のオブジェクトをキャッシュしません。工場は即時リターンの新しいインスタンスをキャッシュします。これは、アプリケーションのユーザーが他のことをしている間に、ファクトリが新しいインスタンスを構築するいくつかのタスク/スレッドを生成できるため、次にリクエストが行われたときに、新しいインスタンスがat準備ができている、したがってすぐに返されます、そしてもちろん、(おそらく永久に)ファクトリーによって忘れられます(何らかの理由でインスタンスのカウントなどを行っている場合を除きます)。

したがって、ファクトリのコンテキストでは、キャッシングは、事前に作成されたオブジェクトを毎回同じオブジェクトを配布するのではなく、すぐに配布するという意味で、より有用な意味を持っています。インスタンス参照を誰かに渡すと、「完全な」所有権はすぐに失われます。これで、他の誰かがyourオブジェクトで遊ぶことができるので、工場とし​​て、「これらのオブジェクトはyoursを渡した後ですか?」

1
Vector Zita

いいえ、ジョシュア・ブロックは正しいです(彼は通常そうです)。ファクトリまたはファクトリメソッドは、呼び出しごとに新しいインスタンスを作成する必要はありません。それは、実際には、工場の利点の1つです。

このように見てください。ファクトリのポイントは、クライアントプログラマがリソースの正確な型階層を理解する必要をなくすことです。つまり、頑丈なStandardStructureをおしゃれなImprovedStructureに置き換えて、クライアントコードを変更せずにロールアウトできるということです。これは良いことです。

同様に、工場の顧客はオブジェクトのライフサイクルの詳細を理解する必要はありません。それらは1つのクライアントだけに適している場合もあれば、多くのマスターにサービスを提供するのに十分に賢い場合もあります。それらは、任意の数の呼び出しを処理できるか、使用に伴って減衰するリソース(おそらくエントロピーのビット)を持つことができます。とにかく-ファクトリはオブジェクトのライフタイムを正しく管理する方法を知っているので、クライアントプログラマはそれを理解する必要がありません。その結果、タイプ階層の詳細と同じように、保守性が向上します。

4
Kilian Foth

この質問の考えられる解釈は2つあります。

  1. ファクトリメソッドは常に新しいインスタンスインスタンスを返す必要があります-「ファクトリメソッド」という用語は、このように動作する関数を記述するためにソフトウェア開発で広く受け入れられているためですか?

したがって、解決策は、メソッドに「factory」という用語を含む名前とは異なる名前を付けることです。

または

  1. ファクトリメソッドは常に新しいインスタンスインスタンスを返す必要があります-そうでない場合、エラーが発生しやすくなりますか?

だから解決策はそのようなメソッドを書かないことですか?

最初の質問への答えは間違いなく「いいえ」です。ソフトウェアエンジニアリングの多くの用語と同様に、「ファクトリーメソッド」という名前には複数の有効な意味があり、「数学的に厳密な定義」はありません。ファクトリは、異なるサブタイプから選択し、newで作成しなければならなかったオブジェクトを配信することが期待されますが、それらがnewを独自に呼び出すか、異なる方法で実装されるかは問題ではありません。工場で好きな規則を定義できます。また、特定の工場で常に新しいインスタンスを作成する必要があるとシステムが考える正当な理由がある場合は、先に進んで、チームでこの規則を確立してください。または、ファクトリメソッドの動作を変えることができます。これを禁止する「法則」はありません。

2番目の質問に対する答えは、ファクトリメソッドの入力値が同じである場合、通常、ファクトリはimmutable objectの同じインスタンスを複数回配信しても安全です。上記の例では、すべてのActionListenerは不変であるか、不変の方法で処理されます。返されたオブジェクトが不変でない場合、同じインスタンスを2回返すと、あらゆる種類の望ましくない副作用が発生する可能性があるため、常に新しく作成されたインスタンスを配信することでエラーが発生しにくくなります。

可変オブジェクトでもオブジェクトの再利用が可能な別の設計があります。それは、ファクトリがリソースプール(たとえば、スレッドプール)を管理し、ユーザーがそこから「リソースオブジェクト」を割り当て、使用後に使用済みオブジェクトを返す必要がある場合です明示的に。もちろん、そのような場合に「ファクトリー」という名前が相応しいかどうかは議論の余地があります。

コード例についてメモを追加しましょう。その関数の呼び出し元getActionListenerの観点からは、この関数の呼び出しが常に新しいオブジェクトを生成するか、同じ(不変)オブジェクトは、同じ入力パラメーターを使用した2つの呼び出しで返されます。したがって、そのコードスニペットが「ファクトリ」または「マッパー」と呼ばれる方がよい場合は、ほとんどが好みの問題です。名前は、関数が内部でどのように実装されているかに依存するべきではありません。

2
Doc Brown