ブリッジパターンとアダプタパターンの違いは何ですか?
11 に答える
「アダプターは設計後に機能します。ブリッジは設計前に機能します。[GoF、p219]」
事実上、アダプタパターンは、サードパーティであれ社内であれ、既存のコードがあるが、制御できない場合や、必要なインターフェイスに完全に適合するように変更できない場合に役立ちます。たとえば、終末兵器の細かい配列を制御できるSuperWeaponsArrayがあります。
public class SuperWeaponsArray {
/*...*/
public void destroyWorld() {
for (Weapon w : armedWeapons) {
w.fire();
}
}
}
素晴らしい。兵器インターフェースへの変換よりもはるかに前の核兵器が兵器庫にあることに気付いた場合を除きます。しかし、私たちは本当にそれがここで機能することを望んでいます...それで私たちは何をしますか...それをくさびで留めます!
NukeWeaponsAdaptor-Nukeクラスに基づいていますが、Weaponインターフェイスをエクスポートしています。甘い、今私たちは確かに世界を破壊することができます。ちょっとしたごちゃごちゃしているように見えますが、うまくいきます。
ブリッジパターンは、事前に実装するものです。2つの直交する階層があることがわかっている場合は、非常に多くのクラスを取得しないように、インターフェイスと実装を分離する方法を提供します。あなたが持っているとしましょう:
ファイルオブジェクトのMemoryMappedFileおよびDirectReadFileタイプ。さまざまなソース(おそらくLinuxとWindowsの実装など)からファイルを読み取れるようにしたいとします。ブリッジは、次のような問題を回避するのに役立ちます。
MemoryMappedWindowsFile MemoryMappedLinuxFile DirectReadWindowsFile DirectReadLinuxFile
http://en.wikipedia.org/wiki/Adapter_pattern
アダプタパターンとは、既存のコードを新しいシステムまたはインターフェイスで機能させることです。
別のアプリケーションの既存の拡張性インターフェースに提供したい一連の会社標準のWebサービスAPIがある場合は、これを行うための一連のアダプターを作成することを検討してください。灰色の領域があり、ファサードのような他のパターンも同様であるため、これはパターンを技術的に定義する方法に関するものであることに注意してください。
http://en.wikipedia.org/wiki/Bridge_pattern
ブリッジパターンを使用すると、アルゴリズムまたはシステムの代替実装を使用できるようになります。
古典的なブリッジパターンの例ではありませんが、データストアの実装がいくつかある場合を想像してみてください。1つはスペースで効率的で、もう1つは生のパフォーマンスで効率的です...そしてアプリまたはフレームワークの両方で提供するビジネスケースがあります。
「どこでどのパターンを使用できるか」という質問に関しては、プロジェクトにとって意味のある場所であればどこでも答えが返ってきます。おそらく、どちらか一方を使用する必要があると思われる場所についての議論を導くために、説明の編集を提供することを検討してください。
アダプタ:
- 構造パターンです
- 互換性のない2つのインターフェイスを操作すると便利です
UML図:ドファクトリーの記事から:
ターゲット :クライアントが使用するドメイン固有のインターフェースを定義します。
アダプタ:インターフェイスAdapteeをターゲットインターフェイスに適合させます。
Adaptee:適応が必要な既存のインターフェースを定義します。
クライアント :ターゲットインターフェイスに準拠するオブジェクトと連携します。
例:
SquareとRectangleは2つの異なる形状であり、それぞれのarea()を取得するには異なるメソッドが必要です。ただし、Squareは、一部のプロパティを変換してRectangleインターフェイスで動作します。
public class AdapterDemo{
public static void main(String args[]){
SquareArea s = new SquareArea(4);
System.out.println("Square area :"+s.getArea());
}
}
class RectangleArea {
public int getArea(int length, int width){
return length * width;
}
}
class SquareArea extends RectangleArea {
int length;
public SquareArea(int length){
this.length = length;
}
public int getArea(){
return getArea(length,length);
}
}
橋:
- 構造模様です
- それは抽象化をその実装から切り離し、両方とも独立して変化する可能性があります
- 継承の代わりに構成が使用されているため、可能です
編集: (@quasoftの提案による)
このパターンには4つのコンポーネントがあります。
抽象化:インターフェースを定義します
RefinedAbstraction:抽象化を実装します:
実装者:実装のためのインターフェースを定義します
ConcreteImplementor:Implementorインターフェースを実装します。
コードスニペット:
Gear gear = new ManualGear();
Vehicle vehicle = new Car(gear);
vehicle.addGear();
gear = new AutoGear();
vehicle = new Car(gear);
vehicle.addGear();
関連記事:
いつブリッジパターンを使用しますか?アダプタパターンとどう違うのですか?
主な違い:ソース作成の記事から
- アダプタは、設計後に機能します。Bridgeは、それらが機能する前に機能させます。
- Bridgeは、抽象化と実装を独立して変更できるように事前に設計されています。アダプタは、無関係のクラスが連携して機能するように改良されています。
この投稿はかなり前から出回っています。ただし、ファサードはアダプターにいくらか似ていますが、まったく同じではないことを理解することが重要です。アダプタは、既存のクラスを通常は互換性のないクライアントクラスに「適応」させます。アプリケーションがクライアントとして使用している古いワークフローシステムがあるとします。あなたの会社は、ワークフローシステムを(インターフェースに関して)新しい「互換性のない」ものに置き換える可能性があります。ほとんどの場合、アダプタパターンを使用して、新しいワークフローエンジンのインターフェイスを実際に呼び出すコードを記述できます。ブリッジは一般的に別の方法で使用されます。さまざまなファイルシステム(ローカルディスク、NFSなど)で動作する必要のあるシステムが実際にある場合は、ブリッジパターンを使用して、すべてのファイルシステムで動作する1つの抽象化レイヤーを作成できます。これは基本的に、ブリッジパターンの単純な使用例です。ファサードとアダプターはいくつかのプロパティを共有しますがファサードは通常、既存のインターフェース/クラスを単純化するために使用されます。EJBの初期には、EJBのローカル呼び出しはありませんでした。開発者は常にスタブを入手し、それを絞り込んで「疑似リモート」と呼びました。これはしばしばパフォーマンスの問題を引き起こしました(特に実際にネットワーク上で呼び出された場合)。経験豊富な開発者は、ファサードパターンを使用して、クライアントに非常に粗いインターフェイスを提供します。次に、このファサードは、さまざまなよりきめ細かいメソッドを複数回呼び出します。全体として、これにより、必要なメソッド呼び出しの数が大幅に削減され、パフォーマンスが向上しました。
一番上の答えでは、@ Jamesは219ページのGoFからの文章を引用しています。ここで完全な説明を再現することは、価値があると思います。
アダプターとブリッジ
アダプターとブリッジのパターンには、いくつかの共通の属性があります。どちらも、別のオブジェクトに一定レベルの間接参照を提供することにより、柔軟性を促進します。どちらも、それ自体以外のインターフェースからこのオブジェクトに要求を転送する必要があります。
これらのパターンの主な違いは、その意図にあります。アダプタは、2つの既存のインターフェイス間の非互換性の解決に重点を置いています。これらのインターフェースがどのように実装されているかに焦点を当てておらず、それらが独立してどのように進化するかについても考慮していません。これは、どちらか一方を再実装することなく、2つの独立して設計されたクラスを連携させる方法です。一方、Bridgeは、抽象化とその(潜在的に多数の)実装をブリッジします。それを実装するクラスを変えることができるとしても、それはクライアントに安定したインターフェースを提供します。また、システムの進化に合わせて新しい実装にも対応します。
これらの違いの結果として、アダプタとブリッジはソフトウェアライフサイクルのさまざまな時点で使用されることがよくあります。通常、コードの複製を避けるために、互換性のない2つのクラスが連携する必要があることに気付いた場合、アダプターが必要になることがよくあります。カップリングは予期しないものです。対照的に、ブリッジのユーザーは、抽象化にはいくつかの実装が必要であり、両方が独立して進化する可能性があることを事前に理解しています。アダプタパターンは、設計後に機能します。Bridgeは、それらが機能する前に機能させます。これは、AdapterがBridgeよりも劣っているという意味ではありません。各パターンは、単に異なる問題に対処します。
ブリッジは改良されたアダプターです。Bridgeにはアダプターが含まれており、柔軟性が追加されています。Ravindraの回答の要素がパターン間でどのようにマッピングされるかを次に示します。
Adapter | Bridge
-----------|---------------
Target | Abstraction
-----------|---------------
| RefinedAbstraction
|
| This element is Bridge specific. If there is a group of
| implementations that share the same logic, the logic can be placed here.
| For example, all cars split into two large groups: manual and auto.
| So, there will be two RefinedAbstraction classes.
-----------|---------------
Adapter | Implementor
-----------|---------------
Adaptee | ConcreteImplementor
ここでの別のstackoverflowの回答によると、私には短く明確な回答のように見えます:
アダプタは、抽象インターフェイスがあり、そのインターフェイスを、同様の機能的役割を持つがインターフェイスが異なる別のオブジェクトにマップする場合に使用されます。
BridgeはAdapterと非常に似ていますが、抽象インターフェースと基礎となる実装の両方を定義するときにBridgeと呼びます。つまり、レガシーコードやサードパーティコードに適応していない場合、すべてのコードの設計者ですが、さまざまな実装を交換できる必要があります。
(汎用/抽象)描画機能を備えた抽象Shapeクラスと、Shapeを実装するCircleがあるとします。ブリッジパターンは、実装(Circleで描画)と汎用/抽象化機能(Shapeクラスで描画)を分離するための双方向の抽象化アプローチです。
それは本当にどういう意味ですか?一見すると、それはあなたがすでに作っているもののように聞こえます(依存性逆転による)。ですから、あまり馬鹿げていない、またはよりモジュール化されたコードベースを持っていることについて心配する必要はありません。しかし、それはその背後にあるもう少し深い哲学です。
私の理解では、現在のシステム(RedCircleやGreenCircleなど)と密接に関連し、単一の機能(colorなど)のみが異なる新しいクラスを追加する必要がある場合に、使用パターンの必要性が生じる可能性があります。また、特に既存のシステムクラス(CircleまたはShape)が頻繁に変更され、新しく追加されたクラスがそれらの変更の影響を受けないようにする場合は、Bridgeパターンが必要になります。そのため、一般的な描画機能が新しいインターフェイスに抽象化され、ShapeやCircleから独立して描画動作を変更できるようになりました。
アダプタとブリッジを区別するための答えはたくさんあります。しかし、pplはコード例を探しているので、タイムラインストーリーに細工されたアダプターデザインパターンの例を1つ挙げます。
//---------------------------------------External Vendor/Provider--------------------------------
//Adaptee | RussianTankInterface is adaptee | adaptee lives in is own lala land and do not care about any other class or interface
RussianTankInterface smerch9K58 = new RussianTank("The Russian Artillery bought by India in October 2015");
smerch9K58.aboutMyself();
smerch9K58.stuff();
smerch9K58.rotate();
smerch9K58.launch();
//---------------------------------2016 : India manufactures Bharat52 ------------------------------
//Client_1 :: IndianTank
EnemyAttacker bharat52Attacker = new IndianTank("Tank built in India delivered to Army in Jul 2016");
// behaves normally -------------------------(1)
bharat52Attacker.aboutMe();
bharat52Attacker.load();
bharat52Attacker.revolve();
bharat52Attacker.fireArtillery();
//---------------------------------2019 : India mnufactures Pinaka, and thought about fusion with Russian technology - so adaption required ------------------------------
//Client_2 :: IndianTank
EnemyAttacker pinakaAttacker = new IndianTank("Tank built in India in 1998 got upgraded_v1 in 9 Sep 2019");
#####----Bilateral-Coalition happens----##
##### India : I want a fusion artillery technology with
##### 1) Indian materials and brain-power but
##### 2) Russian machine-parts-movement technology
##### Russia : Give me your Interface - at max we can help by providing an Adapter
//---------------------------------------External Vendor/Provider-----------------------------------
//Adapter :: RussianTechnologyAdapter | Russia gets EnemyAttacker interface only from India & creates RussianTechnologyAdapter
RussianTechnologyAdapter russianTechnologyAdapter = new RussianTechnologyAdapter(smerch9K58);
//Target | EnemyAttacker was initially ClientInterface but later becomes the Target as story evolves | <- client owns this Interface
EnemyAttacker dhanushAttacker = russianTechnologyAdapter;
#####----Russia keeps her Word----##
##### Russia to India : Here you go! Take Dhanush, a wrapper over our encapsulated adapter, and plug-in anything conforming to your EnemyAttacker.
##### India : Thanks a lot!
//--------------------------------- 2020 : India returns back happily with dhanushAttacker---------------------------------------
//Client_2 - adapted behavior -------------------------(2)
dhanushAttacker.setNavigationCapability(pinakaAttacker.getCuttingEdgeNavigableTargets());
dhanushAttacker.aboutMe(); //calls RussianInstance -> aboutMyself()
dhanushAttacker.load(); //calls RussianInstance -> stuff()
dhanushAttacker.revolve(); //calls RussianInstance -> rotate()
dhanushAttacker.fireArtillery(); //calls RussianInstance -> launch()
注意深く注意してください:
- (1)と(2)の同じAPIの動作が異なります
- インドはその頭脳をロシアのwireFrameに押し込むことができます。
dhanushAttacker.setNavigationCapability(pinakaAttacker.get(..))
注目すべき点
クライアント側が所有するもの:
- Invoker /Use(uses Adapter later point)/Client
- ClientInterface (a.k.a Target )
後で共有:
- ClientInterface ( becomes Target after sharing)
受信者側が所有します:
- Adapter (later shared directly or as a wrapper )
- Adaptee
誰かがBridgeにもインラインを提供することを願っています:)
BridgeはAdapterに非常に似ていますが、抽象インターフェイスと基盤となる実装の両方を定義するときにBridgeと呼びます。つまり、外部コードに適応しないことを意味します。すべてのコードの設計者ですが、別のコードを交換できる必要があります。実装。
簡単だと思います。
アダプタは、サードパーティのアプリケーションがアプリケーションと連携できるように設計されています。逆に、アプリケーションがサードパーティのアプリケーションと連携できるようにします。
ブリッジパターンを使用して、アダプタを実装せずに2つ以上のアプリケーションを接続することになっています。
実際、ブリッジは2つのアプリケーションが相互作用するためのインターフェースです。ブリッジングの例として、これらはPHPのPSRインターフェースです。
例:
OtherApp
<?php
interface IRequestDataOtherApp {};
interface IResponseDataOtherApp {};
class ResponseDataOtherApp implements IResponseDataOtherApp {
};
class OtherApp {
public static function request(IRequestDataOtherApp $requestData):IResponseOtherApp
{
// code
return new ResponseDataOtherApp ();
}
}
MyApp
<?php
interface IResponseDataMyApp {};
interface IReqestDataMyApp {};
class ReqestDataMyApp implements IReqestDataMyApp {};
class Adapter {
public static function convertResponseData(IResponseDataOtherApp $response):IResponseDataMyApp
{
}
public static function convertRequestData(IReqestDataMyApp $request):IRequestOtherApp
{
}
};
$unformattedResponse=OtherApp::request(Adapter::convertRequestData(new ReqestDataMyApp ()));
$myResponse=ResponseAdapter::convertResponseData($unformattedResponse);
//...
前の例では、リクエストごととレスポンスごとに2つのアダプターを実装しました。例を書き直して、ブリッジを実装しようとすると。
<?php
interface IBridgeResponse {};
OtherApp
<?php
interface IRequestDataOtherApp {};
interface IResponseDataOtherApp {};
class ResponseDataOtherApp implements IBridgeResponse, IResponseDataOtherApp {
};
class OtherApp {
public static function request(IRequestDataOtherApp $requestData):IResponseOtherApp
{
// code
return new ResponseDataOtherApp ();
}
}
MyApp
<?php
interface IResponseDataMyApp {};
interface IReqestDataMyApp {};
class ReqestDataMyApp implements IReqestDataMyApp {};
class Adapter {
public static function convertResponseData(IResponseDataOtherApp $response):IResponseDataMyApp
{
}
public static function convertRequestData(IReqestDataMyApp $request):IRequestOtherApp
{
}
};
$response=OtherApp::request(Adapter::convertRequestData(new ReqestDataMyApp ()));
if($response instanceof IBridgeResponse){
/**
The component has implemented IBridgeResponse interface,
thanks to which our application can interact with it.
This is the bridge.
Our application does not have to create an additional adapter
(if our application can work with IBridgeResponse).
*/
}
//...
六角形のアーキテクチャでは、最初からアプリケーションを作成し、使用されている別のアプリケーションのルールを受け入れる準備ができている場合、ポート(インターフェイス、コントラクト)は「ブリッジ」として機能できます。それ以外の場合は、「アダプタ」と記述する必要があります。