Понимание шаблона адаптера
Я пытаюсь понять шаблон адаптера и его использование в реальном мире. Пройдя через различные статьи в Интернете и www.dofactory.com, я создал этот образец кода. Я просто хочу знать, правильно ли я понимаю. В приведенном ниже примере я создал объект MSDAO в классе Adapter. Позже я изменил его на 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();
}
}
Ответы
Ответ 1
Как правило, шаблон адаптера преобразует один интерфейс в другой, но он может просто обернуть поведение, чтобы изолировать ваш класс от основной реализации. В вашем случае вы используете адаптер, но вы могли бы так же легко определить объекты DAO, чтобы просто реализовать интерфейс и запрограммировать его на интерфейс. Шаблон адаптера обычно используется, если у вас нет контроля над целевым классом. Мое основное использование шаблона адаптера состояло в создании оболочек для класса framework, который не реализует интерфейс.
Скажем, я хочу высмеять класс рамки, который не реализует интерфейс (и не имеет виртуальных методов). Со многими издевательскими apis это трудно или невозможно сделать. Тогда я буду определять свой собственный интерфейс как подмножество подписи класса, на который я нацелен. Я реализую класс-оболочку, реализующий этот интерфейс, и просто делегирует вызовы в класс оболочки. Этот класс-оболочка работает как адаптер для класса framework. Мои классы используют этот адаптер вместо класса framework, но получают поведение класса framework.
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();
Ответ 2
Канонический пример внутри рамки .NET существует в классе System.Drawing.Bitmap
.
Этот битмап имеет конструктор, который позволяет загружать изображение из Stream
:
public Bitmap(
Stream stream
)
то, что вы не знаете, заключается в том, что внутри .NET класс Bitmap
является оберткой класса GDI + Bitmap
, и его конструктор, который принимает IStream
:
Bitmap(
[in] IStream *stream,
[in] BOOL useIcm
);
Итак, в мире С#, когда я звоню:
new Bitmap(stream);
он должен развернуться и вызвать:
IStream stm;
IntPtr gpBitmap;
GdipCreateBitmapFromStream(stm, out gpBitmap);
Вопрос заключается в том, как представить объект .NET Stream для метода, который ожидает интерфейс COM IStream.
Следовательно, внутренний 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)
... ...
Итак, теперь у вас есть адаптер:
![введите описание изображения здесь]()
И код выглядит примерно так:
IStream stm = new GPStream(stream); //adapter to convert Stream --> IStream
IntPtr gpBitmap;
GdipCreateBitmapFromStream(stm, out gpBitmap);
Ответ 3
Я добавил комментарии, которые, мы надеемся, помогут вам услышать об этом весь jargon адаптера /adapttee/client/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 freddieMercury = new EnglishMan(translator);
// Freddie greets the Tokyo crowd, though he doesn't know a word of Japanese
Console.WriteLine(freddieMercury.Greetings()); // "Konichiwa, hisashiburi!"
// when in France:
ITarget translator2 = new FrenchTranslator(new FrenchSpeaker());
EnglishMan brianMay = new EnglishMan(translator2);
// Brian greets the crowd in Paris, though he doesn't know a word in French
Console.WriteLine(brianMay.Greetings()); // "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!!";
}
}
}