誰も実際のアプリケーションで Bridge Pattern を使用したことがありますか?その場合、どのように使用しましたか?それは私ですか、それともミックスにスローされた依存関係の注入がわずかなアダプターパターンですか?それは本当に独自のパターンに値しますか?
ブリッジパターンの典型的な例は、UI環境での形状の定義に使用されます( ブリッジパターンウィキペディアエントリ を参照)。ブリッジパターンは、 テンプレート および 戦略 パターンの 複合 です。
これは、ブリッジパターンのアダプタパターンのいくつかの側面の一般的なビューです。ただし、 この記事 から引用するには:
一見すると、Bridgeパターンは、ある種のインターフェースを別の種類に変換するためにクラスが使用されるという点で、Adapterパターンによく似ています。ただし、アダプタパターンの目的は、1つまたは複数のクラスのインターフェイスを特定のクラスのインターフェイスと同じように見せることです。ブリッジパターンは、クラスのインターフェイスを実装から分離するように設計されているため、クライアントコードを変更せずに実装を変更または置換できます。
Federico's と John's の回答の組み合わせがあります。
いつ:
----Shape---
/ \
Rectangle Circle
/ \ / \
BlueRectangle RedRectangle BlueCircle RedCircle
リファクタリング:
----Shape--- Color
/ \ / \
Rectangle(Color) Circle(Color) Blue Red
Bridgeパターンは、「継承よりも構成を優先する」という古いアドバイスを適用したものです。互いに直交する方法で異なる時間をサブクラス化する必要がある場合に便利です。色付きの図形の階層を実装する必要があるとします。 ShapeをRectangleとCircleでサブクラス化してから、RectangleをRedRectangle、BlueRectangle、GreenRectangleでサブクラス化し、Circleでも同じようにしますか?各Shape hasはColorであると言い、色の階層を実装することをお勧めします。これがブリッジパターンです。まあ、私は「色の階層」を実装しませんが、あなたはアイデアを得る...
いつ:
A
/ \
Aa Ab
/ \ / \
Aa1 Aa2 Ab1 Ab2
リファクタリング:
A N
/ \ / \
Aa(N) Ab(N) 1 2
アダプタとブリッジは確かに関連しており、区別は微妙です。これらのパターンのいずれかを使用していると考える人の中には、実際に他のパターンを使用している人もいるでしょう。
私が見た説明は、既に存在するいくつかの互換性のないクラスのインターフェースを統一しようとしているときにアダプタが使用されるということです。アダプタは、legacyと見なされる可能性がある実装に対する一種のトランスレータとして機能します。
一方、ブリッジパターンは、グリーンフィールドである可能性が高いコードに使用されます。ブリッジを設計して、さまざまな実装に抽象インターフェースを提供しますが、これらの実装クラスのインターフェースも定義します。
デバイスドライバーはよく引用されるBridgeの例ですが、デバイスベンダーのインターフェイス仕様を定義している場合はブリッジですが、既存のデバイスドライバーを取得してラッパークラスを作成している場合はアダプターです統一されたインターフェースを提供します。
したがって、コードに関しては、2つのパターンは非常に似ています。ビジネス面では、それらは異なります。
http://c2.com/cgi/wiki?BridgePattern も参照してください
私の経験では、Bridgeはドメイン内に2つの直交する次元があるの場合は常に解決策であるため、非常に頻繁に繰り返されるパターンです。例えば。形状と描画方法、動作とプラットフォーム、ファイル形式とシリアライザーなど。
そしてアドバイス:常に設計パターンを考えてください概念の観点から、実装の観点からではありません。正しい観点から見ると、Bridgeはアダプターと混同することはできません。Bridgeは別の問題を解決し、構成は継承のためではなく、それ自体のためではなく、直交する懸念を個別に処理できるためです。
BridgeとAdapterの意図は異なり、両方のパターンが別々に必要です。
ブリッジパターン:
次の場合にBridgeパターンを使用します。
@ John Sonmezの回答は、クラス階層の削減におけるブリッジパターンの有効性を明確に示しています。
以下のドキュメントリンクを参照して、コード例でブリッジパターンのより良い洞察を得ることができます
アダプタパターン:
主な違い:
関連するSEの質問とUML図および作業コード:
有用な記事:
sourcemaking bridge パターン記事
sourcemaking adapter パターン記事
journaldev bridge パターン記事
編集:
ブリッジパターンの実世界の例(meta.stackoverflow.comの提案に従い、ドキュメントはサンセットに送信されるため、この投稿にドキュメントサイトの例を組み込みました)
ブリッジパターンは抽象化を実装から分離するため、両方が独立して変化します。これは、継承ではなく構成で実現されています。
ウィキペディアのブリッジパターンUML:
このパターンには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
_
説明:
Vehicle
は抽象化です。Car
とTruck
は、Vehicle
の2つの具体的な実装です。Vehicle
は、抽象メソッドaddGear()
を定義します。Gear
は実装インターフェースですManualGear
とAutoGear
はGear
の2つの実装ですVehicle
には、インターフェースを実装するのではなく、implementor
インターフェースが含まれます。実装者インターフェースのCompositon
は、このパターンの要点です。これにより、抽象化と実装を独立して変更できます。Car
およびTruck
は、抽象化の実装を定義します(再定義された抽象化):addGear()
:Gear
を含む-Manual
またはAuto
ブリッジパターンの使用例:
私は職場でブリッジパターンを使用しました。私は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
ヘッダーをプライベートに保ち、コンパイル時間を短縮できます。
形状の例をコードに入れるには:
#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
置換によるサブクラスの爆発を招くことなく、新しい色と形状をシステムに簡単に追加できることに注意してください。
私にとっては、インターフェイスを交換できるメカニズムと考えています。現実の世界では、複数のインターフェイスを使用できるクラスがあり、Bridgeでは交換できます。