CodeGym /Java Blog /ランダム /アダプター設計パターンはどのような問題を解決しますか?
John Squirrels
レベル 41
San Francisco

アダプター設計パターンはどのような問題を解決しますか?

ランダム グループに公開済み
ソフトウェア開発は、連携して動作する必要がある互換性のないコンポーネントによってさらに困難になります。たとえば、新しいライブラリを以前のバージョンの Java で記述された古いプラットフォームと統合する必要がある場合、互換性のないオブジェクト、または互換性のないインターフェイスが発生する可能性があります。 アダプター設計パターンはどのような問題を解決しますか?  - 1この場合どうすればよいでしょうか? コードを書き換えますか? システムの分析に時間がかかるか、アプリケーションの内部ロジックに違反する可能性があるため、それはできません。この問題を解決するために、アダプター パターンが作成されました。これは、互換性のないインターフェイスを持つオブジェクトが連携して動作するのに役立ちます。使い方を見てみましょう!

問題の詳細

まず、古いシステムの動作をシミュレートします。それが仕事や学校に遅刻する言い訳を生み出したとします。これを行うために、 および メソッドを備えたインターフェイスが用意されていExcuseます。 generateExcuse()likeExcuse()dislikeExcuse()

public interface Excuse {
   String generateExcuse();
   void likeExcuse(String excuse);
   void dislikeExcuse(String excuse);
}
このWorkExcuseクラスは次のインターフェイスを実装します。

public class WorkExcuse implements Excuse {
   private String[] excuses = {"in an incredible confluence of circumstances, I ran out of hot water and had to wait until sunlight, focused using a magnifying glass, heated a mug of water so that I could wash.",
   "the artificial intelligence in my alarm clock failed me, waking me up an hour earlier than normal. Because it is winter, I thought it was still nighttime and I fell back asleep. Everything after that is a bit hazy.",
   "my pre-holiday mood slows metabolic processes in my body, leading to depression and insomnia."};
   private String [] apologies = {"This will not happen again, of course. I'm very sorry.", "I apologize for my unprofessional behavior.", "There is no excuse for my actions. I am not worthy of this position."};

   @Override
   public String generateExcuse() { // Randomly select an excuse from the array
       String result = "I was late today because " + excuses[(int) Math.round(Math.random() + 1)] + "\\n" +
               apologies[(int) Math.round(Math.random() + 1)];
       return result;
   }

   @Override
   public void likeExcuse(String excuse) {
       // Duplicate the element in the array so that its chances of being chosen are higher
   }

   @Override
   public void dislikeExcuse(String excuse) {
       // Remove the item from the array
   }
}
この例をテストしてみましょう。

Excuse excuse = new WorkExcuse();
System.out.println(excuse.generateExcuse());
出力:

"I was late today because my pre-holiday mood slows metabolic processes in my body, leading to depression and insomnia.
I apologize for my unprofessional behavior.
ここで、言い訳生成サービスを立ち上げて統計を収集し、ユーザーのほとんどが大学生であることに気付いたと想像してください。このグループにより良いサービスを提供するために、あなたは別の開発者に、特に大学生向けの言い訳を生成するシステムを作成するよう依頼しました。開発チームは市場調査を実施し、言い訳をランク付けし、人工知能を接続し、サービスを交通情報や天気予報などと統合しました。これで、大学生向けの言い訳を生成するライブラリができましたが、インターフェースは異なりますStudentExcuse

public interface StudentExcuse {
   String generateExcuse();
   void dislikeExcuse(String excuse);
}
このインターフェイスにはgenerateExcuse、言い訳を生成する と、dislikeExcuse言い訳が今後再び表示されないようにする の 2 つのメソッドがあります。サードパーティのライブラリは編集できません。つまり、ソース コードを変更することはできません。これで、インターフェイスを実装する 2 つのクラスを含むシステムと、インターフェイスを実装するクラスをExcuse含むライブラリが完成しました。 SuperStudentExcuseStudentExcuse

public class SuperStudentExcuse implements StudentExcuse {
   @Override
   public String generateExcuse() {
       // Logic for the new functionality
       return "An incredible excuse adapted to the current weather conditions, traffic jams, or delays in public transport schedules.";
   }

   @Override
   public void dislikeExcuse(String excuse) {
       // Adds the reason to a blacklist
   }
}
コードは変更できません。現在のクラス階層は次のようになります。 アダプター設計パターンはどのような問題を解決しますか?  - 2このバージョンのシステムは Excuse インターフェイスでのみ動作します。コードを書き直すことはできません。大規模なアプリケーションでは、そのような変更を行うと時間がかかるプロセスになったり、アプリケーションのロジックが壊れたりする可能性があります。基本インターフェイスを導入して階層を拡張できます。 アダプター設計パターンはどのような問題を解決しますか?  - 3これを行うには、インターフェイスの名前を変更する必要がありますExcuse。しかし、本格的なアプリケーションでは余分な階層は望ましくありません。共通のルート要素を導入するとアーキテクチャが破壊されます。新しい機能と古い機能の両方を最小限の損失で使用できるようにする中間クラスを実装する必要があります。つまり、アダプターが必要です

アダプターパターンの原理

アダプターは、あるオブジェクトのメソッド呼び出しを別のオブジェクトが理解できるようにする中間オブジェクトです。この例のアダプターを実装して、それを と呼びましょうMiddleware。アダプターは、いずれかのオブジェクトと互換性のあるインターフェースを実装する必要があります。そうしましょうExcuse。これにより、Middleware最初のオブジェクトのメソッドを呼び出すことができます。 Middleware呼び出しを受信し、互換性のある方法で 2 番目のオブジェクトに転送します。およびメソッドMiddlewareを使用した実装は次のとおりです。 generateExcusedislikeExcuse

public class Middleware implements Excuse { // 1. Middleware becomes compatible with WorkExcuse objects via the Excuse interface

   private StudentExcuse superStudentExcuse;

   public Middleware(StudentExcuse excuse) { // 2. Get a reference to the object being adapted
       this.superStudentExcuse = excuse;
   }

   @Override
   public String generateExcuse() {
       return superStudentExcuse.generateExcuse(); // 3. The adapter implements an interface method
   }

    @Override
    public void dislikeExcuse(String excuse) {
        // The method first adds the excuse to the blacklist,
        // Then passes it to the dislikeExcuse method of the superStudentExcuse object.
    }
   // The likeExcuse method will appear later
}
テスト (クライアント コード内):

public class Test {
   public static void main(String[] args) {
       Excuse excuse = new WorkExcuse(); // We create objects of the classes
       StudentExcuse newExcuse = new SuperStudentExcuse(); // that must be compatible.
       System.out.println("An ordinary excuse for an employee:");
       System.out.println(excuse.generateExcuse());
       System.out.println("\n");
       Excuse adaptedStudentExcuse = new Middleware(newExcuse); // Wrap the new functionality in the adapter object
       System.out.println("Using new functionality with the adapter:");
       System.out.println(adaptedStudentExcuse.generateExcuse()); // The adapter calls the adapted method
   }
}
出力:

An ordinary excuse for an employee:
I was late today because my pre-holiday mood slows metabolic processes in my body, leading to depression and insomnia.
There is no excuse for my actions. I am not worthy of this position. Using new functionality with the adapter:
現在の気象状況、交通渋滞、公共交通機関の遅延に合わせた信じられない言い訳です。このgenerateExcuseメソッドは、追加の変更を行わずに、呼び出しを別のオブジェクトに渡すだけです。このdislikeExcuse方法では、まず言い訳をブラックリストに登録する必要がありました。中間データ処理を実行できることが、アダプター パターンが好まれる理由です。しかしlikeExcuse、インターフェースの一部であってExcuseもインターフェースの一部ではないメソッドはどうなるでしょうかStudentExcuse? 新しい機能はこの操作をサポートしていません。はUnsupportedOperationExceptionこの状況のた​​めに発明されました。要求された操作がサポートされていない場合にスローされます。使ってみましょう。Middlewareクラスの新しい実装は 次のようになります。

public class Middleware implements Excuse {

   private StudentExcuse superStudentExcuse;

   public Middleware(StudentExcuse excuse) {
       this.superStudentExcuse = excuse;
   }

   @Override
   public String generateExcuse() {
       return superStudentExcuse.generateExcuse();
   }

   @Override
   public void likeExcuse(String excuse) {
       throw new UnsupportedOperationException("The likeExcuse method is not supported by the new functionality");
   }

   @Override
   public void dislikeExcuse(String excuse) {
       // The method accesses a database to fetch additional information,
       // and then passes it to the superStudentExcuse object's dislikeExcuse method.
   }
}
一見すると、このソリューションはあまり良くないように見えますが、機能を模倣すると状況が複雑になる可能性があります。クライアントが注意を払い、アダプターが十分に文書化されている場合、そのようなソリューションは受け入れられます。

アダプターを使用する場合

  1. サードパーティのクラスを使用する必要があるが、そのインターフェイスがメイン アプリケーションと互換性がない場合。上の例は、ターゲット オブジェクトが理解できる形式で呼び出しをラップするアダプター オブジェクトを作成する方法を示しています。

  2. いくつかの既存のサブクラスに共通の機能が必要な場合。追加のサブクラスを作成する (コードの重複につながる) 代わりに、アダプターを使用することをお勧めします。

長所と短所

利点:アダプターは、あるオブジェクトから別のオブジェクトへの処理リクエストの詳細をクライアントから隠します。クライアント コードは、データのフォーマットやターゲット メソッドへの呼び出しの処理については考慮しません。複雑すぎて、プログラマは怠け者です :) 欠点:追加のクラスによってプロジェクトのコード ベースが複雑になります。互換性のないインターフェイスが多数ある場合、追加クラスの数が管理できなくなる可能性があります。

アダプターをファサードまたはデコレーターと混同しないでください

表面的な検査だけでは、アダプターがファサードやデコレーターのパターンと混同される可能性があります。アダプターとファサードの違いは、ファサードが新しいインターフェイスを導入し、サブシステム全体をラップすることです。また、デコレータはアダプタとは異なり、インターフェイスではなくオブジェクト自体を変更します。

段階的なアルゴリズム

  1. まず、このパターンで解決できる問題があることを確認してください。

  2. 互換性のないオブジェクトと間接的に対話するために使用されるクライアント インターフェイスを定義します。

  3. アダプター クラスに、前の手順で定義したインターフェイスを継承させます。

  4. アダプター クラスで、アダプティ オブジェクトへの参照を保存するフィールドを作成します。この参照はコンストラクターに渡されます。

  5. すべてのクライアント インターフェイス メソッドをアダプターに実装します。メソッドは次のことを行うことができます。

    • 変更を加えずに通話を引き継ぎます

    • データの修正や補足、対象メソッドの呼び出し回数の増減など。

    • 極端な場合、特定のメソッドに互換性がない場合は、UnsupportedOperationException がスローされます。サポートされていない操作は厳密に文書化する必要があります。

  6. アプリケーションがクライアント インターフェイス経由でのみアダプター クラスを使用する場合 (上記の例のように)、アダプターは将来問題なく拡張できます。

もちろん、このデザイン パターンはすべての問題を解決する万能薬ではありませんが、異なるインターフェイスを持つオブジェクト間の非互換性の問題をエレガントに解決するのに役立ちます。本格的なアプリケーションを作成するには設計パターンが必要であるため、基本パターンを知っている開発者は、アルゴリズムの書き方しか知らない開発者よりも数歩先を行っています。コードの再利用はそれほど難しくなく、メンテナンスが楽しくなります。それが今日のすべてです!しかし、私たちはすぐにさまざまなデザインパターンを知ることになるでしょう:)
コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION