Você está na página 1de 8

simplemente reenvía las peticiones al dele- gado.

NEXTSTEP [Add94] usa este


enfoque intensivamente para reducir el uso de subclases. Los lenguajes
estáticamente tipados, como C++, necesitan una definición de interfaz explícita
para el delegado. Podemos especificar dicha interfaz poniendo la interfaz
reducida que necesita VisualizadorDeArboles en una clase abstracta
DelegadoDeAccesoAIArbol. A continuación, podemos combinar esta interfaz con
el delegado de nuestra elección - ExploradorDeDirectorios, en este caso a través
de la herencia. Usaremos herencia simple si el ExploradorDeDirectorios no tiene
ninguna clase padre, y herencia múltiple en caso de que la tenga. Combinar
clases de esta forma es más fácil que introducir una nueva subclase
VisualizadorDeDirectorios e implementar sus operaciones individualmente.

c. Adaptadores parametrizados. La forma más común de permitir adaptadores


conectables en Smalltalk es parametrizar un adaptador con uno o más
bloques. La construcción bloque permite la adaptación sin necesidad de
subclases. Un bloque puede adaptar una petición y el adaptador puede
almacenar un bloque para cada petición individual. En nuestro ejemplo, esto
quiere decir que VisualizadorDeArboles guarda un bloque para convertir un
nodo en un NodoGráfico y otro bloque para acceder a los hijos de un nodo.

Por ejemplo, para crear VisualizadorDeArboles en una jerarquía de


directorios, escribimos:

directoryDisplay :=
(TreeDisplay on : treeRoot)
getChildrenBlock:
[mode | node getSubdirectories]
createGraphicNodeBlock:
[:node | node createGraphicNode].
Si está creando una adaptación de interfaz en una clase, este enfoque ofrece
una alternativa conveniente a la subclasificación.

CÓDIGO DE EJEMPLO
Daremos un breve esbozo de la implementación de adaptadores de clases y de
objetos para el ejemplo de la sección de Motivación, comenzando con las clases
Forma y VistaTexto.
daremos un breve esbozo de la implementación de clases y adaptadores de
objetos para el ejemplo de motivación que comienza con la VistaTexto y la forma
de las clases.
class Shape {
public:
Shape();
virtual void BoundingBox(
Point& bottomLeft, Point& topRight
) const;
virtual Manipulator* CreateManipulator() const;
};
class TextView {
public:
TextView();
void GetOrigin(Coordk x, Coord& y) const;
void GetExtent(Coordk width, Coord& height) const;
virtual bool IsEmptyO const;
};
Forma determina sus límites mediante una caja definida por sus esquinas
opuestas. VistaTexto, por el contrario, está definida por un origen, un alto y un
ancho. Forma también define una operación Crear - Manipulador para crear un
Manipulador, el cual sabe cómo mover una forma cuando ésta es manipulada
por el usuario. VistaTexto no tiene una operación equivalente. La clase Forma
Texto es un adaptador entre estas interfaces diferentes.
Un adaptador de clases usa la herencia múltiple para adaptar interfaces. La clave
de los adaptadores de clases es usar una rama de la herencia para heredar la
interfaz y otra para heredar la implementación. La forma normal de hacer esta
distinción en C++ es heredar públicamente la interfaz y privadamente la
implementación.
Usaremos este convenio para definir el adaptador FormaTexto.
class TextShape : public Shape, private TextView {
public:
TextShape();
virtual void BoundingBox(
Point & bottomLeft, Point& topRight
) const;
virtual bool IsEmpty () const;
virtual Manipulator* CreateManipulator() const;
};
La operación CajaLimitrofe convierte la interfaz de VistaTexto para que se ajuste
a la de Forma.
void TextShape::BoundingBox (
Point& bottomLeft, Point& topRight
) const {
Coord bottom, left, width, height;

GetOrigin(bottom, left);
GetExtent(width, height);

bottomLeft = Point(bottom, left);


topRight = Point(bottom + height, left + width);
}
La operación EstaVacia ilustra el reenvío directo de peticiones que es común en
las implementaciones de adaptadores:
bool TextShape::IsEmpty () const {
return TextView::IsEmpty();
}
Por último, definiremos CrearManipulador (que no es admitida por VistaTexto)
desde cero. Suponemos que ya hemos implementado una clase
ManipuladorDeTexto que permite la manipulación de una FormaTexto.
Manipulator* TextShape::CreateManipulator ( ) const {
return new TextManipulator(this);
}
El objeto adaptador usa composición de objetos para combinar clases con
diferentes interfaces. Con este enfoque, el adaptador FormaTexto mantiene un
puntero a VistaTexto.
class TextShape : public Shape {
public:
TextShape(TextView*);

virtual void BoundingBox(


Point& bottomLeft, Point& topRight
) const;
virtual bool IsEmpty() const;
virtual Manipulator* CreateManipulator() const;
private:
TextView* _text;
};
FormaTexto debe inicializar el puntero a la instancia de VistaTexto, y lo hace en
el constructor. También tiene que llamar a las operaciones de su objeto
VistaTexto cada vez que se llama a las suyas. En este ejemplo suponemos que
el cliente crea el objeto VistaTexto y lo pasa al constructor de FormaTexto:
TextShape::TextShape (TextView* t) {
_text = t;
}
void TextShape::BoundingBox (
Point& bottomLeft, Points topRight
) const {
Coord bottom, left, width, height;

_text->GetOrigin(bottom, left);
_text->GetExt ent (width, height) ;

bottomLeft = Point(bottom, left);


topRight = Point(bottom + height, left + width);
}
bool TextShape::IsEmpty ( ) const {
return _text->IsEmpty();
}
La implementación de CrearManipulador no cambia respecto de la versión del
adaptador de clases, ya que está implementado desde cero y no reutiliza nada
de la funcionalidad existente de VistaTexto.
Manipulator* TextShape::CreateManipulator () const {
return new TextManipulator(this);
}
Compárese este código con el caso del adaptador de clases. El adaptador de
objetos requiere un poco más de esfuerzo a la hora de escribirlo, pero también
es más flexible. Por ejemplo, la versión del adaptador de objetos de FormaTexto
funcionará igualmente bien con subclases de VistaTexto el cliente simplemente
pasa una instancia de una subclase de VistaTexto al constructor de FormaTexto.
USOS CONOCIDOS
El ejemplo de la sección Motivación viene de ET++Draw, una aplicación de dibujo
basada en ET++ [WGM88]. ET++Draw reutiliza las clases de ET++ para editar
texto mediante una clase adoptadora FormaTexto.
Interviews 2.6 define una clase abstracta Interactor para elementos de la interfaz
de usuario como barras de desplazamiento, botones y menús [VL88], También
define una clase abstracta Graphic para objetos gráficos estructurados, tales
como líneas, círculos, polígonos y splines. Tanto Interactor como Graphic tienen
representación visual, pero ambos tienen diferentes interfaces e
implementaciones (no comparten una clase padre común) y son por tanto
incompatibles no podemos insertar directamente un objeto gráfico estructurado
en, pongamos por caso, un cuadro de diálogo.
En vez de eso, Interviews 2.6 define un adaptador de objetos llamado
GraphicBlock, una subclase de Interactor que contiene una instancia de Graphic.
El GraphicBlock adapta la interfaz de la clase Graphic a la de Interactor. El
GraphicBlock permite que se pueda mostrar, desplazar y hacer zoom sobre una
instancia de Graphic dentro de una estructura Interactor.
Los adaptadores conectables son frecuentes en ObjectWorks\Smalltalk [Par90],
El Smalltalk estándar define una clase ValueModel para las vistas que muestran
un valor único. ValueModel define una interfaz valué, valué: para acceder al
valor. Estos son métodos abstractos. Los implementadores de la aplicación
acceden al valor con nombres más específicos del dominio, como ancho y ancho:
pero no deberían tener que crear una subclase de ValueModel para adaptar
dichos nombres específicos' del dominio a la interfaz ValueModel.
En lugar de eso, ObjectWorkskSmalltalk incluye una subclase de ValueModel
llamada PluggableAdaptor. Un objeto PluggableAdaptor adapta otros objetos a
la interfaz de ValueModel (valúa, valué:). Puede ser parametrizado con bloques
para obtener y establecer el valor deseado. PluggableAdaptor utiliza
internamente estos bloques para implementar la interfaz valué, valué:
PluggableAdaptor también permite pasar nombres directamente en el selector
(por ejemplo, ancho ancho:) por conveniencia sintáctica, convirtiendo estos
selectores en los bloques correspondientes de forma automáticamente.

Otro ejemplo de ObjectWorksVSmalltalk es la clase TableAdaptor. Un


TableAdaptor puede adaptar una secuencia de objetos a una presentación
tabular. La tabla muestra un objeto por fila. El cliente parametriza TableAdaptor
con el conjunto de mensajes que puede usar una tabla para obtener de un objeto
los valores de las columnas.
Algunas clases del AppKjt de NeXT [Add94] usan delegación de objetos para
llevar a cabo adaptación de interfaces. Un ejemplo es la clase NXBrowser que
puede mostrar listas jerárquicas de datos. NXBrowser usa un objeto delegado
para acceder a los datos y adaptarlos.
El “Matrimonio de Conveniencia" de Meyer [Mey88] es una forma de adaptador
de clases. Meyer describe cómo una clase PilaFija adapta la implementación de
una clase Array a la interfaz de una clase Pila. El resultado es una pila que
contiene un número fijo de entradas.

PATRONES RELACIONADOS
El patrón Bridge (141) tiene una estructura similar a un adaptador de objetos,
pero con un propósito diferente: está pensado para separar una interfaz de su
implementación, de manera que ambos puedan cambiar fácilmente y de forma
independiente uno del otro, mientras que un adaptador está pensado para
cambiar la interfaz de un objeto existente.
El patrón Decorator (161) decora otro objeto sin cambiar su interfaz. Un
decorador es por tanto más transparente a la aplicación que un adaptador. Como
resultado, el patrón Decorator permite la composición recursiva, lo que no es
posible con adaptadores puros.
El patrón Proxy (191) define un representante o sustituto de otro objeto sin
cambiar su interfaz.
BRIDGE Estructura de Objetos
(PUENTE)
PROPÓSITO
Desacopla una abstracción de su implementación, de modo que ambas puedan
variar de forma independiente.
TAMBIÉN CONOCIDO COMO
Handle/Body (Manej ador/Cuerpo)
MOTIVACIÓN
Cuando una abstracción puede tener varias implementaciones posibles, la forma
más habitual de dar- les cabida es mediante la herencia. Una clase abstracta
define la interfaz de la abstracción, y las sub- clases concretas la implementan
de distintas formas. Pero este enfoque no siempre es lo bastante flexible. La
herencia liga una implementación a la abstracción de forma permanente, lo que
dificulta modificar, extender y reutilizar abstracciones e implementaciones de
forma independiente.
Pensemos en la implementación de una abstracción portable Ventana en un
toolkit de interfaces de usuario. Esta abstracción debería permitimos escribir
aplicaciones que funcionen, por ejemplo, tanto en el Sistema de Ventanas X
como en Presentation Manager de IBM (PM). Mediante la herencia podríamos
definir una clase abstracta Ventana y subclases VentanaX y VentanaPM que
implementen la interfaz Ventana para las distintas plataformas. Pero este
enfoque tiene dos inconvenientes:
1. No es conveniente extender la abstracción Ventana para cubrir diferentes
tipos de ventanas o nuevas plataformas. Imaginemos una subclase
Ventanalcono, que especializa la abstracción Ventana para representar
iconos. Para admitir este tipo de ventanas en ambas plataformas
debemos implementar dos nuevas clases, VentanalconoX y
VentanalconoPM. Y lo que es peor, tendremos que definir dos clases para
cada tipo de ventana. Dar cabida a una tercera plataforma requeriría otra
nueva subclase de Ventana para cada tipo de ventana.

Você também pode gostar