web-dev-qa-db-ja.com

プロキシ、デコレータ、アダプタ、およびブリッジパターンはどのように異なりますか?

私はプロキシパターンを見ていましたが、デコレータ、アダプタ、ブリッジのパターンに非常によく似ているように思えます。私は何かを誤解していますか?違いは何ですか?他のプロキシパターンに対してプロキシパターンを使用するのはなぜですか?過去に実際のプロジェクトでどのように使用しましたか?

377
Charles Graham

プロキシ、デコレータ、アダプタ、およびブリッジはすべて、クラスの「ラップ」のバリエーションです。しかし、それらの用途は異なります。

  • Proxyは、オブジェクトを遅延インスタンス化する場合、リモートサービスを呼び出しているという事実を非表示にする場合、またはオブジェクトへのアクセスを制御する場合に使用できます。

  • デコレータは「スマートプロキシ」とも呼ばれます。これは、オブジェクトに機能を追加するときに使用されますが、そのオブジェクトのタイプを拡張することではありません。これにより、実行時に実行できます。

  • Adapterは、抽象インターフェイスがあり、そのインターフェイスを、機能は似ているがインターフェイスが異なる別のオブジェクトにマッピングする場合に使用されます。

  • BridgeはAdapterに非常に似ていますが、抽象インターフェースと基礎となる実装の両方を定義するとき、それをBridgeと呼びます。つまりレガシーまたはサードパーティのコードに適応していない場合、すべてのコードの設計者ですが、異なる実装を交換できる必要があります。

  • Facadeは、1つ以上のクラスのサブシステムへの高レベル(読み取り:シンプル)インターフェースです。表現するために複数のオブジェクトを必要とする複雑なコンセプトがあるとします。そのオブジェクトのセットに変更を加えると、どのオブジェクトに呼び出す必要があるメソッドがあるかが常にわからないため、混乱を招きます。これが、オブジェクトのコレクションに対して行うことができるすべての複雑な操作に対して高レベルのメソッドを提供するFacadeを作成するときです。例:countStudents()reportAttendance()assignSubstituteTeacher()などのメソッドを持つ学校セクションのドメインモデル。

617
Bill Karwin

ビルの答えが言うように、それらのユースケースは異なります

その構造も同様です。

  • ProxyおよびDecoratorはどちらもラップされたタイプと同じインターフェースを持っていますが、プロキシーは内部でインスタンスを作成しますが、デコレーターはコンストラクターでインスタンスを取得します。

  • AdapterおよびFacadeは、どちらもラップするものとは異なるインターフェースを持っています。ただし、アダプタは既存のインターフェイスから派生し、ファサードは新しいインターフェイスを作成します。

  • BridgeおよびAdapterは両方とも既存のタイプを指します。ただし、ブリッジは抽象型を指し、アダプターは具体的な型を指す場合があります。ブリッジを使用すると、実行時に実装をペアリングできますが、通常はアダプターではペアリングできません。

184

私の主題について。

4つのパターンにはすべて多くの共通点があり、4つすべては非公式にラッパーまたはラッパーパターンと呼ばれることもあります。すべてが構成を使用し、サブジェクトをラップし、ある時点でサブジェクトに実行を委任し、あるメソッド呼び出しを別のメソッド呼び出しにマッピングします。クライアントは、別のオブジェクトを作成し、関連するすべてのデータをコピーする必要をクライアントに与えます。賢く使用すると、メモリとプロセッサを節約できます。

疎結合を促進することにより、一度安定したコードは避けられない変更にさらされにくくなり、仲間の開発者にとって読みやすくなります。

アダプター

アダプターは、サブジェクト(アダプター)を別のインターフェースに適合させます。この方法で、名目上異なるタイプのコレクションに配置されるオブジェクトを追加できます。

アダプターはクライアントに関連するメソッドのみを公開し、他のすべてを制限し、外部ライブラリの適応など、特定のコンテキストの使用目的を明らかにし、一般的ではなく、アプリケーションのニーズに焦点を合わせます。アダプタは、コードの可読性と自己記述を向上させます。

アダプタは、1つのチームを他のチームの揮発性コードから保護します。オフショアチームを扱うときの救世主ツール;-)

あまり言及されていないのは、サブジェクトクラスが注釈を過剰に使用しないようにすることです。アノテーションに基づく非常に多くのフレームワークにより、これはかつてないほど重要な使用法になります。

アダプターは、単一継承のみのJava制限を回避するのに役立ちます。 1つのエンベロープの下で複数のアダプタを組み合わせて、多重継承の印象を与えることができます。

コードに関しては、Adapterは「薄い」です。単にアダプタメソッドを呼び出したり、そのような呼び出しを行うために必要なデータ変換を行ったりする以外に、アダプタクラスに多くのコードを追加しないでください。

JDKまたは基本ライブラリには、適切なアダプタの例は多くありません。アプリケーション開発者はアダプタを作成して、ライブラリをアプリケーション固有のインターフェイスに適合させます。

デコレーター

デコレータは、委任するだけでなく、あるメソッドを別のメソッドにマッピングするだけでなく、さらに多くのことを行い、サブジェクトメソッドの動作を変更し、サブジェクトメソッドをまったく呼び出さない、別のオブジェクト、ヘルパーオブジェクトに委任することを決定できます。

通常、デコレータは、対象へのロギング、暗号化、フォーマット、または圧縮などの機能をラップオブジェクトに(透過的に)追加します。この新しい機能は、多くの新しいコードをもたらす可能性があります。したがって、デコレーターは通常、アダプターよりも「太い」です。

デコレータは、サブジェクトのインターフェイスのサブクラスである必要があります。それらは、サブジェクトの代わりに透過的に使用できます。 BufferedOutputStreamを参照してください。これはまだOutputStreamであり、そのまま使用できます。これは、アダプターとの大きな技術的な違いです。

デコレータファミリ全体のテキストブックの例は、JDKのJava IOにあります。 BufferedOutputStreamFilterOutputStream 、および ObjectOutputStream などのすべてのクラスは、 OutputStream のデコレーターです。それらはオニオンレイヤー化することができ、1つのデコレータが再び装飾され、機能が追加されます。

プロキシ

プロキシは典型的なラッパーではありません。プロキシサブジェクトであるラップされたオブジェクトは、プロキシ作成時にまだ存在していない可能性があります。多くの場合、プロキシは内部的に作成します。オンデマンドで作成された重いオブジェクトの場合もあれば、異なるJVMまたは異なるネットワークノードのリモートオブジェクトであり、ネイティブコードのコンポーネントである非Javaオブジェクトである場合もあります。別のオブジェクトにラップしたり委任したりする必要はまったくありません。

最も典型的な例は、リモートプロキシ、重いオブジェクト初期化子、アクセスプロキシです。

  • リモートプロキシ-サブジェクトはリモートサーバー、異なるJVM、または非Javaシステムにあります。プロキシは、メソッド呼び出しをRMI/REST/SOAP呼び出しまたは必要なものに変換し、クライアントを基礎となるテクノロジーにさらさないようにします。

  • Lazy Load Proxy –最初の使用または最初の集中的な使用のみでオブジェクトを完全に初期化します。

  • アクセスプロキシ-サブジェクトへのアクセスを制御します。

ファサード

ファサードは、最小知識の設計原則(デメテルの法則)と密接に関連しています。ファサードはアダプターに非常に似ています。両方ともラップし、1つのオブジェクトを別のオブジェクトにマッピングしますが、意図は異なります。ファサードは、サブジェクトの複雑な構造、複雑なオブジェクトグラフを平坦化し、複雑な構造へのアクセスを簡素化します。

ファサードは複雑な構造を包み込み、フラットなインターフェースを提供します。これにより、クライアントオブジェクトがサブジェクト構造の内部関係にさらされるのを防ぎ、疎結合を促進します。

ブリッジ

実装が異なるだけでなく、抽象化も行うアダプタパターンのより複雑なバリアント。委任にもう1つの間接性を追加します。追加の委任はブリッジです。アダプターを適応インターフェースから切り離します。他のどのラッピングパターンよりも複雑さが増すため、慎重に適用してください。

コンストラクターの違い

コンストラクターを見ると、パターンの違いも明らかです。

  • Proxyは既存のオブジェクトをラップしていません。コンストラクターにサブジェクトはありません。

  • DecoratorおよびAdapterは既存のオブジェクトをラップします。
    コンストラクタで提供されます。

  • Facadeコンストラクターは、オブジェクトグラフ全体のルート要素を取ります。それ以外は、Adapterと同じように見えます。

実際の例– JAXB Marshalling Adapter 。このアダプターの目的は、単純なフラットクラスを、外部で必要とされるより複雑な構造にマッピングし、過剰な注釈でサブジェクトクラスを「汚染」するのを防ぐことです。

50
Espinosa

GoFパターンの多くには多くの重複があります。それらはすべてポリモーフィズムの力に基づいて構築されており、意図が実際に異なる場合があります。 (戦略と状態)

Head First Design Patterns を読んだ後、パターンに対する私の理解は100倍になりました。

私はそれを強くお勧めします!

29
Dinah

専門家からの良い答えはすべて、各パターンが何を表しているのかをすでに説明しています。

装飾キーポイントにします。

デコレーター:

  1. 実行時にオブジェクトに動作を追加。継承はこの機能を実現するための鍵であり、このパターンの利点と欠点の両方です。
  2. インターフェースの動作を変更します。

例えば(連鎖あり):Java.ioパッケージクラスはInputStreamおよびOutputStreamインターフェイスに関連します

FileOutputStream fos1 = new FileOutputStream("data1.txt");  
ObjectOutputStream out1 = new ObjectOutputStream(fos1);

プロキシ:

  1. 遅延初期化、オブジェクトのキャッシュとクライアント/呼び出し元へのアクセスの制御によるパフォーマンスの改善に使用します。代替動作を提供したり、実際のオブジェクトを呼び出したりする場合があります。このプロセス中に、新しいオブジェクトが作成される場合があります。
  2. オブジェクトの連鎖を許可するDecoratorとは異なり、プロキシ連鎖を許可しません。

例:Java.rmiパッケージクラス。

アダプター:

  1. 2つの無関係なインターフェイスが異なるオブジェクトを介して連携できる、おそらく同じ役割を果たします。
  2. 元のインターフェイスを変更する

例えばJava.io.InputStreamReaderInputStreamReaderを返します)

ブリッジ:

  1. 抽象化と実装の両方を独立して変更できる
  2. 継承に対する構成を使用します。

例えばJava.utilのコレクションクラス。 ListArrayListによって実装されています。

キーノート:

  1. Adapterは、サブジェクトに対して異なるインターフェースを提供します。 Proxyは同じインターフェースを提供します。 Decoratorは、強化されたインターフェースを提供します。
  2. Adapterはオブジェクトのインターフェースを変更します、Decoratorオブジェクトの責任を強化します。
  3. デコレーターおよびプロキシ目的は異なりますが、構造は似ています
  4. Adapterは、設計後に物事を機能させます。 Bridgeは、それらが機能する前に機能するようにします。
  5. ブリッジは、抽象化と実装が独立して異なるように事前に設計されています。 Adapterは、無関係なクラスを連携させるために改良されました
  6. Decoratorは、サブクラス化せずにオブジェクトに責任を追加できるように設計されています。

さまざまなデザインパターンの例に関する、SEに関するすばらしい質問/記事をご覧ください。

デコレータパターンを使用する場合

ブリッジパターンはいつ使用しますか?アダプタパターンとはどう違いますか?

プロキシとデコレータパターンの違い

8
Ravindra babu

それらは非常に似ており、それらの間の線は非常に灰色です。 c2 wikiの Proxy Pattern および Decorator Pattern エントリを読むことをお勧めします。

エントリとディスカッションは非常に広範囲であり、他の関連記事にもリンクしています。ちなみに、c2 wikiは、異なるパターン間のニュアンスについて疑問がある場合に優れています。

C2エントリをまとめると、デコレータは動作を追加/変更しますが、プロキシはアクセス制御(遅延インスタンス化、リモートアクセス、セキュリティなど)に関係しています。しかし、私が言ったように、それらの間の線は灰色であり、デコレータと簡単に見ることができるプロキシへの参照があります。

7

これは Head First Design Patterns からの引用です

定義は本に属します。例は私のものです。

Decorator-インターフェースを変更しませんが、責任を追加します。車のインターフェースを持っていると仮定し、車の異なるモデル(s、sv、sl)にこれを実装する場合、責任を追加する必要があるかもしれません一部のモデル。サンルーフ、エアバッグなどがあります。

Adapter-あるインターフェースを別のインターフェースに変換します。車のインターフェースがあり、ジープのように振る舞うことを望んでいます。だから、あなたは車を取り、それを修正し、ジープに変わります。 本物のジープではないため。しかし、ジープのように振る舞います

Facade-インターフェースをよりシンプルにします。車、飛行機、船のインターフェースがあるとします。実際に必要なのは、ある場所から別の場所に人を送るクラスです。どの車両を使用するかをファサードで決定する必要があります。次に、すべてのインターフェース参照を1つの傘の下で収集し、シンプルにするために決定/委任します。

ヘッドファースト:「ファサードはインターフェイスを単純化するだけでなく、クライアントをコンポーネントのサブシステムから切り離します。ファサードとアダプターは複数のクラスをラップする場合があります。 」

3
Teoman shipahi

4つのパターンはすべて、内部オブジェクト/クラスを外部パターンでラップするため、構造的に非常に似ています。目的による違いの概要を説明します。

  • Proxy外部から内部へのアクセスをカプセル化します。
  • デコレーターインナーの動作をアウターで変更または拡張します。
  • Adaptorインターフェイスを内部から外部に変換します。
  • ブリッジ動作の不変部分(外側)を変数またはプラットフォーム依存部分(内側)から分離します。

そして、内側と外側のオブジェクト間のインターフェースの違いにより:

  • in Proxyインターフェイスは同じです。
  • in Decoratorインターフェイスは同じです。
  • in Adaptorインターフェイスは正式には異なりますが、同じ目的を果たします。
  • in ブリッジインターフェイスは概念的に異なります。
2
Alexey

詳細な実装といえば、ProxyとDecorator、Adapter、Facadeの違いがあります。これらのパターンの一般的な実装では、囲んでいるオブジェクトでラップされたターゲットオブジェクトがあります。クライアントは、ターゲットオブジェクトの代わりに囲んでいるオブジェクトを使用します。そして、ターゲットオブジェクトは実際には、オブジェクトを囲むメソッドのいくつかの中で重要な役割を果たします。

ただし、プロキシの場合、囲んでいるオブジェクトはいくつかのメソッドを単独で再生できます。クライアントがターゲットオブジェクトに参加する必要のあるメソッドを呼び出すと、ターゲットオブジェクトを初期化します。これは遅延初期化です。他のパターンの場合、囲んでいるオブジェクトは実質的にターゲットオブジェクトに基づいています。そのため、ターゲットオブジェクトは、コンストラクター/セッターでオブジェクトを囲むとともに常に初期化されます。

もう1つ、プロキシはターゲットが行うことを正確に行いますが、他のパターンはターゲットに機能を追加します。

1
bnguyen82

Webサービスを使用するときに頻繁に使用します。プロキシパターンは、おそらく「ラッパーパターン」など、より実用的な名前に変更する必要があります。また、MS Excelのプロキシであるライブラリもあります。バージョンがインストールされています(ある場合)。

1
hmcclungiii

私はビル・カーウィングの答えに例を追加したいと思います(これは素晴らしいです)。実装のいくつかの重要な違いも追加します。

引用部分は[ https://stackoverflow.com/a/350471/1984346] (ビル・カーウィング)

プロキシ、デコレータ、アダプタ、およびブリッジはすべて、クラスの「ラップ」のバリエーションです。しかし、それらの用途は異なります。

  • Proxyは、オブジェクトを遅延インスタンス化する場合、またはリモートサービスを呼び出しているという事実を非表示にする場合、またはオブジェクト。

プロキシされるProxyClassとObjectClassは同じインターフェイスを実装する必要があるため、交換可能です

例-高価なオブジェクトのプロキシ

class ProxyHumanGenome implements GenomeInterface  {
    private $humanGenome = NULL; 

    // humanGenome class is not instantiated at construct time
    function __construct() {
    }

    function getGenomeCount() {
        if (NULL == $this->humanGenome) {
            $this->instantiateGenomeClass(); 
        }
        return $this->humanGenome->getGenomeCount();
    }
} 
class HumanGenome implement GenomeInterface { ... }
  • Decoratorは、「Smart Proxy」とも呼ばれます。これは、オブジェクトに機能を追加するときに使用されますが、そのオブジェクトのタイプを拡張することではありません。これにより、実行時に実行できます。

DecoratorClassは、ObjectClassの拡張インターフェースを実装する必要があります。したがって、ObjectClassをDecoratorClassに置き換えることはできますが、その逆はできません。

例-追加機能の追加

class DecoratorHumanGenome implements CheckGenomeInterface  {

    // ... same code as previous example

    // added functionality
    public function isComplete() {
        $this->humanGenome->getCount >= 21000
    }
}

interface CheckGenomeInterface extends GenomeInterface {

    public function isComplete();

}

class HumanGenome implement GenomeInterface { ... }
  • Adapterは、抽象的なインターフェースがあり、そのインターフェースを、機能的な役割は似ているがインターフェースが異なる別のオブジェクトにマッピングする場合に使用されます。

実装の違いプロキシ、デコレータ、アダプタ

アダプタは、そのサブジェクトに対して異なるインターフェイスを提供します。プロキシは同じインターフェースを提供します。デコレータは、強化されたインターフェイスを提供します。

  • BridgeはAdapterに非常に似ていますが、抽象インターフェースと基礎となる実装の両方を定義するときにBridgeと呼びます。つまりレガシーまたはサードパーティのコードに適応していない場合、すべてのコードの設計者ですが、異なる実装を交換できる必要があります。

  • Facadeは、1つ以上のクラスのサブシステムへの高レベル(読み取り:シンプル)インターフェースです。表現するために複数のオブジェクトを必要とする複雑なコンセプトがあるとします。そのオブジェクトのセットに変更を加えると、どのオブジェクトに呼び出す必要があるメソッドがあるかが常にわからないため、混乱を招きます。これが、オブジェクトのコレクションに対して行うことができるすべての複雑な操作に対して高レベルのメソッドを提供するFacadeを作成するときです。例:countStudents()reportAttendance()assignSubstituteTeacher()などのメソッドを持つ学校セクションのドメインモデル。

この回答のほとんどの情報は、https://sourcemaking.com/design_patternsからのものであり、デザインパターン用の優れたリソース

1
nakashu

私は、コードが明確なアイデアを提供すると信じています(他の回答も補完するため)。以下を参照してください(クラスが実装およびラップする型に注目してください)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TestConsole
{
    class Program
    {
        static void Main(string[] args)
        {
            /* Proxy */

            Console.WriteLine(Environment.NewLine);
            Console.WriteLine("PROXY");
            Console.WriteLine(Environment.NewLine);

            //instead of creating here create using a factory method, the facory method will return the proxy
            IReal realProxy = new RealProxy();
            Console.WriteLine("calling do work with the proxy object ");
            realProxy.DoWork();

            Console.WriteLine(Environment.NewLine);
            Console.WriteLine("ADAPTER");
            Console.WriteLine(Environment.NewLine);

            /*Adapter*/
            IInHand objectIHave = new InHand();
            Api myApi = new Api();
            //myApi.SomeApi(objectIHave); /*I cant do this, use a adapter then */
            IActual myAdaptedObject = new ActualAdapterForInHand(objectIHave);
            Console.WriteLine("calling api with  my adapted obj");
            myApi.SomeApi(myAdaptedObject);


            Console.WriteLine(Environment.NewLine);
            Console.WriteLine("DECORATOR");
            Console.WriteLine(Environment.NewLine);

            /*Decorator*/
            IReady maleReady = new Male();
            Console.WriteLine("now male is going to get ready himself");
            maleReady.GetReady();

            Console.WriteLine(Environment.NewLine);

            IReady femaleReady = new Female();
            Console.WriteLine("now female is going to get ready her self");
            femaleReady.GetReady();

            Console.WriteLine(Environment.NewLine);

            IReady maleReadyByBeautician = new Beautician(maleReady);
            Console.WriteLine("now male is going to get ready by beautician");
            maleReadyByBeautician.GetReady();

            Console.WriteLine(Environment.NewLine);

            IReady femaleReadyByBeautician = new Beautician(femaleReady);
            Console.WriteLine("now female is going to get ready by beautician");
            femaleReadyByBeautician.GetReady();

            Console.WriteLine(Environment.NewLine);

            Console.ReadLine();


        }
    }

    /*Proxy*/

    public interface IReal
    {
        void DoWork();
    }

    public class Real : IReal
    {
        public void DoWork()
        {
            Console.WriteLine("real is doing work ");
        }
    }


    public class RealProxy : IReal
    {
        IReal real = new Real();

        public void DoWork()
        {
            real.DoWork();
        }
    }

    /*Adapter*/

    public interface IActual
    {
        void DoWork();
    }

    public class Api
    {
        public void SomeApi(IActual actual)
        {
            actual.DoWork();
        }
    }

    public interface IInHand
    {
        void DoWorkDifferently();
    }

    public class InHand : IInHand
    {
        public void DoWorkDifferently()
        {
            Console.WriteLine("doing work slightly different ");
        }
    }

    public class ActualAdapterForInHand : IActual
    {
        IInHand hand = null;

        public ActualAdapterForInHand()
        {
            hand = new InHand();
        }

        public ActualAdapterForInHand(IInHand hnd)
        {
            hand = hnd;
        }

        public void DoWork()
        {
            hand.DoWorkDifferently();
        }
    }

    /*Decorator*/

    public interface IReady
    {
        void GetReady();
    }

    public class Male : IReady
    {
        public void GetReady()
        {
            Console.WriteLine("Taking bath.. ");
            Console.WriteLine("Dress up....");
        }
    }

    public class Female : IReady
    {
        public void GetReady()
        {
            Console.WriteLine("Taking bath.. ");
            Console.WriteLine("Dress up....");
            Console.WriteLine("Make up....");
        }
    }

    //this is a decorator
    public class Beautician : IReady
    {
        IReady ready = null;

        public Beautician(IReady rdy)
        {
            ready = rdy;
        }

        public void GetReady()
        {
            ready.GetReady();
            Console.WriteLine("Style hair ");

            if (ready is Female)
            {
                for (int i = 1; i <= 10; i++)
                {
                    Console.WriteLine("doing ready process " + i);
                }

            }
        }
    }

}
0