web-dev-qa-db-ja.com

アダプタ-アダプタパターンの実際の例

Adapter Pattern の使用をチームに示したいと思います。私は多くの本や記事をオンラインで読みました。誰もがコンセプトを理解するのに役立つ例(形状、メモリカード、電子アダプタなど)を引用していますが、実際のケーススタディはありません。

アダプタパターンのケーススタディを教えてください。

追伸stackoverflowで既存の質問を検索しようとしましたが、答えが見つからなかったため、新しい質問として投稿しました。これに対する回答が既にあることがわかっている場合は、リダイレクトしてください。

75
AksharRoop

アダプターの多くの例は、些細なまたは非現実的です( 長方形vs.レガシー長方形、ラチェットvs.ソケットSquarePeg vs RoundPegダックvs.トルコ )。さらに悪いことに、多くの場合、異なるアダプタのmultiple Adaptersが表示されません( アダプタパターンの例としてJavaのArrays.asListを引用した人 )。 1つのクラスのみのインターフェイスを別のインターフェイスと連動させることは、GoFアダプターパターンの弱い例のようです。このパターンは継承とポリモーフィズムを使用しているため、さまざまなアダプター用のアダプターの複数の実装を示す良い例が期待されます。

私が見つけた最良の例MLとパターンの適用:オブジェクト指向分析と設計と反復開発(第3版)の紹介 の26章にあります。以下の画像は、本のFTPサイトで提供されているインストラクター資料からのものです。

最初の例では、機能的に類似している(税計算機、会計モジュール、クレジット認証サービスなど)が、APIが異なる複数の実装(アダプター)をアプリケーションが使用する方法を示します。ドメインレイヤーコードをハードコーディングして、税の計算、販売後の処理、クレジットカードリクエストの承認など、さまざまな方法を処理するのを避けたいと思います。これらはすべて異なる外部モジュールであり、変更することはできませんコード。アダプターを使用すると、アダプターでハードコーディングを行うことができますが、ドメインレイヤーコードは常に同じインターフェイス(IWhateverAdapterインターフェイス)を使用します。

Fig. 26.1

上記の図には実際のアダプターは表示されていません。ただし、次の図は、IAccountingAdapterインターフェースでpostSale(...)の多態的な呼び出しがどのように行われるかを示しており、SAPシステムへのSOAP)による販売の転記になります。

Fig. 26.2

73
Fuhrmanator

フランス人を普通の人に変える方法...

 public interface IPerson
    {
        string Name { get; set; }
    }

    public interface IFrenchPerson
    {
        string Nom { get; set; }
    }

    public class Person : IPerson
    {
        public string Name { get; set; }
    }

    public class FrenchPerson : IFrenchPerson
    {
        public string Nom { get; set; }
    }

    public class PersonService
    {
        public void PrintName(IPerson person)
        {
            Debug.Write(person.Name);
        }
    }

    public class FrenchPersonAdapter : IPerson
    {
        private readonly IFrenchPerson frenchPerson;

        public FrenchPersonAdapter(IFrenchPerson frenchPerson)
        {
            this.frenchPerson = frenchPerson;
        }

        public string Name 
        {
            get { return frenchPerson.Nom; }
            set { frenchPerson.Nom = value; }
        }
    } 

    var service = new PersonService();
    var person = new Person();
    var frenchPerson = new FrenchPerson();

    service.PrintName(person);
    service.PrintName(new FrenchPersonAdapter(frenchPerson));
44
CountZero

インターフェイスを別のインターフェイスに変換します。

アダプタパターンの実際の例

電源を接続するために、世界中にさまざまなインターフェイスがあります。アダプタを使用すると、賢明なように簡単に接続できます。

enter image description here

42
Premraj

analog dataからdigit dataへの変換をシミュレートする例を次に示します。

浮動小数点データをバイナリデータに変換するアダプターを提供しますが、実際にはおそらく役に立たず、アダプターパターンの概念を説明するのに役立ちます。


コード

AnalogSignal.Java

package eric.designpattern.adapter;

public interface AnalogSignal {
    float[] getAnalog();

    void setAnalog(float[] analogData);

    void printAnalog();
}

DigitSignal.Java

package eric.designpattern.adapter;

public interface DigitSignal {
    byte[] getDigit();

    void setDigit(byte[] digitData);

    void printDigit();
}

FloatAnalogSignal.Java

package eric.designpattern.adapter;

import Java.util.Arrays;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class FloatAnalogSignal implements AnalogSignal {
    private Logger logger = LoggerFactory.getLogger(this.getClass());
    private float[] data;

    public FloatAnalogSignal(float[] data) {
        this.data = data;
    }

    @Override
    public float[] getAnalog() {
        return data;
    }

    @Override
    public void setAnalog(float[] analogData) {
        this.data = analogData;
    }

    @Override
    public void printAnalog() {
        logger.info("{}", Arrays.toString(getAnalog()));
    }
}

BinDigitSignal.Java

package eric.designpattern.adapter;

import Java.util.Arrays;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class BinDigitSignal implements DigitSignal {
    private Logger logger = LoggerFactory.getLogger(this.getClass());
    private byte[] data;

    public BinDigitSignal(byte[] data) {
        this.data = data;
    }

    @Override
    public byte[] getDigit() {
        return data;
    }

    @Override
    public void setDigit(byte[] digitData) {
        this.data = digitData;
    }

    @Override
    public void printDigit() {
        logger.info("{}", Arrays.toString(getDigit()));
    }
}

AnalogToDigitAdapter.Java

package eric.designpattern.adapter;

import Java.util.Arrays;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * <p>
 * Adapter - convert analog data to digit data.
 * </p>
 * 
 * @author eric
 * @date Mar 8, 2016 1:07:00 PM
 */
public class AnalogToDigitAdapter implements DigitSignal {
    public static final float DEFAULT_THRESHOLD_FLOAT_TO_BIN = 1.0f; // default threshold,
    private Logger logger = LoggerFactory.getLogger(this.getClass());

    private AnalogSignal analogSignal;
    private byte[] digitData;
    private float threshold;
    private boolean cached;

    public AnalogToDigitAdapter(AnalogSignal analogSignal) {
        this(analogSignal, DEFAULT_THRESHOLD_FLOAT_TO_BIN);
    }

    public AnalogToDigitAdapter(AnalogSignal analogSignal, float threshold) {
        this.analogSignal = analogSignal;
        this.threshold = threshold;
        this.cached = false;
    }

    @Override
    public synchronized byte[] getDigit() {
        if (!cached) {
            float[] analogData = analogSignal.getAnalog();
            int len = analogData.length;
            digitData = new byte[len];

            for (int i = 0; i < len; i++) {
                digitData[i] = floatToByte(analogData[i]);
            }
        }

        return digitData;
    }

    // not supported, should set the inner analog data instead,
    @Override
    public void setDigit(byte[] digitData) {
        throw new UnsupportedOperationException();
    }

    public synchronized void setAnalogData(float[] analogData) {
        invalidCache();
        this.analogSignal.setAnalog(analogData);
    }

    public synchronized void invalidCache() {
        cached = false;
        digitData = null;
    }

    @Override
    public void printDigit() {
        logger.info("{}", Arrays.toString(getDigit()));
    }

    // float -> byte convert,
    private byte floatToByte(float f) {
        return (byte) (f >= threshold ? 1 : 0);
    }
}

コード-テストケース

AdapterTest.Java

package eric.designpattern.adapter.test;

import Java.util.Arrays;

import junit.framework.TestCase;

import org.junit.Test;

import eric.designpattern.adapter.AnalogSignal;
import eric.designpattern.adapter.AnalogToDigitAdapter;
import eric.designpattern.adapter.BinDigitSignal;
import eric.designpattern.adapter.DigitSignal;
import eric.designpattern.adapter.FloatAnalogSignal;

public class AdapterTest extends TestCase {
    private float[] analogData = { 0.2f, 1.4f, 3.12f, 0.9f };
    private byte[] binData = { 0, 1, 1, 0 };
    private float[] analogData2 = { 1.2f, 1.4f, 0.12f, 0.9f };

    @Test
    public void testAdapter() {
        AnalogSignal analogSignal = new FloatAnalogSignal(analogData);
        analogSignal.printAnalog();

        DigitSignal digitSignal = new BinDigitSignal(binData);
        digitSignal.printDigit();

        // adapter
        AnalogToDigitAdapter adAdapter = new AnalogToDigitAdapter(analogSignal);
        adAdapter.printDigit();
        assertTrue(Arrays.equals(digitSignal.getDigit(), adAdapter.getDigit()));

        adAdapter.setAnalogData(analogData2);
        adAdapter.printDigit();
        assertFalse(Arrays.equals(digitSignal.getDigit(), adAdapter.getDigit()));
    }
}

依存-Maven経由

    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.8.2</version>
    </dependency>

    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.7.13</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>1.7.13</version>
    </dependency>
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.16</version>
    </dependency>

テスト方法

単体テストを実行するだけです。

11
Eric Wang

アダプタパターンは、2つの互換性のないインターフェイス間のブリッジとして機能します。このパターンには、2つの独立したインターフェースまたは互換性のないインターフェース間の通信を担当するアダプターと呼ばれる単一のクラスが含まれます。

実際の例としては、言語翻訳者やモバイル充電器などがあります。このYouTubeビデオの詳細:

Youtube-アダプター設計パターン:はじめに

7
babu

1つの実際の例はQt-Dbusです。

Qt-dbusには、提供されたxmlファイルからアダプターとインターフェースコードを生成するユーティリティがあります。手順は次のとおりです。

 1. Create the xml file - this xml file should have the interfaces 
that can be viewed by the qdbus-view in the system either on 
the system or session bus.

    2.With the utility - qdbusxml2cpp , you generate the interface adaptor code. 
This interface adaptor does the demarshalling of the data that is 
received from the client. After demarshalling, it invokes the 
user defined - custom methods ( we can say as adaptee).

    3. At the client side, we generate the interface from the xml file. 
This interface is invoked by the client. The interface does the 
marshalling of the data and invokes the adaptor interface. As told 
in the point number 2, the adaptor interface does the demarshalling 
and calls the adaptee - user defined methods.

Qt-Dbusの完全な例をこちらでご覧いただけます-

http://www.tune2wizard.com/linux-qt-signals-and-slots-qt-d-bus/

2

インジェクション攻撃に対する防御として使用されるアダプタパターンのPHP実装をここで見つけることができます:

http://www.php5dp.com/category/design-patterns/adapter-composition/

アダプタパターンの興味深い側面の1つは、多重継承に依存するクラスアダプタと構成に依存するオブジェクトアダプタの2つのフレーバがあることです。上記の例は、構成に依存しています。

2
Bill

似たような振る舞いを持つ異なるインターフェース(通常、似た振る舞いをするがメソッドが異なるクラスを意味する)を扱う必要がある場合、アダプター設計パターンを使用できます。たとえば、Samsung TVに接続するクラスと、Sony TVに接続するクラスがあります。メニューを開く、再生を開始する、ネットワークに接続するなどの一般的な動作を共有しますが、各ライブラリには異なる実装(異なるメソッド名とシグネチャ)があります。これらの異なるベンダー固有の実装は、UML図ではAdapteeと呼ばれます。

そのため、コードでは(UML図ではClientと呼ばれます)、各ベンダーのメソッド呼び出しをハードコードする代わりに(またはAdaptee)、汎用インターフェイス(UMLダイアグラムではTargetと呼ばれる)を作成してこれらをラップすることができます同様の動作と1つのタイプのオブジェクトのみで動作します。

Adaptersは、メソッド呼び出しを委任するTargetインターフェイスを実装しますAdapteesは、コンストラクタを介してAdaptersに渡されます。

Javaコードでこれを実現するために、複数のスマートTVインターフェイスを処理するアダプターを使用して、上記とまったく同じ例を使用して非常に単純なプロジェクトを作成しました。説明のため、実際の実装がどのようになるかを確認してください。

コードをダウンロードして、MavenプロジェクトとしてEclipse(またはお気に入りのIDE)にインポートするだけです。 org.example.Main.Javaを実行してコードを実行できます。ここで重要なことは、パターンを設計するためにクラスとインターフェースがどのように組み立てられるかを理解することです。また、パッケージcom.thirdparty.libsに偽のAdapteesを作成しました。それが役に立てば幸い!

https://github.com/Dannemann/Java-design-patterns

2
Jaabax

アダプター設計パターンの実際の例は多数あります

Mobile/Chargerアダプター、配水管レデューサー、言語トランスレーターなどの詳細については、 Javaのアダプター設計パターン をご覧ください。

enter image description here

enter image description here

0
Rupesh Agrawal

実際の例は、アプリケーション内のドキュメントのレポートです。ここにある単純なコード。

プログラミング構造に非常に役立つと思うアダプター。

class WordAdaptee implements IReport{
    public void report(String s) {
        System.out.println(s +" Word");
    }
}

class ExcellAdaptee implements IReport{
    public void report(String s) {
        System.out.println(s +" Excel");
    }
}


class ReportAdapter implements IReport{
    WordAdaptee wordAdaptee=new WordAdaptee();
    @Override
    public void report(String s) {
        wordAdaptee.report(s);
    }
}

interface IReport {
    public void report(String s);
}

public class Main {
    public static void main(String[] args) {

        //create the interface that client wants
        IReport iReport=new ReportAdapter();

        //we want to write a report both from Excel and world
        iReport.report("Trial report1 with one adaptee");  //we can directly write the report if one adaptee is avaliable 

        //assume there are N adaptees so it is like in our example
        IReport[] iReport2={new ExcellAdaptee(),new WordAdaptee()};

        //here we can use Polymorphism here  
        for (int i = 0; i < iReport2.length; i++) {
            iReport2[i].report("Trial report 2");
        }
    }
}

結果は次のようになります。

Trial report1 with one adaptee Word
Trial report 2 Excel
Trial report 2 Word
0
huseyin

Judith Bishop著の「C#3.0 Design Patterns」本によると、Appleはアダプタパターンを使用してMac OSをIntel製品で動作するように適合させました(第4章4抜粋で説明 2

0
CBU

変更できないが、使用する必要があるインターフェイスがある場合は、アダプターを使用します。あなたはオフィスの新しい人であり、白髪をあなたのルールに合わせることができないので、それを見てください-あなたは彼らに順応しなければなりません。これは、ユーザーインターフェイスが与えられたときに作業した実際のプロジェクトの実際の例です。

ファイル内のすべての行をListデータ構造に読み取り、グリッドに表示するアプリケーションがあります(基になるデータストアインターフェイスIDataStoreを呼び出しましょう)。ユーザーは、「最初のページ」、「前のページ」、「次のページ」、「最後のページ」ボタンをクリックして、これらのデータをナビゲートできます。すべてが正常に動作します。

ここで、アプリケーションは、メモリに読み込むには大きすぎる実稼働ログで使用する必要がありますが、ユーザーはそれをナビゲートする必要があります! 1つの解決策は、最初のページ、次のページ、前のページ、および最後のページを保存するキャッシュを実装することです。ユーザーが[次のページ]をクリックすると、キャッシュからページが返され、キャッシュが更新されます。最後のページをクリックすると、キャッシュから最後のページが返されます。バックグラウンドでは、すべての魔法を行うファイルストリームがあります。そうすることで、ファイル全体ではなく、メモリ内に4ページしかありません。

アダプタを使用して、ユーザーに気付かれずにこの新しいキャッシュ機能をアプリケーションに追加できます。現在のIDataStoreを拡張し、CacheDataStoreと呼びます。ロードするファイルが大きい場合、CacheDataStoreを使用します。 First、Next、Previous、Lastページのリクエストを行うと、情報はキャッシュにルーティングされます。

明日、上司はデータベーステーブルからファイルの読み取りを開始したいと考えています。 Cacheの場合と同様に、IDataStoreをSQLDataStoreに拡張し、バックグラウンドで接続を設定するだけです。 [次のページ]をクリックすると、必要なSQLクエリを生成して、データベースから次の数百行をフェッチします。

基本的に、アプリケーションの元のインターフェイスは変更されていません。従来のインターフェイスを維持しながら、最新のクールな機能を単純に機能させて動作させました。

0
Justice O.

@Justice oの例では、アダプターパターンについて明確に説明していません。彼の答えを拡張します-コンシューマコードが使用する既存のインターフェイスIDataStoreがあり、それを変更することはできません。ここで、実装したいことを行うXYZライブラリのクールな新しいクラスを使用するように求められますが、そのクラスを変更してIDataStoreを拡張することはできません。新しいクラス-ADAPTERを作成し、コンシューマコードが期待するインターフェイス、つまりIDataStoreを実装し、機能が必要なライブラリのクラスを使用することにより、ADAPTEEをADAPTERのメンバーとして使用して、目的を達成できます。

0
rahil008