c# - observer - Comprender el patrón del adaptador
proxy design pattern c# (3)
Estoy tratando de entender el patrón del Adaptador y su uso en el mundo real. Después de revisar varios artículos en internet y www.dofactory.com, creé este código de muestra. Solo quiero saber si mi entendimiento es correcto. En el siguiente ejemplo, he creado un objeto MSDAO en la clase Adapter. Más tarde lo cambié a 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();
}
}
En general, el patrón del adaptador transforma una interfaz en otra, pero simplemente puede ajustar el comportamiento para aislar su clase de la implementación subyacente. En su caso, está utilizando un adaptador, pero podría haber definido tan fácilmente los objetos DAO para simplemente implementar la interfaz y programar contra la interfaz. El patrón del adaptador generalmente se usa cuando no tiene control sobre la clase objetivo. Mi uso principal del patrón de adaptador sería crear contenedores para una clase de marco que no implementa una interfaz.
Digamos que quiero burlarme de una clase de framework que no implementa una interfaz (y no tiene métodos virtuales). Con muchas apologías burlonas esto es difícil o imposible de hacer. Lo que haré, entonces, es definir mi propia interfaz como un subconjunto de la firma de la clase a la que me dirijo. Implemento una clase de contenedor que implementa esta interfaz y simplemente delega las llamadas a la clase de marco envuelto. Esta clase de contenedor funciona como un adaptador para la clase de marco. Mis clases usan este adaptador en lugar de la clase framework, pero obtienen el comportamiento de la clase 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();
}
}
Considere también el caso en el que tiene un par de clases diferentes que tienen básicamente la misma funcionalidad, pero firmas diferentes y desea poder usarlas indistintamente. Si no puede transformarlos (o no quiere hacerlo por otros motivos), puede escribir una clase de adaptador que defina una interfaz común y traduzca entre los métodos de esa interfaz y los métodos disponibles en las clases de destino.
Clases de marco:
public class TargetA
{
public void Start() { ... }
public void End() { ... }
}
public class TargetB
{
public void Begin() { ... }
public void Terminate() { ... }
}
Un adaptador para ellos
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(); }
}
Luego se usa como:
ITargetAdapter adapter = new AdapterA( new TargetA() );
adapter.Open();
adapter.Close();
Un ejemplo canónico dentro del framework .NET existe en la clase System.Drawing.Bitmap
.
Este mapa de bits tiene un constructor que le permite cargar una imagen desde un Stream
:
public Bitmap(
Stream stream
)
lo que no sabes, es que internamente la clase .NET Bitmap
es un envoltorio alrededor de la clase GDI + Bitmap
, y su constructor que toma un IStream
:
Bitmap(
[in] IStream *stream,
[in] BOOL useIcm
);
Entonces en el mundo C #, cuando llamo:
new Bitmap(stream);
tiene que dar la vuelta y llamar:
IStream stm;
IntPtr gpBitmap;
GdipCreateBitmapFromStream(stm, out gpBitmap);
La pregunta es cómo presentar un objeto .NET Stream a un método que espera una interfaz COM IStream .
De ahí la clase interna GPStream
:
internal class GPStream : IStream
{
GPStream(Stream stream) { ... }
}
IStream
presentar una interfaz IStream
a su objeto 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)
... ...
Entonces ahora tienes un adaptador:
Y el código es algo así como:
IStream stm = new GPStream(stream); //adapter to convert Stream --> IStream
IntPtr gpBitmap;
GdipCreateBitmapFromStream(stm, out gpBitmap);
He agregado comentarios que con suerte te ayudarán a escuchar todo el tema del adaptador / adaptado / cliente / Itarget, lo cual es un poco confuso, con un ejemplo que con suerte será más intuitivo, con suerte :) (dedos cruzados).
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!!";
}
}
}