Java Bridgeパターン入門:基礎と実装例【デザインパターン】

Java Bridgeパターン入門:基礎と実装例【デザインパターン】

PR Amazonのアソシエイトとして、ITナレッジライフは適格販売により収入を得ています。

記事の文字数:2,332

「Java Bridge」と検索してデザインパターンの「Bridgeパターン」について学びたい方向けの記事です。GoFパターンの1つであるBridgeパターンの目的、クラス図、そしてJavaによる具体的な実装例をわかりやすく解説します。

ITエンジニアにお勧めの本 ↗

おすすめ

エンジニアが知っておきたい思考の整理術 複雑な情報を【理解する】【伝える】テクニック

難易度
実用性
図解

思考の整理というテーマを、実務ですぐに使える具体的な「技術」として落とし込んだ一冊です。

ベストセラー

世界一流エンジニアの思考法

難易度
実用性
思考法

技術スキル以上に大切な「考え方」をアップデートできる。世界基準の効率的な仕事術が学べます。

文系社会人のためのIT資格の歩き方: 文系のあなたに最適な道をご提案します (YKcreate kiki)

難易度
実用性
キャリア度

ロードマップが非常に明確。キャリアプランを見据えた、無駄のない学習順序を提案してくれます。

エンジニアを説明上手にする本 相手に応じた技術情報や知識の伝え方

難易度
実用性
コミュ力

「伝える技術」もエンジニアの重要なスキル。相手の視点に立った、具体的な説明のコツが満載です。

技術広報入門 ー テックブログから始めるエンジニアカルチャーのつくり方

難易度
実用性
独自性

テックブログを入口にエンジニア組織の文化をどう設計し、育てていくかという実践論が学べます。

JavaJava [ジャバ]オブジェクト指向プログラミング言語などのオブジェクト指向プログラミングを学んでいると、「Bridgeパターン」という言葉を耳にすることがあります。

本記事では、オブジェクト指向設計の文脈で用いられる GoF(Gang of Four)のデザインパターンの1つである「Bridgeパターン」 について目的やメリット、そしてJavaでの具体的な実装例を初心者にもわかりやすく解説します。

1. JavaにおけるBridgeパターン(デザインパターン)とは?

Bridgeパターンは、システムを 「機能のクラス階層」と「実装のクラス階層」の2つに分離 し、それらの間に橋(Bridge)を架けることで、それぞれを独立して拡張できるようにする構造に関するデザインパターンです。

なぜJavaプログラミングでBridgeパターンが必要なのか?

通常、あるクラスに新しい機能を追加したい場合、そのクラスを継承したサブクラスを作成します(機能の拡張)。 また、同じ機能で別の実装(例えば、Windows版とMac版、データベースの違いなど)を行いたい場合も、サブクラスを作成します(実装の拡張)。

しかし、単純な継承だけで「機能」と「実装」の両方を拡張しようとすると、以下のような問題(クラスの爆発)が発生します。

  • データ出力機能
    • CSV出力機能 (Windows版)
    • CSV出力機能 (Mac版)
    • PDF出力機能 (Windows版)
    • PDF出力機能 (Mac版)

機能が増えるたび、また対応プラットフォームが増えるたびに、掛け算でクラスの数が増大してしまいます。 これを防ぐために、「何をするか(抽象)」と「どうやって実現するか(実装)」を分け、委譲(コンポジション)によって結びつけるのがBridgeパターンです。

2. Bridgeパターンの構成要素とクラス構造

Bridgeパターンには、主に以下の4つの役割(クラス・インターフェース)が登場します。

【機能のクラス階層】

  1. Abstraction(抽象) 機能の基本となるクラスです。内部に「Implementor」のインスタンスを保持し、実際の処理はそちらに委譲します。
  2. RefinedAbstraction(拡張された抽象) Abstractionを継承し、新しい機能を追加・拡張したクラスです。

【実装のクラス階層】

  1. Implementor(実装者) Abstractionが呼び出すためのAPIAPI [エーピーアイ]Application Programming Interface。異なるソフトウェア間の接続口(メソッド)を定義するインターフェースです。
  2. ConcreteImplementor(具体的な実装者) Implementorインターフェースを実装し、具体的な処理内容を記述するクラスです。

3. JavaによるBridgeパターンの具体的な実装例

ここでは、「メッセージを表示する機能」と「表示先の媒体(コンソール出力、ファイル出力)」を分離するシンプルな例をJavaで実装してみます。

実装のクラス階層(どうやって表示するか)

まずは、表示処理そのものを定義するインターフェース(Implementor)を作成します。

DisplayImpl.java
// Implementor: 実装のインターフェース
public interface DisplayImpl {
void rawOpen();
void rawPrint(String message);
void rawClose();
}

次に、具体的な表示方法(ConcreteImplementor)を実装します。ここではコンソールにテキストアート風に出力するクラスとします。

ConsoleDisplayImpl.java
// ConcreteImplementor: 具体的な実装
public class ConsoleDisplayImpl implements DisplayImpl {
@Override
public void rawOpen() {
System.out.println("<< Start >>");
}
@Override
public void rawPrint(String message) {
System.out.println(" > " + message);
}
@Override
public void rawClose() {
System.out.println("<< End >>");
}
}

機能のクラス階層(何を表示するか)

次に、機能側の基本クラス(Abstraction)を作成します。 このクラスは、コンストラクタで DisplayImpl を受け取り(これが橋渡しになります)、実際の処理は impl に委譲します。

Display.java
// Abstraction: 機能の抽象クラス
public class Display {
// 実装部分への「橋(Bridge)」となるフィールド
private DisplayImpl impl;
public Display(DisplayImpl impl) {
this.impl = impl;
}
public void open() {
impl.rawOpen();
}
public void print(String message) {
impl.rawPrint(message);
}
public void close() {
impl.rawClose();
}
// 基本的な表示機能
public final void display(String message) {
open();
print(message);
close();
}
}

ここで機能を追加(RefinedAbstraction)してみましょう。 例えば、「指定した回数だけ繰り返し表示する」機能を追加したクラスを作成します。

CountDisplay.java
// RefinedAbstraction: 機能の拡張
public class CountDisplay extends Display {
public CountDisplay(DisplayImpl impl) {
super(impl);
}
// 新しい機能の追加
public void multiDisplay(String message, int times) {
open();
for (int i = 0; i < times; i++) {
print(message);
}
close();
}
}

実行クラス(Main)

最後に、これらを組み合わせて実行します。

Main.java
public class Main {
public static void main(String[] args) {
// 機能:基本表示 + 実装:コンソール表示
Display d1 = new Display(new ConsoleDisplayImpl());
// 機能:繰り返し表示 + 実装:コンソール表示
CountDisplay d2 = new CountDisplay(new ConsoleDisplayImpl());
System.out.println("--- 通常の表示 ---");
d1.display("Hello, Bridge Pattern!");
System.out.println("\n--- 繰り返しの表示 ---");
d2.multiDisplay("Java is Fun!", 3);
}
}

実行結果:

--- 通常の表示 ---
<< Start >>
> Hello, Bridge Pattern!
<< End >>
--- 繰り返しの表示 ---
<< Start >>
> Java is Fun!
> Java is Fun!
> Java is Fun!
<< End >>

この設計の強みは、今後「ファイルに出力する FileDisplayImpl」を作った場合でも、機能側(DisplayCountDisplay)のコードを一切変更せずに、そのまま組み合わせて使える点にあります。

4. Java開発におけるBridgeパターンのメリット

  1. 拡張性が高い

    機能(抽象)と実装が分離されているため、片方を変更・拡張する際に、もう片方への影響を抑えやすくなります。

  2. クラスの爆発を防ぐ

    継承関係を多用することによる、無数のサブクラスの誕生を防ぐことができます。

  3. オブジェクト生成時に実装を選択できる

    委譲(コンポジション)を使用しているため、プログラムの実行時(オブジェクト生成時)に実装(Implementor)のオブジェクトを動的に選択することが容易です。

補足:Adapterパターンとの違い

Bridgeパターンと似た構造を持つパターンに「Adapterパターン」があります。

  • Bridgeパターン:設計の初期段階で「抽象」と「実装」を分離し、これからそれぞれを独立して拡張できるようにすることが主目的です。
  • Adapterパターン既存クラスのインターフェースを、クライアントが期待するインターフェースに合わせる(変換する)ことが主目的です。

事前の設計(Bridge)か、事後の適合(Adapter)か、という目的の違いを意識すると使い分けやすくなります。

5. まとめ:Java Bridgeパターンで柔軟な設計を実現

Bridgeパターンは、「抽象」と「実装」を切り離すことで、柔軟でメンテナンスしやすい設計を実現するためのデザインパターンです。

継承は強力な仕組みですが、使いすぎるとクラス階層が複雑化してしまいます。「機能の追加」と「実装の追加」が同時に起こり得るようなシステムを設計する際は、このBridgeパターンを活用し、委譲によってすっきりとしたクラス構造を目指してみてください。


以上で本記事の解説を終わります。
よいITライフを!
ITナレッジライフ 運営者
この記事を書いた人

Z (ITナレッジライフ)

現役のITエンジニア。Linux、プログラミング、IT用語など、日々の業務で得た「痒いところに手が届く」技術情報を発信しています。

プロフィールと編集ポリシーを見る

ITエンジニアにお勧めの本 ↗

おすすめ

エンジニアが知っておきたい思考の整理術 複雑な情報を【理解する】【伝える】テクニック

難易度
実用性
図解

思考の整理というテーマを、実務ですぐに使える具体的な「技術」として落とし込んだ一冊です。

人気記事


記事を評価

Thanks!
目次
Scroll to Top