
「Adapter」ってどんなパターン?
既存のクラスに、別のインターフェイス(メソッド)を持たせたい場合に使えます。
1 2 |
例えば、「クラスA」と、「クラスAに似ているが微妙に異なるクラスB」があり、クラスBをクラスAと同じプログラムの中で、同じように呼び出したいが、 微妙な異なる部分だけ、同じ書き方で取り出せないといった場合に効果を発揮します。「クラスA」と同じインターフェイスになるように、「クラスB」を変換してあげます。た |
どのような場面で使うか?
呼び出し先のソースに手を加えることができない状況というのは応答にして発生するのでそのような場合に使うと良いでしょう。
- 他社の製品
- そもそもソースがない。(実行ファイルだけもらっている)
- すでにテストされていて手を加えるとテストが大変になる。
メリット
既存クラスを修正することなく、適切なインターフェイスを追加することができます。要は、クラスの型を整えるための「ラッパー」ともいえますね。
例えば、大きいシステムで、既存クラスや、これから修正するクラスが既に色々なクラスで使われてしまっていて、影響調査が難しいといった場合に効果を発揮します。
クラスの設計方法は?
変換のためには、「アダプタ」という変換用のクラスを作ります。
「アダプタクラス」では、あくまで、インターフェイスを追加することが目的で、それ以外の独自のメソッドは実装しないです。
イメージ
Client
利用者
ターゲットインターフェース
「望みの規格」になります。インターフェースか抽象クラスなどで実装します。
Adapter
適合させる側、ターゲットインターフェースを継承させて作ります。
Adaptee
適合される側。具象クラスをそのまま使っても良いですが、Adapteeインターフェースを一旦かまして複数の同じような具象クラスを継承させて実装することも可能だったりします。
AdapterとAdapteeの接続方法
AdapterとAdapteeの接続方法には2種類の実装方法があります。結論から申しますと「コンポジションを使った実装が良い」です。
コンポジションを使う方法
「持つ」手法。Adapter内にAdapteeのコードをメンバ変数としてコンポジションで持たせます。
継承を使う方法
「Class Adapterパターン」とも言われます。
Adapter内でAdapteeのコードを継承させて実装します。
なぜコンポジションの方が良いのか
そもそも近年の設計では具象クラスを継承することは推奨されていない
そもそも、近年は具象クラスを継承することはあまり推奨されていないです。(スーパークラスを読まないとサブクラスの内容を理解できないので開発者の負荷が増えるなどが理由だったりします。)
ターゲットインターフェースに選択肢が生まれる
二重継承が禁止されている言語もあるので、その場合はターゲットインターフェースがインターフェースにしかできません。(抽象クラスを選べない。)
「Adapter:Adaptee=1:多」にすることもできる。
メンバ変数ならインターフェースをメンバの型として使えるのでAdapteeを複数に拡張性が生まれます。
実際に実装してみる。
実装するクラス図や、実装イメージは下記です。
Main.java(メインプログラム)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
package Adapter; public class Main { public static void main(String[] args) { //顧客1:アダプタなしで呼び出せる。 Customer1 customer1 = new Customer1("北海道XXX市○○町"); Post.sendPost(customer1); //顧客2:アダプタを介すれば同じように呼び出せる。 Customer2 customer2 = new Customer2("山口県","XXX市○○町"); Adapter adpCustomer2 = new Adapter(customer2); Post.sendPost(adpCustomer2); } } |
Post.java(Customer1クラスをインターフェイスとするクラス)
1 2 3 4 5 6 7 8 9 10 11 |
package Adapter; /** * 郵送クラス */ public class Post { public static void sendPost(Customer1 customer) { System.out.println(customer.getAddress()+"に郵送します。"); } } |
Customer1.java(Post.javaから利用されることができるクラス)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
package Adapter; public class Customer1 { private String address; Customer1(String address){ this.address = address; } public String getAddress() { return this.address; } } |
Customer2.java(Post.javaから、新たに利用できるようにしたいクラス)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
package Adapter; public class Customer2 { /* 県 */ private String prefecture; /* 市区町村 */ private String city; Customer2(String prefecture,String city){ this.prefecture = prefecture; this.city = city; } public String getPrefecture() { return this.prefecture; } public String getCity() { return this.city; } } |
Adapter.java(Customer2.javaを利用できるようにするために仲介となるクラス)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
package Adapter; public class Adapter extends Customer1 { Customer2 customer2; Adapter(Customer2 customer2){ //継承元のコンストラクタ呼び出しはダミー。 super(null); this.customer2 = customer2; } /* * 継承元のメソッドを、オーバーライドして、アダプタ用のメソッドを作成する。 */ public String getAddress() { return this.customer2.getPrefecture() + this.customer2.getCity(); } } |
実行結果
アダプタを介することで、既存クラスと同じようにsendPostメソッドを使うことができました。
この記事へのコメントはありません。