いつブリッジパターンを使用しますか?アダプタパターンとはどう違うのですか?
-
11-07-2019 - |
質問
実際のアプリケーションでブリッジパターンを使用したことがありますか?その場合、どのように使用しましたか?それは私ですか、それともミックスにスローされた依存関係の注入がわずかなアダプタパターンですか?本当に独自のパターンに値しますか?
解決
ブリッジパターンの古典的な例は、UI環境での形状の定義に使用されます(ブリッジを参照してくださいパターンウィキペディアエントリ)。ブリッジパターンは、コンポジット < href = "http://en.wikipedia.org/wiki/Template_method_pattern" rel = "noreferrer">テンプレートおよび戦略パターン。
これは、ブリッジパターンのアダプタパターンのいくつかの側面の一般的なビューです。ただし、この記事から引用するには:
一見、Bridgeパターンは、ある種のインターフェイスを別の種類に変換するためにクラスが使用されるという点で、Adapterパターンによく似ています。ただし、アダプタパターンの目的は、1つまたは複数のクラスのインターフェイスを特定のクラスのインターフェイスと同じように見せることです。 Bridgeパターンは、クラスのインターフェースを実装から分離するように設計されているため、クライアントコードを変更せずに実装を変更または置換できます。
他のヒント
ブリッジパターンは、「継承よりも合成を優先する」という古いアドバイスを適用したものです。 互いに直交する方法で異なる時間をサブクラス化する必要がある場合に便利です。色付きの図形の階層を実装する必要があるとします。 ShapeをRectangleとCircleでサブクラス化してから、RectangleをRedRectangle、BlueRectangle、GreenRectangleでサブクラス化し、Circleでも同じようにしますか?各シェイプには色があると言って、色の階層を実装することをお勧めします。それがブリッジパターンです。さて、「色の階層」を実装するつもりはありませんが、アイデアは得られます...
いつ:
A
/ \
Aa Ab
/ \ / \
Aa1 Aa2 Ab1 Ab2
リファクタリング:
A N
/ \ / \
Aa(N) Ab(N) 1 2
アダプターとブリッジは確かに関連しており、区別は微妙です。これらのパターンのいずれかを使用していると考える人の中には、実際に他のパターンを使用している人がいる可能性があります。
私が見た説明は、すでに存在する互換性のないクラスのインターフェースを統一しようとしているときにアダプタが使用されるということです。アダプタは、レガシーと見なされる可能性のある実装に対する一種のトランスレータとして機能します。
一方、ブリッジパターンは、グリーンフィールドである可能性が高いコードに使用されます。ブリッジを設計して、さまざまな実装に抽象インターフェースを提供しますが、これらの実装クラスのインターフェースも定義します。
デバイスドライバーはよく引用されるBridgeの例ですが、デバイスベンダーのインターフェイス仕様を定義している場合はBridgeですが、既存のデバイスドライバーを使用してラッパーを作成している場合はAdapterです-classで統一されたインターフェースを提供します。
コードに関しては、2つのパターンは非常に似ています。ビジネス面では、それらは異なります。
私の経験では、Bridgeは非常に頻繁に繰り返されるパターンです。これは、ドメイン内に直交する2つの次元がある場合の解決策だからです。例えば。形状と描画方法、動作とプラットフォーム、ファイル形式とシリアライザーなど。
そしてアドバイス:実装の観点からではなく、常に概念の観点から 設計パターンを考えてください。正しい観点から見ると、Bridgeはアダプタと混同することはできません。Bridgeは別の問題を解決するためです。また、構成は継承のためではなく、それ自体のためではなく、直交する懸念を個別に処理できるため、継承よりも優れています。
ブリッジとアダプターの意図は異なり、両方のパターンが別々に必要です。
ブリッジパターン:
- これは構造的なパターンです
- 抽象化と実装はコンパイル時にバインドされません
- 抽象化と実装-どちらもクライアントに影響を与えずに変更できます
- 継承よりも合成を使用します。
次の場合にブリッジパターンを使用します。
- 実装の実行時バインディングが必要です
- 結合されたインターフェースと多数の実装により、クラスが急増しています
- 複数のオブジェクト間で実装を共有したい
- 直交クラス階層をマッピングする必要があります。
@ John Sonmezの回答は、クラス階層の削減におけるブリッジパターンの効果を明確に示しています。
以下のドキュメントリンクを参照して、コード例でブリッジパターンのより良い洞察を得ることができます
アダプターパターン :
- それは 2つの無関係なインターフェースが異なるオブジェクトを介して連携することを許可します、おそらく同じ役割を果たします。
- 元のインターフェースを変更します。
主な違い:
- アダプターは、設計後に物事を機能させます。 ブリッジは、彼らが彼らの前に働くようにします。
- ブリッジは、抽象化と実装が独立して変化するように、前もって設計されています。 アダプタは、無関係なクラスを連携させるために改良されています。
- 意図:アダプターにより、2つの無関係なインターフェースを連携させることができます。 ブリッジにより、抽象化と実装を独立して変更できます。
関連するSE質問とUML図および作業コード:
有用な記事:
sourcemaking bridge パターンの記事
ソースメイキングアダプターパターンの記事
journaldev bridge パターンの記事
編集:
ブリッジパターンの実世界の例(meta.stackoverflow.comの提案によると、ドキュメンテーションは日没になるため、この投稿にドキュメントサイトの例を組み込みました)
ブリッジパターンは、抽象化と実装を分離するため、両方が独立して変化します。継承ではなく構成で達成されました。
WikipediaのブリッジパターンUML:
このパターンには4つのコンポーネントがあります。
抽象化
:インターフェースを定義します
RefinedAbstraction
:抽象化を実装します:
Implementor
:実装用のインターフェースを定義します
ConcreteImplementor
:Implementorインターフェースを実装します。
ブリッジパターンの要点:
構成を使用した2つの直交クラス階層(継承なし)。抽象化階層と実装階層は独立して異なる場合があります。実装が抽象化を参照することはありません。抽象化には、構成として実装インターフェイスがメンバーとして含まれます。この構成は、継承階層のもう1つのレベルを減らします。
実際の使用例:
異なる車両で手動および自動ギアシステムの両方のバージョンを使用できるようにします。
例
私は職場でブリッジパターンを使用しました。私は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つは、実装クラスの一部のパブリックメンバーのみを公開し、他の公開メンバーは公開しないことです。この例では、 A
のパブリックインターフェイスを介して呼び出すことができるのは Aimpl :: foo()
のみであり、 Aimpl :: bar()
別の利点は、 A
のユーザーがインクルードする必要のない別のヘッダーファイルで Aimpl
を定義できることです。必要なのは、 A
を定義する前に Aimpl
の前方宣言を使用し、 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では交換できます。
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);
}
}
}