【Java】Adapterパターンとは?2つの実装例を解説

【Java】Adapterパターンとは?2つの実装例を解説

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

記事の文字数:2,558

JavaのAdapterパターン(アダプターパターン)について、現実世界の例えや、クラスによる実装・オブジェクトによる実装の違い、具体的なコード例を用いて分かりやすく解説します。既存のクラスを修正せずに新しいインターフェースに適合させる方法を学びましょう。

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

おすすめ

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

難易度
実用性
図解

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

ベストセラー

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

難易度
実用性
思考法

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

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

難易度
実用性
キャリア度

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

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

難易度
実用性
コミュ力

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

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

難易度
実用性
独自性

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

JavaJava [ジャバ]オブジェクト指向プログラミング言語でシステム開発をしていると、「既存の便利なクラスを使いたいけれど、今求めているインターフェース(メソッド名や引数の型など)と合わない…」という問題に直面することがあります。

このようなインターフェースのズレを吸収し、既存のコードを変更せずに再利用できるようにするのが、GoF(Gang of Four)のデザインパターンの1つである Adapter(アダプター)パターン です。

本記事では、Adapterパターンの概要や現実世界での例え、2種類の実装方法(クラスとオブジェクト)、そして具体的なJavaコード例まで分かりやすく解説します。

1. JavaのAdapterパターン(アダプターパターン)とは?

Adapterパターンは、その名の通り「アダプター(変換器)」の役割を果たすデザインパターンです。 互換性のないインターフェースを持つクラス同士を繋ぎ、連携して動作できるようにする ことを目的としています。

既存のクラス(提供されている側)のソースコードに手を加えることなく、クライアント(利用する側)が期待するインターフェースに変換(適合)させることができるため、サードパーティ製のライブラリやレガシーコードを活用する際に非常に役立ちます。

現実世界での例え:ACアダプターをイメージしよう

身近な例として、スマートフォンの充電などに使う「ACアダプター」を想像してみてください。

  • Target(目的):機器が利用できる「直流(DC)」を求めている
  • Adaptee(既存のもの):家庭用のコンセントは「交流(AC)」を提供している
  • Adapter(適合者):ACアダプターが「交流(AC)」を「直流(DC)」に変換する

このように、仕様が異なる2つの間に入って橋渡しをするのがAdapterの役割です。

2. Adapterパターンの4つの構成要素(Target・Adapteeなど)

Adapterパターンには、主に以下の4つの役割(構成要素)が登場します。

  1. Target(対象) クライアントが使いたい(期待している)インターフェースです。
  2. Adaptee(適合される側) 既に存在しているが、Targetとはインターフェースが合わないクラスです(レガシーコードなど)。
  3. Adapter(適合者) Targetのインターフェースを実装(または継承)し、内部でAdapteeのメソッドを呼び出すことで、ズレを吸収するクラスです。
  4. Client(利用者) Targetのインターフェースを使って処理を行うクラスです。

3. Adapterパターンの2種類の実装方法とJavaコード例

JavaにおけるAdapterパターンの実装には、大きく分けて以下の2つの手法があります。

  1. 継承を使った実装(クラスアダプター)
  2. 委譲(コンポジション)を使った実装(オブジェクトアダプター)

それぞれの違いを具体的なコードとともに見ていきましょう。

ここでは、新しいシステムで使いたい ModernPrinter(Target)と、既存の古い印刷クラス LegacyPrinter(Adaptee)があると仮定します。

① 継承を使った実装方法(クラスアダプター・Class Adapter)

クラスアダプターは、既存のクラス(Adaptee)を継承し、同時にTargetインターフェースを実装することで変換を行います。

Target(期待するインターフェース)
public interface ModernPrinter {
void modernPrint(String text);
}
Adaptee(既存のクラス)
public class LegacyPrinter {
public void legacyPrint(String text) {
System.out.println("【旧型プリンター】: " + text);
}
}
Adapter(クラスアダプターによる変換)
// Targetインターフェースを実装し、Adapteeクラスを継承する
public class PrinterClassAdapter extends LegacyPrinter implements ModernPrinter {
@Override
public void modernPrint(String text) {
// 継承しているLegacyPrinterのメソッドを呼び出す
this.legacyPrint(text);
}
}
Client(実行クラス)
public class Main {
public static void main(String[] args) {
// クライアントはModernPrinterとして扱うことができる
ModernPrinter printer = new PrinterClassAdapter();
printer.modernPrint("こんにちは、クラスアダプター!");
}
}

【特徴】 Javaではクラスの多重継承ができないため、Class Adapterを実装する場合、一般的にはAdapteeを継承しつつ、Targetをインターフェースとして実装します。

② 委譲を使った実装方法(オブジェクトアダプター・Object Adapter)

オブジェクトアダプターは、既存のクラス(Adaptee)のインスタンスを内部に保持し、その処理を委譲することで変換を行います。オブジェクト指向設計における「継承よりもコンポジションを優先する」という原則(合成優先の原則)とも相性がよく、一般的に実務ではこちらの手法が推奨されることが多いです。

Adapter(オブジェクトアダプターによる変換)
// Targetインターフェースのみを実装する
public class PrinterObjectAdapter implements ModernPrinter {
// Adapteeのインスタンスを保持する
private LegacyPrinter legacyPrinter;
// コンストラクタでAdapteeを受け取る
public PrinterObjectAdapter(LegacyPrinter legacyPrinter) {
this.legacyPrinter = legacyPrinter;
}
@Override
public void modernPrint(String text) {
// 保持しているインスタンスに処理を委譲する
legacyPrinter.legacyPrint(text);
}
}
Client(実行クラス)
public class Main {
public static void main(String[] args) {
// 既存のインスタンスを生成
LegacyPrinter legacyPrinter = new LegacyPrinter();
// アダプターに既存のインスタンスを渡す
ModernPrinter printer = new PrinterObjectAdapter(legacyPrinter);
printer.modernPrint("こんにちは、オブジェクトアダプター!");
}
}

【特徴】 Adapteeの継承を必要としないため柔軟性が高く、Targetがクラスの場合でも、Adapter自身がTargetを継承することで対応できます(※今回のコード例ではTargetをインターフェースとして実装しています)。また、Adapteeのサブクラスに対しても同じアダプターを使い回すことができます。

4. Adapterパターンを導入するメリット・デメリット

Adapterパターンのメリット(既存コードの再利用)

  • 開放閉鎖原則(OCP)と相性がよい

    既存のコードを変更せずに新しいAdapterを追加して対応できるため、開放閉鎖原則(OCP)の考え方と相性がよいです。テスト済みの既存クラス(Adaptee)を一切書き換えることなく、新しいインターフェースに対応させることができます。

  • コードの再利用性が高まる

    古いシステムやサードパーティ製のライブラリを、現代のシステム要件に合わせて簡単に統合できます。

Adapterパターンのデメリット(構成の複雑化)

  • クラスの数が増える

    アダプタークラスを新しく作成する必要があるため、全体のコード量やクラス数が増加し、構成が複雑になる可能性があります。

  • 根本的な解決ではない場合がある

    もし既存クラス(Adaptee)自体を変更できる環境であり、かつ影響範囲が小さいのであれば、直接リファクタリングしてしまった方がコードがシンプルになるケースもあります。

5. まとめ:JavaのAdapterパターンで柔軟な設計を実現しよう

JavaのAdapterパターンは、「インターフェースが合わない既存のクラスを、新しい要件に合わせて変換する」 非常に実用的なデザインパターンです。

  • クラスアダプター(継承): Adapteeを継承して実装。Javaでは多重継承の制約に注意。
  • オブジェクトアダプター(委譲): Adapteeのインスタンスを保持して実装。より柔軟であり、合成優先の設計原則からも実務で推奨されることが多い。

「この便利なライブラリを使いたいけど、メソッド名が違うな…」という状況に遭遇したら、既存コードを無理にいじる前に、ぜひAdapterパターンの導入を検討してみてください。


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

Z (ITナレッジライフ)

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

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

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

おすすめ

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

難易度
実用性
図解

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

人気記事


記事を評価

Thanks!
目次
Scroll to Top