web-dev-qa-db-ja.com

プロトタイプパターンに関する質問

私はさまざまなデザインパターンについて学んでおり、この特定のパターンを理解する上で不可欠な部分が欠けていると強く感じています。

私が閲覧したすべてのWebサイトとGoFの本には、クローンメソッドがあります。私の理解では、オブジェクトのさまざまなバージョンが必要なときに複製できるオブジェクトのタイプがありますが、(Javaのように)「new」コマンドを使用して各オブジェクトを手動で作成する必要はありません。これにより、具体的な実装を隠すことができます。したがって、クローンを作成するときは、クローンを少し調整して、そのオブジェクトを最初に作成する方法を難しい方法で知らなくても、必要なものにすることができます。これは私の考えは正しいですか?

また、これによりサブクラス化が減り、その後、作成する必要のあるクラスの数が減ると言われています。この部分はよくわかりません。誰かが私がこれを理解するのを手伝ってもらえますか?

私の最後の質問は、抽象ファクトリ(またはファクトリメソッド)パターンについてです。これらのファクトリパターンとプロトタイプパターンは、新しいオブジェクトの作成時に具体的な実装を隠そうとしているように感じます。どちらかを選択するのはいつですか?

皆さん、ありがとうございました!

40
Silverbolt

プロトタイプパターン

プロトタイプは、元のオブジェクトとは異なるクローンオブジェクトになります。オリジナルの状態は、クローン作成時のクローンと同じです。その後、各オブジェクトは状態変化を受ける可能性があります。これは、オリジナルをコピーしてから、いくつかの場所でコピーを変更するのと似たようなものと考えることができます。

  • DVDの複製:複数のコピーを作成するためのマスターDVDの複製
  • レポートオブジェクト:GUIに渡される処理済み情報を含むレポートオブジェクトについて考えてみます。元のレポートには、データが昇順で含まれています。これで、このパターンを使用して、同様のレポートを作成できますが、データは降順で並べ替えられます。

利点

  • パフォーマンス:クローン作成( MemberwiseClone を使用)は、新しいオブジェクトを新たに作成する( new operator を使用)よりもかなり安価です。ディープコピーを実行するには、MemberwiseClose()をオーバーライドする必要があることに注意してください。
  • オブジェクトは、事前のインスタンス化を主張することなく、非常に動的に複製できます。最初に作成されたオブジェクトは、アプリケーションの実行中にいつでも作成でき、それ以降の複製はいつでも実行できます。

いつ使用するか

  • インスタンス化するクラスが実行時に、たとえば動的ロードによって指定されている場合。
  • クラスのインスタンスが、いくつかの異なる状態の組み合わせのうちの1つを持つことができる場合。毎回適切な状態でクラスを手動でインスタンス化するよりも、対応する数のプロトタイプをインストールしてクローンを作成する方が便利な場合があります。

ファクトリパターンとの比較

プロトタイプパターンを使用すると、オブジェクトは、クラスや作成方法の詳細を知らなくても、カスタマイズされたオブジェクトを作成できます。したがって、この側面は、ファクトリメソッドパターンによく似ているように見えます。これらのパターンの両方で、クライアントは、自身の構造について何も知らなくても、派生クラスオブジェクトを作成できます。

ただし、2つのパターンの違いは、Factory Methodの場合、存在しないオブジェクトタイプの1つのオブジェクトをfresh creationとして作成することに集中するという事実です(Creatorクラスの正確なサブタイプを理解することにより)。 Prototypeパターンは、クラス自体、特にself duplicationアクションの派生クラスを使用します。


ファクトリメソッドパターン

このパターンでは、クライアント(またはコンシューマー)は、クラス階層から特定のタイプのオブジェクトを作成者(またはファクトリ)に要求します。ファクトリクラスのCreatorメソッドは、特定のオブジェクトの作成を派生クラスに委任し、クライアントから要求されたタイプのクラスのオブジェクトを返します。本質的に、クラス階層の複数のオブジェクトを作成するための単一の連絡先があります。

これは、航空券カウンター(コントローラー)に行き、チケットの種類(ファーストクラス、エグゼクティブ、エコノミー)を選択してチケットを要求することと考えることができます。オブジェクト表現では、ファーストクラスとエコノミーチケットの両方が基本チケットクラスから派生している場合でも、ユーザーはチケットがどのように生成されるかを気にしません。

いつ使用するか

  • 柔軟性が重要です(低結合)
  • オブジェクトはサブクラスで拡張できます
  • あるサブクラスが別のサブクラスよりも選択される特定の理由があります。このロジックはファクトリメソッドの一部を形成します。
  • クライアントは、並列階層のサブクラスに責任を委任します。


抽象ファクトリパターン

特定のファクトリクラスがすでに存在します。ただし、ファクトリの方法は少し異なります。各メソッドはインスタンスを生成できます。クライアントは適切な方法を選択してインスタンスを取得できます。

[〜#〜] mvc [〜#〜] ベースの完璧な建築設計の例をとると、クライアントはビジネスコントローラークラスになり、コンクリート製品はすべてビジネスエンティティになります。ファクトリは補助(ヘルパー)コントローラーです。これらは、ビジネスコントローラーからの要求に関連して機能します。

いつ使用するか

  • このシステムは、製品の作成方法に依存しないことが期待されています。製品がどのように構成され、表現されるかについての独立性を期待することさえあります。製品という用語は、クライアント開発者がそのメソッドを呼び出すことによって利用する必要がある、最終的に得られるオブジェクトに適用されます。
  • 複数の製品ファミリの1つで構成可能である必要があるシステム。したがって、ファミリの実際の選択は、コーディング時ではなく、後の構成時に行われます。
  • 製品ファミリは、常に連携して動作するように設計されています。
  • 作成は製品のライブラリ用です。ここでさらに気になるのは、実装ではなく、関連するインターフェースです。
61

見た目だけでプロトタイプパターンができました。

サブクラス化を減らす方法

MineCraftを作成していて、さまざまな種類のブロック(土、石など)ごとにプロトタイプパターンを使用しているとします。すべてのプロトタイプオブジェクトは実際には同じクラスBlockですが、各オブジェクトには異なるプロパティが設定されているため、次のように表示と動作が異なります。

_prototypes.dirt = new Block;
prototypes.dirt.texture = new Image("dirt.jpg");
prototypes.dirt.hardness = 1;

prototypes.stone = new Block;
prototypes.stone.texture = new Image("stone.jpg");
prototypes.stone.hardness = 9;
_

したがって、_new DirtBlock_または_new StoneBlock_を記述する場所をサブクラス化する代わりに、prototypes.dirt.clone()またはprototypes.stone.clone()を記述します。サブクラス化は必要ありませんが、必要に応じてサブクラス化するオプションがあります。

ファクトリパターンとの違い

ファクトリパターンの代わりにプロトタイプパターンを選択する場合については、2つの状況が考えられます。

  1. プロトタイプのリストを反復処理することはできますが、抽象ファクトリのすべてのメソッドを反復処理することはできません^。上記のコードから続けると、次のようなランダムなブロックを作成できます。

    prototypes.allValues().objectAtIndex(Rand() % prototypes.size()).clone();

    ファクトリメソッドを使用してブロックを作成している場合、ランダムなブロックを取得するのは困難です。

  2. オブジェクトの作成に費用がかかるが、コピーに費用がかかる場合は、プロトタイプパターンの方が効率的です。たとえば、次のファクトリメソッドを使用します。

    _Image loadUserImage() { 
        //loads from disk. will be slow
        return new JPEGImage("path/to/user/image.jpg"); 
    }
    _

    このメソッドが繰り返し呼び出される場合は、次のようなプロトタイプを使用する方が効率的です。

    _Image loadUserImage() {
        //copy in memory. will be fast
        return userImagePrototype.clone();
    }
    _

^使用している言語に応じて実際にメソッドを反復処理できるため、これは白い嘘ですが、配列を反復処理する方が、リフレクション/イントロスペクションよりも複雑ではないため、おそらくより良い解決策です。

27
Tom Dalling

プロトタイプを使用することの効率の向上は、私の心の中で疑わしいです。ほとんどの言語では、cloneメソッド自体がnewの呼び出しを実行して、それ自体の新しいオブジェクトインスタンスを構築するため、効率は向上しません。

プロトタイプパターンを使用することで私が見る唯一の利点は、利便性の1つです。クローンを使用すると、オブジェクトの正確なコピーが得られるため、新しいオブジェクトの属性を自分で同じ値に設定する必要がなくなり、苦労する可能性があります。ディープコピー付き。

15
rb39999