私のチームにアダプタパターンの使用法を示したいと思います。私はオンラインでたくさんの本や記事を読みました。誰もが概念を理解するのに役立つ例(形状、メモリーカード、電子アダプターなど)を引用していますが、実際のケーススタディはありません。
アダプタパターンのケーススタディを教えてください。
ps stackoverflowで既存の質問を検索しようとしましたが、答えが見つからなかったため、新しい質問として投稿しました。これに対する答えがすでにあることがわかっている場合は、リダイレクトしてください。
私のチームにアダプタパターンの使用法を示したいと思います。私はオンラインでたくさんの本や記事を読みました。誰もが概念を理解するのに役立つ例(形状、メモリーカード、電子アダプターなど)を引用していますが、実際のケーススタディはありません。
アダプタパターンのケーススタディを教えてください。
ps stackoverflowで既存の質問を検索しようとしましたが、答えが見つからなかったため、新しい質問として投稿しました。これに対する答えがすでにあることがわかっている場合は、リダイレクトしてください。
回答:
アダプターの多くの例は、取るに足らないまたは非現実的です(Rectangle vs. LegacyRectangle、Ratchet vs. Socket、SquarePeg vs RoundPeg、Duck vs. Turkey)。さらに悪いことに、多くの場合、異なるアダプタの複数のアダプタが表示されません(アダプタパターンの例としてJavaのArrays.asListを引用した人がいます)。1つのクラスのみのインターフェースを別のクラスと連携するように適合させることは、GoFアダプターパターンの弱い例のようです。このパターンは継承とポリモーフィズムを使用するため、さまざまなアダプターのアダプターの複数の実装を示す良い例が期待されます。
最良の例私が見つけたは、の第26章にあるオブジェクト指向分析設計と反復的な開発(第3版)に入門:UMLとパターンを適用します。次の画像は、本のFTPサイトで提供されているインストラクターの資料からのものです。
1つ目は、機能的に類似しているがAPIが異なる複数の実装(アダプター)をアプリケーションがどのように使用できるかを示しています(たとえば、税計算機、会計モジュール、クレジット認証サービスなど)。ドメインレイヤーコードをハードコーディングして、税金の計算、販売後の販売、クレジットカードリクエストの承認など、さまざまな方法を処理することは避けたいと考えています。これらはすべて外部モジュールであり、変更することはできません。コード。アダプターを使用すると、アダプターでハードコーディングを行うことができますが、ドメインレイヤーコードは常に同じインターフェイス(IWhateverAdapterインターフェイス)を使用します。

上の図には、実際の適応者は表示されていません。ただし、次の図はpostSale(...)、IAccountingAdapterインターフェースでのポリモーフィック呼び出しがどのように行われるかを示しています。これにより、SOAPを介してSAPシステムに販売が転記されます。

フランス人を普通の人に変える方法...
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));
インターフェイスを別のインターフェイスに変換します。
アダプターパターンの実際の例
電源を接続するために、世界中にさまざまなインターフェースがあります。アダプターを使用すると、賢明なように簡単に接続できます。

これは、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()));
}
}
<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>
ユニットテストを実行するだけです。
アダプタパターンは、互換性のない2つのインターフェイス間のブリッジとして機能します。このパターンには、2つの独立したインターフェースまたは互換性のないインターフェース間の通信を担当するアダプターと呼ばれる単一のクラスが含まれます。
実際の例としては、言語翻訳者やモバイル充電器などがあります。このyoutubeビデオの詳細:
動作が似ているさまざまなインターフェイスを処理する必要がある場合は、アダプタデザインパターンを使用できます(通常、動作は似ているがメソッドが異なるクラスを意味します)。その一例は、Samsung TVに接続するクラスと、SonyTVに接続するクラスです。これらは、メニューを開く、再生を開始する、ネットワークに接続するなどの共通の動作を共有しますが、ライブラリごとに異なる実装があります(メソッド名と署名が異なります)。これらのさまざまなベンダー固有の実装は、UML図ではAdapteeと呼ばれます。
したがって、コード(UML図ではクライアントと呼ばれます)では、各ベンダー(またはAdaptee)のメソッド呼び出しをハードコードする代わりに、これらの同様の動作をラップして機能する汎用インターフェイス(UML図ではターゲットと呼ばれます)を作成できます。オブジェクトのタイプは1つだけです。
アダプタは、次に実装するターゲットをにそのメソッド呼び出しを委任するインタフェースAdapteesに渡されるアダプタコンストラクタを介して。
これをJavaコードで実現するために、アダプターを使用して複数のスマートTVインターフェースを処理する上記とまったく同じ例を使用して、非常に単純なプロジェクトを作成しました。コードは小さく、十分に文書化されており、自明なので、実際の実装がどのように見えるかを調べてください。
コードをダウンロードして、MavenプロジェクトとしてEclipse(またはお気に入りのIDE)にインポートするだけです。org.example.Main.javaを実行してコードを実行できます。ここで重要なことは、クラスとインターフェイスを組み合わせてパターンを設計する方法を理解することです。また、com.thirdparty.libsパッケージに偽のAdapteeをいくつか作成しました。それが役に立てば幸い!
実際の例の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/
インジェクション攻撃に対する防御として使用されるアダプタパターンのPHP実装は、次の場所にあります。
http://www.php5dp.com/category/design-patterns/adapter-composition/
アダプタパターンの興味深い側面の1つは、多重継承に依存するクラスアダプタと構成に依存するオブジェクトアダプタの2つの種類があることです。上記の例は、構成に依存しています。
アダプタデザインパターンは、1つのクラスのインターフェイスをクライアントが期待するインターフェイスに変換するのに役立ちます。
例: 都市名を入力値として渡すことにより、天気(摂氏)を返すサービスがあります。ここで、クライアントが郵便番号を入力として渡し、その見返りに都市の気温を期待していると仮定します。ここでは、これを実現するためのアダプターが必要です。
public interface IWetherFinder {
public double getTemperature(String cityName);
}
class WeatherFinder implements IWetherFinder{
@Override
public double getTemperature(String cityName){
return 40;
}
}
interface IWeatherFinderClient
{
public double getTemperature(String zipcode);
}
public class WeatherAdapter implements IWeatherFinderClient {
@Override
public double getTemperature(String zipcode) {
//method to get cityname by zipcode
String cityName = getCityName(zipcode);
//invoke actual service
IWetherFinder wetherFinder = new WeatherFinder();
return wetherFinder.getTemperature(cityName);
}
private String getCityName(String zipCode) {
return "Banaglore";
}
}
実際の例は、アプリケーション内のドキュメントのレポートです。ここにあるような単純なコード。
アダプターはプログラミング構造に非常に役立つと思います。
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
変更できないが使用する必要のあるインターフェースがある場合は、アダプターを使用します。あなたがオフィスの新しい男であり、白髪をあなたの規則に従わせることができないので、それを見てください-あなたは彼らの規則に適応しなければなりません。これは、ユーザーインターフェイスが指定されているときに私が取り組んだ実際のプロジェクトの実際の例です。
ファイル内のすべての行をリストデータ構造に読み込み、グリッドに表示するアプリケーションがあります(基になるデータストアインターフェイスをIDataStoreと呼びましょう)。ユーザーは、「最初のページ」、「前のページ」、「次のページ」、「最後のページ」のボタンをクリックして、これらのデータをナビゲートできます。すべてが正常に動作します。
ここで、アプリケーションは、メモリに読み込むには大きすぎる本番ログで使用する必要がありますが、ユーザーはそれをナビゲートする必要があります。1つの解決策は、最初のページ、次のページ、前のページ、最後のページを格納するキャッシュを実装することです。ユーザーが[次のページ]をクリックすると、キャッシュからページが返され、キャッシュが更新されます。最後のページをクリックすると、キャッシュから最後のページが返されます。バックグラウンドでは、すべての魔法を実行するファイルストリームがあります。そうすることで、ファイル全体ではなく、メモリに4ページしかありません。
アダプターを使用して、ユーザーが気付かないうちにこの新しいキャッシュ機能をアプリケーションに追加できます。現在のIDataStoreを拡張し、それをCacheDataStoreと呼びます。ロードするファイルが大きい場合は、CacheDataStoreを使用します。First、Next、Previous、Lastページをリクエストすると、情報はキャッシュにルーティングされます。
そして、誰が知っているか、明日、上司はデータベーステーブルからファイルを読み始めたいと思っています。キャッシュの場合と同じように、IDataStoreをSQLDataStoreに拡張し、バックグラウンドで接続をセットアップするだけです。[次のページ]をクリックすると、データベースから次の数百行をフェッチするために必要なSQLクエリが生成されます。
基本的に、アプリケーションの元のインターフェイスは変更されていません。従来のインターフェースを維持しながら、モダンでクールな機能を採用して機能させました。
@Justice oの例では、アダプターパターンについて明確に説明していません。彼の答えを拡張する-コンシューマーコードが使用する既存のインターフェイスIDataStoreがあり、変更することはできません。ここで、実装したいことを実行するXYZライブラリのクールな新しいクラスを使用するように求められますが、そのクラスを変更してIDataStoreを拡張することはできません。すでに問題が発生していますか?コンシューマーコードが期待するインターフェイス(IDataStore)を実装する新しいクラス(ADAPTER)を作成し、必要な機能を備えたライブラリのクラスを使用することで(ADAPTEE)、ADAPTERのメンバーとして目的を達成できます。
Judith Bishopによる「C#3.0 Design Patterns」の本によると、Appleはアダプタパターンを使用してMac OSをIntel製品で動作するように適合させました(第4章で説明、ここから抜粋2)
Yiiフレームワークの例は次のとおりです。YiiはインターフェイスICacheを利用して内部キャッシュを使用します。 https://www.yiiframework.com/doc/api/1.1/ICache
その署名は次のようなものです:-
abstract public boolean set(string $id, mixed $value, integer $expire=0, ICacheDependency $dependency=NULL)
abstract public mixed get(string $id)
たとえば、Yiiサービスコンポーネント(サービスロケーター)構成https:/でこのサービスを定義することにより、Yiiプロジェクト内でsymfonyキャッシュライブラリhttps://packagist.org/packages/symfony/cacheとそのキャッシュインターフェイス を使用したいとし ます。 /github.com/symfony/cache-contracts/blob/master/CacheInterface.php
public function get(string $key, callable $callback, float $beta = null, array &$metadata = null);
Symfonyは2番目の呼び出し可能パラメーターを提供するときにgetメソッドをセッターとしても使用するため、symfonyキャッシュにはgetメソッドのみのインターフェイスがあり、setメソッドとgetメソッドの異なる署名がありません。
Yiiコアは内部でこのYiiキャッシュ/インターフェースを使用するため、場所で不可能ではないにしても、そのインターフェースへの呼び出しを書き換えることは困難です(Yii / YiiBaseを拡張します)。
さらに、Symfonyキャッシュは私たちのクラスでもないので、Yiiキャッシュインターフェイスに合うようにインターフェイスを書き直すことはできません。
それで、ここに救助するアダプターパターンが来ます。マッピング= Yiiキャッシュインターフェース呼び出しをSymfonyキャッシュインターフェースにマッピングする中間アダプターを記述します
このようになります
class YiiToSymfonyCacheAdapter implements \Yii\system\caching\ICache
{
private \Symfony\Contracts\Cache\CacheInterface $symfonyCache;
public function __construct(\Symfony\Contracts\Cache\CacheInterface $symfonyCache)
{
$this->symfonyCache = $symfonyCache;
}
public boolean set(string $id, mixed $value, integer $expire=0, ICacheDependency
$dependency=NULL)
{
// https://symfony.com/doc/current/cache.html
return $this->symfonyCache->get(
$id,
function($item) {
// some logic ..
return $value;
}
);
// https://github.com/symfony/cache/blob/master/Adapter/MemcachedAdapter.php
// if a class could be called statically, the adapter could call statically also eg. like this
// return \Symfony\Component\Cache\Adapter\MemcacheAdapter::get(
// $id,
// function($item) {
// // some logic ..
// return $value;
// }
);
}
public mixed get(string $id)
{
// https://github.com/symfony/cache/blob/master/Adapter/FilesystemAdapter.php
// if a class could be called statically, the adapter could call statically also eg. like this
// \Symfony\Component\Cache\Adapter\FileSystemAdapter::get($id)
return $this->symfonyCache->get($id)
}
}
これは、アダプターの実装例です。
interface NokiaInterface {
chargementNokia(x:boolean):void
}
class SamsungAdapter implements NokiaInterface {
//nokia chargement adapted to samsung
chargementNokia(x:boolean){
const old= new SamsungCharger();
let y:number = x ? 20 : 1;
old.charge(y);
}
}
class SamsungCharger {
charge(x:number){
console.log("chrgement x ==>", x);
}
}
function main() {
//charge samsung with nokia charger
const adapter = new SamsungAdapter();
adapter.chargementNokia(true);
}