回答:
ブリッジパターンの古典的な例は、UI環境での形状の定義に使用されます(ブリッジパターンウィキペディアエントリを参照)。ブリッジパターンがある複合のテンプレートと戦略パターン。
これは、BridgeパターンのAdapterパターンのいくつかの側面の一般的なビューです。ただし、この記事から引用するには:
一見すると、クラスを使用してインターフェイスの種類を別の種類に変換するという点で、ブリッジパターンはアダプタパターンとよく似ています。ただし、Adapterパターンの目的は、1つ以上のクラスのインターフェースを特定のクラスのインターフェースと同じに見せることです。Bridgeパターンは、クラスのインターフェースを実装から分離するように設計されているため、クライアントコードを変更せずに実装を変更または置換できます。
いつ:
----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 にはColorがあり、色の階層を実装すると言います。それがブリッジパターンです。ええと、私は「色の階層」を実装するつもりはありませんが、あなたは考えを理解しています...
いつ:
A
/ \
Aa Ab
/ \ / \
Aa1 Aa2 Ab1 Ab2
リファクタリング:
A N
/ \ / \
Aa(N) Ab(N) 1 2
アダプターとブリッジは確かに関連しており、その区別は微妙です。これらのパターンの1つを使用していると考える一部の人々は、実際には他のパターンを使用している可能性があります。
私が見た説明は、すでに存在するいくつかの互換性のないクラスのインターフェースを統一しようとしているときにアダプタが使用されるということです。アダプタは、レガシーと見なすことができる実装への一種のトランスレータとして機能します。
一方、グリーンパターンである可能性が高いコードには、ブリッジパターンが使用されます。変更する必要がある実装に抽象インターフェースを提供するようにブリッジを設計していますが、それらの実装クラスのインターフェースも定義します。
デバイスドライバーはBridgeのよく引用される例ですが、デバイスベンダーのインターフェイス仕様を定義している場合はBridgeですが、既存のデバイスドライバーを使用してラッパークラスを作成している場合はBridgeです統一されたインターフェースを提供します。
したがって、コード的には、2つのパターンは非常によく似ています。ビジネス的には、それらは異なります。
BridgeとAdapterの目的は異なり、両方のパターンが個別に必要です。
ブリッジパターン:
Bridgeパターンは次の場合に使用します。
@ John Sonmezの回答は、クラス階層の削減におけるブリッジパターンの有効性を明確に示しています。
以下のドキュメントリンクを参照すると、コード例を使用してブリッジパターンをより深く理解できます。
アダプターパターン:
主な違い:
UMLダイアグラムと作業コードを含む関連するSEの質問:
役立つ記事:
ソースメイキングブリッジパターン記事
ソースメイキングアダプターパターン記事
journaldevブリッジパターンの記事
編集:
ブリッジパターンの実世界の例(meta.stackoverflow.comの提案に従って、ドキュメントが沈むため、この投稿にドキュメントサイトの例を組み込んだ)
ブリッジパターンは、抽象化を実装から切り離し、両方を独立して変更できるようにします。それは継承ではなく構成で達成されました。
ウィキペディアのブリッジパターンUML:
このパターンには4つのコンポーネントがあります。
Abstraction
:インターフェースを定義します
RefinedAbstraction
:抽象化を実装します:
Implementor
:実装のためのインターフェースを定義します
ConcreteImplementor
:Implementorインターフェースを実装します。
The crux of Bridge pattern :
構成を使用する2つの直交クラス階層(継承なし)。抽象化階層と実装階層は、個別に変更できます。実装が抽象化を参照することはありません。抽象化には、構成として実装インターフェースがメンバーとして含まれます。この構成により、継承階層のレベルが1つ減少します。
実際の単語の使用例:
さまざまな車両で手動と自動のギアシステムの両方のバージョンを使用できるようにします。
コード例:
/* 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
2つの具体的な実装ですVehicle
。Vehicle
抽象メソッドを定義しますaddGear()
。Gear
実装者インターフェースですManualGear
とのAutoGear
2つの実装です Gear
Vehicle
implementor
インターフェースを実装するのではなく、インターフェースを含みます。Compositon
実装者インターフェースのこのパターンの核心です:それは抽象化と実装が独立して変化することを可能にします。 Car
そして、Truck
抽象化のための実装(再定義された抽象化)を定義しますaddGear()
::含まれています Gear
- Manual
またはAuto
ブリッジパターンの使用例:
私は仕事でブリッジパターンを使用しました。私は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()
もう1つの利点はAimpl
、のユーザーが含める必要のない別のヘッダーファイルで定義できることですA
。あなたがしなければならないのは、Aimpl
before 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
順列によるサブクラスの爆発につながることなく、新しい色と形状をシステムに簡単に追加できることに注意してください。
あなたは保険会社で働いており、さまざまな種類のタスク(会計、契約、請求など)を管理するワークフローアプリケーションを開発しています。これが抽象化です。実装側では、電子メール、ファックス、電子メッセージングなどのさまざまなソースからタスクを作成できる必要があります。
これらのクラスから設計を開始します。
public class Task {...}
public class AccountingTask : Task {...}
public class ContractTask : Task {...}
public class ClaimTask : Task {...}
ここで、各ソースを特定の方法で処理する必要があるため、各タスクタイプを特化することにします。
public class EmailAccountingTask : AccountingTask {...}
public class FaxAccountingTask : AccountingTask {...}
public class EmessagingAccountingTask : AccountingTask {...}
public class EmailContractTask : ContractTask {...}
public class FaxContractTask : ContractTask {...}
public class EmessagingContractTask : ContractTask {...}
public class EmailClaimTask : ClaimTask {...}
public class FaxClaimTask : ClaimTask {...}
public class EmessagingClaimTask : ClaimTask {...}
あなたは13のクラスに終わります。タスクタイプまたはソースタイプを追加することは困難になります。ブリッジパターンを使用すると、タスク(抽象化)をソース(実装上の問題)から分離することで、保守が容易になります。
// Source
public class Source {
public string GetSender();
public string GetMessage();
public string GetContractReference();
(...)
}
public class EmailSource : Source {...}
public class FaxSource : Source {...}
public class EmessagingSource : Source {...}
// Task
public class Task {
public Task(Source source);
(...)
}
public class AccountingTask : Task {...}
public class ContractTask : Task {...}
public class ClaimTask : Task {...}
タスクタイプまたはソースの追加がはるかに簡単になりました。
注:ほとんどの開発者は、この問題を処理するために13のクラス階層を事前に作成しません。ただし、実際には、ソースとタスクのタイプの数が事前にわからない場合があります。ソースが1つとタスクタイプが2つしかない場合は、タスクをソースから切り離さないでしょう。次に、新しいソースとタスクタイプが追加されると、全体的な複雑さが増します。ある時点で、リファクタリングを行い、ほとんどの場合、最終的にはブリッジのようなソリューションになります。
Bridge design pattern we can easily understand helping of service and dao layer.
Dao layer -> create common interface for dao layer ->
public interface Dao<T>{
void save(T t);
}
public class AccountDao<Account> implement Dao<Account>{
public void save(Account){
}
}
public LoginDao<Login> implement Dao<Login>{
public void save(Login){
}
}
Service Layer ->
1) interface
public interface BasicService<T>{
void save(T t);
}
concrete implementation of service -
Account service -
public class AccountService<Account> implement BasicService<Account>{
private Dao<Account> accountDao;
public AccountService(AccountDao dao){
this.accountDao=dao;
}
public void save(Account){
accountDao.save(Account);
}
}
login service-
public class LoginService<Login> implement BasicService<Login>{
private Dao<Login> loginDao;
public AccountService(LoginDao dao){
this.loginDao=dao;
}
public void save(Login){
loginDao.save(login);
}
}
public class BridgePattenDemo{
public static void main(String[] str){
BasicService<Account> aService=new AccountService(new AccountDao<Account>());
Account ac=new Account();
aService.save(ac);
}
}
}