web-dev-qa-db-ja.com

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

誰も実際のアプリケーションで Bridge Pattern を使用したことがありますか?その場合、どのように使用しましたか?それは私ですか、それともミックスにスローされた依存関係の注入がわずかなアダプターパターンですか?それは本当に独自のパターンに値しますか?

143
Charles Graham

ブリッジパターンの典型的な例は、UI環境での形状の定義に使用されます( ブリッジパターンウィキペディアエントリ を参照)。ブリッジパターンは、 テンプレート および 戦略 パターンの 複合 です。

これは、ブリッジパターンのアダプタパターンのいくつかの側面の一般的なビューです。ただし、 この記事 から引用するには:

一見すると、Bridgeパターンは、ある種のインターフェースを別の種類に変換するためにクラスが使用されるという点で、Adapterパターンによく似ています。ただし、アダプタパターンの目的は、1つまたは複数のクラスのインターフェイスを特定のクラスのインターフェイスと同じように見せることです。ブリッジパターンは、クラスのインターフェイスを実装から分離するように設計されているため、クライアントコードを変更せずに実装を変更または置換できます。

76
shek

Federico'sJohn's の回答の組み合わせがあります。

いつ:

                   ----Shape---
                  /            \
         Rectangle              Circle
        /         \            /      \
BlueRectangle  RedRectangle BlueCircle RedCircle

リファクタリング:

          ----Shape---                        Color
         /            \                       /   \
Rectangle(Color)   Circle(Color)           Blue   Red
235

Bridgeパターンは、「継承よりも構成を優先する」という古いアドバイスを適用したものです。互いに直交する方法で異なる時間をサブクラス化する必要がある場合に便利です。色付きの図形の階層を実装する必要があるとします。 ShapeをRectangleとCircleでサブクラス化してから、RectangleをRedRectangle、BlueRectangle、GreenRectangleでサブクラス化し、Circleでも同じようにしますか?各Shape hasはColorであると言い、色の階層を実装することをお勧めします。これがブリッジパターンです。まあ、私は「色の階層」を実装しませんが、あなたはアイデアを得る...

223

いつ:

        A
     /     \
    Aa      Ab
   / \     /  \
 Aa1 Aa2  Ab1 Ab2

リファクタリング:

     A         N
  /     \     / \
Aa(N) Ab(N)  1   2
204
John Sonmez

アダプタとブリッジは確かに関連しており、区別は微妙です。これらのパターンのいずれかを使用していると考える人の中には、実際に他のパターンを使用している人もいるでしょう。

私が見た説明は、既に存在するいくつかの互換性のないクラスのインターフェースを統一しようとしているときにアダプタが使用されるということです。アダプタは、legacyと見なされる可能性がある実装に対する一種のトランスレータとして機能します。

一方、ブリッジパターンは、グリーンフィールドである可能性が高いコードに使用されます。ブリッジを設計して、さまざまな実装に抽象インターフェースを提供しますが、これらの実装クラスのインターフェースも定義します。

デバイスドライバーはよく引用されるBridgeの例ですが、デバイスベンダーのインターフェイス仕様を定義している場合はブリッジですが、既存のデバイスドライバーを取得してラッパークラスを作成している場合はアダプターです統一されたインターフェースを提供します。

したがって、コードに関しては、2つのパターンは非常に似ています。ビジネス面では、それらは異なります。

http://c2.com/cgi/wiki?BridgePattern も参照してください

29
Bill Karwin

私の経験では、Bridgeはドメイン内に2つの直交する次元があるの場合は常に解決策であるため、非常に頻繁に繰り返されるパターンです。例えば。形状と描画方法、動作とプラットフォーム、ファイル形式とシリアライザーなど。

そしてアドバイス:常に設計パターンを考えてください概念の観点から、実装の観点からではありません。正しい観点から見ると、Bridgeはアダプターと混同することはできません。Bridgeは別の問題を解決し、構成は継承のためではなく、それ自体のためではなく、直交する懸念を個別に処理できるためです。

26
thSoft

BridgeAdapterの意図は異なり、両方のパターンが別々に必要です。

ブリッジパターン:

  1. 構造パターンです
  2. 抽象化と実装はコンパイル時にバインドされていません
  3. 抽象化と実装-どちらもクライアントに影響を与えずに変更できます
  4. 継承よりも合成を使用します。

次の場合にBridgeパターンを使用します。

  1. 実装の実行時バインディングが必要な場合、
  2. 結合されたインターフェースと多数の実装に起因するクラスの急増があり、
  3. 複数のオブジェクト間で実装を共有したい場合、
  4. 直交クラス階層をマッピングする必要があります。

@ John Sonmezの回答は、クラス階層の削減におけるブリッジパターンの有効性を明確に示しています。

以下のドキュメントリンクを参照して、コード例でブリッジパターンのより良い洞察を得ることができます

アダプタパターン

  1. それ2つの無関係なインターフェイスを一緒に動作させる異なるオブジェクトを介して、おそらく同じ役割を果たします。
  2. 元のインターフェイスを変更します。

主な違い:

  1. Adapterは、設計後に物事を機能させます。 ブリッジは、それらが機能する前に機能させます。
  2. ブリッジは、抽象化と実装が独立して異なるを可能にするために事前に設計されています。 Adapterは、関係のないクラスを連携させるために改良されています。
  3. 意図:Adapterは、2つの無関係なインターフェイスが連携することを許可します。 Bridgeを使用すると、抽象化と実装を個別に変更できます。

関連するSEの質問とUML図および作業コード:

ブリッジパターンとアダプタパターンの違い

有用な記事:

sourcemaking bridge パターン記事

sourcemaking adapter パターン記事

journaldev bridge パターン記事

編集:

ブリッジパターンの実世界の例(meta.stackoverflow.comの提案に従い、ドキュメントはサンセットに送信されるため、この投稿にドキュメントサイトの例を組み込みました)

ブリッジパターンは抽象化を実装から分離するため、両方が独立して変化します。これは、継承ではなく構成で実現されています。

ウィキペディアのブリッジパターンUML:

Bridge pattern UML from Wikipedia

このパターンには4つのコンポーネントがあります。

Abstraction:インターフェースを定義します

RefinedAbstraction:抽象化を実装します:

Implementor:実装用のインターフェースを定義します

ConcreteImplementor:Implementorインターフェースを実装します。

_The crux of Bridge pattern :_構成を使用する2つの直交クラス階層(継承なし)。抽象化階層と実装階層は独立して異なる場合があります。実装が抽象化を参照することはありません。抽象化には、構成として実装インターフェイスがメンバーとして含まれます。この構成は、継承階層のもう1つのレベルを減らします。

実際のWordの使用例:

手動および自動ギアシステムの両方のバージョンを異なる車両で使用できるようにする

サンプルコード:

_/* Implementor interface*/
interface Gear{
    void handleGear();
}

/* Concrete Implementor - 1 */
class ManualGear implements Gear{
    public void handleGear(){
        System.out.println("Manual gear");
    }
}
/* Concrete Implementor - 2 */
class AutoGear implements Gear{
    public void handleGear(){
        System.out.println("Auto gear");
    }
}
/* Abstraction (abstract class) */
abstract class Vehicle {
    Gear gear;
    public Vehicle(Gear gear){
        this.gear = gear;
    }
    abstract void addGear();
}
/* RefinedAbstraction - 1*/
class Car extends Vehicle{
    public Car(Gear gear){
        super(gear);
        // initialize various other Car components to make the car
    }
    public void addGear(){
        System.out.print("Car handles ");
        gear.handleGear();
    }
}
/* RefinedAbstraction - 2 */
class Truck extends Vehicle{
    public Truck(Gear gear){
        super(gear);
        // initialize various other Truck components to make the car
    }
    public void addGear(){
        System.out.print("Truck handles " );
        gear.handleGear();
    }
}
/* Client program */
public class BridgeDemo {    
    public static void main(String args[]){
        Gear gear = new ManualGear();
        Vehicle vehicle = new Car(gear);
        vehicle.addGear();

        gear = new AutoGear();
        vehicle = new Car(gear);
        vehicle.addGear();

        gear = new ManualGear();
        vehicle = new Truck(gear);
        vehicle.addGear();

        gear = new AutoGear();
        vehicle = new Truck(gear);
        vehicle.addGear();
    }
}
_

出力:

_Car handles Manual gear
Car handles Auto gear
Truck handles Manual gear
Truck handles Auto gear
_

説明:

  1. Vehicleは抽象化です。
  2. CarTruckは、Vehicleの2つの具体的な実装です。
  3. Vehicleは、抽象メソッドaddGear()を定義します。
  4. Gearは実装インターフェースです
  5. ManualGearAutoGearGearの2つの実装です
  6. Vehicleには、インターフェースを実装するのではなく、implementorインターフェースが含まれます。実装者インターフェースのCompositonは、このパターンの要点です。これにより、抽象化と実装を独立して変更できます。
  7. CarおよびTruckは、抽象化の実装を定義します(再定義された抽象化):addGear()Gearを含む-ManualまたはAuto

ブリッジパターンの使用例

  1. AbstractionおよびImplementationは互いに独立して変更でき、それらはコンパイル時間
  2. 直交階層のマッピング-Abstractionに1つ、およびImplementationに1つ。
20
Ravindra babu

私は職場でブリッジパターンを使用しました。私はC++でプログラミングします。C++では、PIMPLイディオム(実装へのポインター)と呼ばれることがよくあります。次のようになります。

_class A
{
public: 
  void foo()
  {
    pImpl->foo();
  }
private:
  Aimpl *pImpl;
};

class Aimpl
{
public:
  void foo();
  void bar();
};  
_

この例では、_class A_にはインターフェイスが含まれ、_class Aimpl_には実装が含まれます。

このパターンの用途の1つは、実装クラスのパブリックメンバーの一部のみを公開し、他の公開メンバーは公開しないことです。この例では、Aimpl::foo()のみではなく、Aのパブリックインターフェイスを介してAimpl::bar()を呼び出すことができます。

別の利点は、Aimplのユーザーがインクルードする必要のない別のヘッダーファイルにAを定義できることです。 Aimplを定義する前にAの前方宣言を使用し、pImplを参照するすべてのメンバー関数の定義を.cppファイルに移動するだけです。これにより、Aimplヘッダーをプライベートに保ち、コンパイル時間を短縮できます。

9
Dima

形状の例をコードに入れるには:

#include<iostream>
#include<string>
#include<cstdlib>

using namespace std;

class IColor
{
public:
    virtual string Color() = 0;
};

class RedColor: public IColor
{
public:
    string Color()
    {
        return "of Red Color";
    }
};

class BlueColor: public IColor
{
public:
    string Color()
    {
        return "of Blue Color";
    }
};


class IShape
{
public:
virtual string Draw() = 0;
};

class Circle: public IShape
{
        IColor* impl;
    public:
        Circle(IColor *obj):impl(obj){}
        string Draw()
        {
            return "Drawn a Circle "+ impl->Color();
        }
};

class Square: public IShape
{
        IColor* impl;
    public:
        Square(IColor *obj):impl(obj){}
        string Draw()
        {
        return "Drawn a Square "+ impl->Color();;
        }
};

int main()
{
IColor* red = new RedColor();
IColor* blue = new BlueColor();

IShape* sq = new Square(red);
IShape* cr = new Circle(blue);

cout<<"\n"<<sq->Draw();
cout<<"\n"<<cr->Draw();

delete red;
delete blue;
return 1;
}

出力は次のとおりです。

Drawn a Square of Red Color
Drawn a Circle of Blue Color

置換によるサブクラスの爆発を招くことなく、新しい色と形状をシステムに簡単に追加できることに注意してください。

6
NotAgain

私にとっては、インターフェイスを交換できるメカニズムと考えています。現実の世界では、複数のインターフェイスを使用できるクラスがあり、Bridgeでは交換できます。

0
j2emanue