JavaにおけるBridgeパターン:抽象と実装を分離する
別名
- ハンドル/ボディ
Bridgeデザインパターンの意図
Bridgeデザインパターンは、Javaにおける構造パターンであり、抽象と実装を分離し、両者が独立して変化できるようにします。このパターンは、柔軟で拡張可能なソフトウェアシステムを開発する上で不可欠です。
現実世界の例を用いたBridgeパターンの詳細な説明
現実世界の例
Javaでは、BridgeパターンはGUIフレームワーク、データベースドライバ、デバイスドライバでよく使用されます。たとえば、ユニバーサルリモコン(抽象)は、一貫したインターフェースを通じてさまざまなテレビブランド(実装)を操作できます。
さまざまなブランドやタイプのテレビ(実装)を操作できるユニバーサルリモコン(抽象)を想像してみてください。リモコンは、電源のオン/オフ、チャンネルの変更、音量調整などの操作のための一貫したインターフェースを提供します。各テレビブランドまたはタイプには、これらの操作の独自の具体的な実装があります。Bridgeパターンを使用することにより、リモコンインターフェースはテレビの実装から分離され、リモコンはブランドや内部構造に関係なく、あらゆるテレビで動作できます。この分離により、リモコンのコードを変更することなく新しいテレビモデルを追加でき、同じテレビセットで動作するさまざまなリモコンを開発できます。
平易な言葉で
Bridgeパターンは、継承よりも合成を優先することです。実装の詳細は、階層から別の階層を持つ別のオブジェクトにプッシュされます。
Wikipediaによると
ブリッジパターンは、ソフトウェアエンジニアリングで使用されるデザインパターンであり、「抽象をその実装から分離して、両者が独立して変化できるようにする」ことを目的としています。
JavaにおけるBridgeパターンのプログラム例
さまざまなエンチャントを持つことができる武器があり、異なる武器と異なるエンチャントを組み合わせる必要があると想像してください。これをどのように処理しますか?それぞれの武器の複数のコピーを作成し、それぞれに異なるエンチャントを持たせるか、それとも個別のエンチャントを作成し、必要に応じて武器に適用しますか?Bridgeパターンを使用すると、後者が可能になります。
ここにWeapon
階層があります
public interface Weapon {
void wield();
void swing();
void unwield();
Enchantment getEnchantment();
}
public class Sword implements Weapon {
private final Enchantment enchantment;
public Sword(Enchantment enchantment) {
this.enchantment = enchantment;
}
@Override
public void wield() {
LOGGER.info("The sword is wielded.");
enchantment.onActivate();
}
@Override
public void swing() {
LOGGER.info("The sword is swung.");
enchantment.apply();
}
@Override
public void unwield() {
LOGGER.info("The sword is unwielded.");
enchantment.onDeactivate();
}
@Override
public Enchantment getEnchantment() {
return enchantment;
}
}
public class Hammer implements Weapon {
private final Enchantment enchantment;
public Hammer(Enchantment enchantment) {
this.enchantment = enchantment;
}
@Override
public void wield() {
LOGGER.info("The hammer is wielded.");
enchantment.onActivate();
}
@Override
public void swing() {
LOGGER.info("The hammer is swung.");
enchantment.apply();
}
@Override
public void unwield() {
LOGGER.info("The hammer is unwielded.");
enchantment.onDeactivate();
}
@Override
public Enchantment getEnchantment() {
return enchantment;
}
}
ここに個別のEnchantment
階層があります
public interface Enchantment {
void onActivate();
void apply();
void onDeactivate();
}
public class FlyingEnchantment implements Enchantment {
@Override
public void onActivate() {
LOGGER.info("The item begins to glow faintly.");
}
@Override
public void apply() {
LOGGER.info("The item flies and strikes the enemies finally returning to owner's hand.");
}
@Override
public void onDeactivate() {
LOGGER.info("The item's glow fades.");
}
}
public class SoulEatingEnchantment implements Enchantment {
@Override
public void onActivate() {
LOGGER.info("The item spreads bloodlust.");
}
@Override
public void apply() {
LOGGER.info("The item eats the soul of enemies.");
}
@Override
public void onDeactivate() {
LOGGER.info("Bloodlust slowly disappears.");
}
}
両方の階層の動作例
public static void main(String[] args) {
LOGGER.info("The knight receives an enchanted sword.");
var enchantedSword = new Sword(new SoulEatingEnchantment());
enchantedSword.wield();
enchantedSword.swing();
enchantedSword.unwield();
LOGGER.info("The valkyrie receives an enchanted hammer.");
var hammer = new Hammer(new FlyingEnchantment());
hammer.wield();
hammer.swing();
hammer.unwield();
}
コンソール出力です。
The knight receives an enchanted sword.
The sword is wielded.
The item spreads bloodlust.
The sword is swung.
The item eats the soul of enemies.
The sword is unwielded.
Bloodlust slowly disappears.
The valkyrie receives an enchanted hammer.
The hammer is wielded.
The item begins to glow faintly.
The hammer is swung.
The item flies and strikes the enemies finally returning to owner's hand.
The hammer is unwielded.
The item's glow fades.
Bridgeパターンのクラス図

JavaでBridgeパターンを使用する状況
以下のような場合は、Bridgeパターンを使用することを検討してください
- 実装をランタイムで選択または切り替える必要がある場合など、抽象とその実装との間に永続的なバインディングを回避する必要がある場合。
- 抽象とその実装の両方が、サブクラス化を通じて拡張可能である必要があり、各コンポーネントを独立して拡張できるようにする場合。
- 抽象の実装に対する変更がクライアントに影響を与えないようにする必要がある場合、つまり、クライアントのコードを再コンパイルする必要がないようにする場合。
- 階層に多数のクラスが存在する場合。これは、Rumbaughが「ネストされた一般化」と呼ぶ、オブジェクトを2つの部分に分割する必要があることを示しています。
- 複数のオブジェクト間で実装を共有したい場合。参照カウントを使用したり、クライアントに詳細を隠したりする可能性があります。CoplienのStringクラスが例示するように、複数のオブジェクトが同じ文字列表現を共有できます。
BridgeパターンJavaチュートリアル
JavaにおけるBridgeパターンの実際の応用例
- 抽象がウィンドウであり、実装が基盤となるOSウィンドウシステムであるGUIフレームワーク。
- 抽象が汎用データベースインターフェースであり、実装がデータベース固有のドライバであるデータベースドライバ。
- 抽象がデバイスに依存しないコードであり、実装がデバイスに依存するコードであるデバイスドライバ。
Bridgeパターンの利点とトレードオフ
利点
- インターフェースと実装の分離:Bridgeパターンは、インターフェース(高レベルの操作)と実装(低レベルの操作)を分離することにより、モジュール性を強化します。
- 拡張性の向上:抽象階層と実装階層を独立して拡張できます。
- 実装の詳細の隠蔽:クライアントは抽象のインターフェースのみを参照し、実装は参照しません。
トレードオフ
- 複雑さの増加:特にこのパターンに慣れていないクライアントの場合、パターンによってシステムアーキテクチャとコードが複雑になる可能性があります。
- ランタイムオーバーヘッド:抽象化の追加レイヤーによってパフォーマンスのペナルティが生じる可能性がありますが、実際には無視できることが多いです。
関連するJavaデザインパターン
- Abstract Factory:Abstract Factoryパターンは、Bridgeパターンと一緒に使用して、オブジェクトの作成に使用される具体的なクラスに依存しないプラットフォームを作成できます。
- Adapter:Adapterパターンは、オブジェクトに異なるインターフェースを提供するために使用され、Bridgeパターンはオブジェクトのインターフェースを実装から分離するために使用されます。
- Composite:Bridgeパターンは、コンポーネントの実装の詳細をモデル化するために、Compositeパターンと組み合わせて使用されることがよくあります。
- Strategy:StrategyパターンはBridgeパターンに似ていますが、意図が異なります。両方のパターンは合成に基づいています。Strategyはクラスの動作を変更するために合成を使用し、Bridgeは抽象を実装から分離するために合成を使用します。