web-dev-qa-db-ja.com

アダプターパターンを理解する

アダプターパターンとその実世界での使用法を理解しようとしています。インターネットやwww.dofactory.comでさまざまな記事を読んだ後、このサンプルコードを作成しました。私の理解が正しいかどうか知りたいだけです。以下の例では、AdapterクラスにMSDAOオブジェクトを作成しました。後でそれをOracleDAOに変更しました。

class Client
{
  static void Main(string[] args)
  {
    ITarget objAdapter = new Adapter();
    object dummyObject = objAdapter.GetData();
  }
}

Interface ITarget
{
  public void GetData();
}

//Decision to use MSDAO

class Adapter : ITarget
{
  public void GetData()
  {
    MSDAO objmsdao = new MSDAO();
    objmsdao.GetData();
  }
}

//After a month, the decision to use OracaleDAO was taken, so the code change

class Adapter : ITarget
{
  public void GetData()
  {
    OracleDAO objoracledao = new OracleDAO();
    objoracledao.GetData();
  }
}
28
pradeeptp

通常、アダプタパターンは、あるインターフェイスを別のインターフェイスに変換しますが、動作をラップするだけで、クラスを基盤となる実装から分離できます。あなたの場合、アダプタを使用していますが、DAOオブジェクトを簡単に定義して、インターフェイスを実装し、インターフェイスに対してプログラムすることもできます。アダプターパターンは通常、ターゲットクラスを制御できない場合に使用されます。アダプターパターンの主な用途は、インターフェイスを実装しないフレームワーククラスのラッパーを作成することです。

インターフェイスを実装していない(仮想メソッドを持たない)フレームワーククラスをモックアウトしたいとします。多くのモックAPIを使用すると、これを行うのは困難または不可能です。次に、自分のインターフェイスを、ターゲットにしているクラスのシグネチャのサブセットとして定義します。このインターフェイスを実装するラッパークラスを実装し、ラップされたフレームワーククラスに呼び出しを委任するだけです。このラッパークラスは、フレームワーククラスのアダプターとして機能します。私のクラスはフレームワーククラスの代わりにこのアダプターを使用しますが、フレームワーククラスの動作を取得します。

 public interface IFoo
 {
     void Bar();
 }

 public class FooWrapper : IFoo
 {
      private FrameworkFoo Foo { get; set; }

      public FooWrapper( FrameworkFoo foo )
      {
           this.Foo = foo;
      }

      public void Bar()
      {
           this.Foo.Bar();
      }
 }

基本的に同じ機能を持つが署名が異なるいくつかの異なるクラスがあり、それらを交換可能に使用できるようにしたい場合も考えてみてください。これらを変換できない場合(または他の理由で変換したくない場合)、共通のインターフェイスを定義し、そのインターフェイスのメソッドとターゲットクラスで使用可能なメソッドの間で変換するアダプタクラスを作成することをお勧めします。

フレームワーククラス:

public class TargetA
{
    public void Start() { ... }
    public void End() { ... }
}

public class TargetB
{
    public void Begin() { ... }
    public void Terminate() { ... }
}

それらのためのアダプター

public interface ITargetAdapter
{
    void Open();
    void Close();
}

public class AdapterA : ITargetAdapter
{
     private TargetA A { get; set; }

     public AdapterA( TargetA a )
     {
           this.A = a;
     }

     public void Open() { this.A.Start(); }
     public void Close() { this.A.End(); }
}

public class AdapterB : ITargetAdapter
{
     private TargetB B { get; set; }

     public AdapterB( TargetB a )
     {
           this.B = a;
     }

     public void Open() { this.B.Begin(); }
     public void Close() { this.B.Terminate(); }
}

次に、次のように使用されます。

ITargetAdapter adapter = new AdapterA( new TargetA() );
adapter.Open();
adapter.Close();     
78
tvanfosson

.NET Framework内の標準的な例は、 System.Drawing.Bitmap クラス。

このビットマップには、 Stream から画像をロードできるコンストラクタがあります。

public Bitmap(
    Stream stream
)

わからないのは、内部的に.NET BitmapクラスがGDI + Bitmap クラスのラッパーであり、そのコンストラクターが IStream

Bitmap(
  [in]  IStream *stream,
  [in]  BOOL useIcm
);

したがって、C#の世界では、次のように呼び出します。

new Bitmap(stream);

振り返って電話する必要があります:

IStream stm;
IntPtr gpBitmap;
GdipCreateBitmapFromStream(stm, out gpBitmap);

問題は、COMIStreamインターフェイスを期待するメソッドに.NETStreamオブジェクトをどのように提示するかです。

したがって、内部GPStreamクラス:

internal class GPStream : IStream
{
   GPStream(Stream stream) { ... }
}

IStreamオブジェクトへのStreamインターフェースを提示する必要があります。

IStream                                     Stream
=======================================     =====================================
int Read(IntPtr buf, int len);          --> int Read(byte[] buffer, int offset, int count)
int Write(IntPtr buf, int len);         --> void Write(byte[] buffer, int offset, int count);
long Seek(long dlibMove, int dwOrigin); --> long Seek(long offset, SeekOrigin orgin)
...                                         ...

これで、アダプターができました。

enter image description here

そして、コードは次のようなものです。

IStream stm = new GPStream(stream); //adapter to convert Stream --> IStream
IntPtr gpBitmap;

GdipCreateBitmapFromStream(stm, out gpBitmap);
3
Ian Boyd

うまくいけば、アダプター/アダプター/クライアント/ Itargetの専門用語全体に頭を悩ませるのに役立つコメントを追加しました-これは少し混乱します:

internal class Program
{
    private static void Main(string[] args)
    {
        // Brian and freddie know only how to say Greetings. But when they tour
        // internationally, they will need a translator so when they say Greetings()
        // the appropriate non-English response comes out of their mouth.
        // they need to make use of the adapter pattern:

        // When in Japan:
        ITarget translator = new JapaneseTranslator(new JapaneseSpeaker());
        EnglishMan freddie = new EnglishMan(translator);

        // Freddie greets Tokyo, though he doesn't know a Word of Japanese
        Console.WriteLine(freddie.Greetings()); //  "teo torriatte!"

        // when in France:
        ITarget translator2 = new FrenchTranslator(new FrenchSpeaker());
        EnglishMan brian = new EnglishMan(translator2);

        // Brian greets the crowd in Paris, though he doesn't know a Word in French
        Console.WriteLine(brian.Greetings()); 
          // "So très charmant my dear! Bonjour"

        // alternatively, the translators can also do the greeting:
        Console.WriteLine(translator.Greetings());  //  "Konichiwa, hisashiburi!"
        Console.WriteLine(translator2.Greetings()); // "Bonjour!"
    }

    /// <summary>
    /// This is the client.
    /// </summary>
    public class EnglishMan : ITarget
    {
        private ITarget target;

        public EnglishMan(ITarget target)
        {
            this.target = target;
        }

        public string Greetings()
        {
            return target.Greetings();
        }
    }

    /// <summary>
    /// The target interface
    /// </summary>
    public interface ITarget
    {
        string Greetings();
    }

    /// <summary>
    /// This is the adaptor
    /// </summary>
    public class JapaneseTranslator : ITarget
    {
        private JapaneseSpeaker japanese;

        public JapaneseTranslator(JapaneseSpeaker japanese)
        {
            this.japanese = japanese;
        }

        public string Greetings()
        {
            return japanese.Konnichiwa();
        }
    }

    /// <summary>
    /// This is the adaptee
    /// </summary>
    public class JapaneseSpeaker
    {
        public JapaneseSpeaker()
        {
        }

        public string Konnichiwa()
        {
            return "Konichiwa, hisashiburi!";
        }
    }

    /// <summary>
    /// This is the adaptor
    /// </summary>
    public class FrenchTranslator : ITarget
    {
        private FrenchSpeaker french;

        public FrenchTranslator(FrenchSpeaker french)
        {
            this.french = french;
        }

        public string Greetings()
        {
            return french.Bonjour();
        }
    }

    /// <summary>
    /// This is the adaptee
    /// </summary>
    public class FrenchSpeaker
    {
        public string Bonjour()
        {
            return "Bonjour!!";
        }
    }
}
3
BKSpurgeon