Factory method - U

Anuncio
Factory method


(Gamma et al.)
Define una interfaz para crear un objeto pero deja a las
subclases decidir que clase instanciar
Motivación: Consideremos un framework que presenta múltiples
documentos al usuario. Aquí aparecen dos abstracciones claves: Aplicación
y Documento. Ambas clases son abstractas y los clientes deben crear las
subclases respectivas para realizar sus implementaciones dependientes de
la aplicación. Dado que la subclase particular de Documento a instanciar es
dependiente de la aplicación, la clase Aplicación no puede predecir qué
subclase Documento instanciar: “la clase Aplicación sólo sabe cuando un
nuevo documento debe ser instanciado pero no cuál”. FactoryMethod provee
una solución: encapsula el conocimiento de qué subclase de Documento
crear y lo mueve fuera del framework.
Metodologías de diseño y programación
Factory method
Ejemplo:
Las subclases de Aplicación redefinen CreateDocument para retornar la
subclase de Documento apropiada
Metodologías de diseño y programación
Factory method
Estructura:
Aplicabilidad:
-Una clase no puede anticipar el tipo de objetos que debe crear
-Una clase quiere que sus subclases especifiquen el objeto a crear
Metodologías de diseño y programación
Factory method
Otros usos: Conecta jerarquías paralelas
Metodologías de diseño y programación
Factory method
Implementaciones:
- Variaciones: (a) La clase Creator es una clase abstracta y no provee
una implementación del factory method que declara, (b) La clase Creator es una clase concreta
y provee una implementación default.
- Factory methods parametrizados: Un factory method puede crear
múltiples productos si recibe como parámetro un valor que identifica el tipo
de objeto a crear.
class Creator{
public:
virtual Product* create(ProductId); }
Product* Creator::create(ProductId id){
if( id == ID_1){ return ID_1Product;}
if( id == ID_2){ return ID_2Product;}
...
return 0;
}
Metodologías de diseño y programación
Factory method
Redefiniendo un factory method parametrizado nos permite extender fácilmente la
creación a incorporar nuevos productos o cambiar los productos que el Creador genera.
class MyCreator: public Creator{
public: virtual Product* create(ProductId);}
Product* MyCreator::create(ProductId id){
if( id == ID_1){ return ID_2Product;}
if( id == ID_2){ return ID_1Product;}
....
if( id == ID_N){ return ID_NProduct;}
return Creator::create(id);
}
Metodologías de diseño y programación
Iterator(continuación)
AbstractList provee una interfaz común para manipular listas. Un iterador abstracto define una
una interfaz de iteración común. El mecanismo de iteración es independiente de las clases
“agregadas”concretas.
Si se desea escribir código independiente de las subclases concretas, es conveniente tener un
método createIterator() para solicitar el iterador a usar. Qué tipo de método es éste?
Metodologías de diseño y programación
Iterator( cont.)
Estructura:
Metodologías de diseño y programación
Iterator(cont.)
Implementaciones:
- Quién controla la iteración? Si el cliente la controla se habla de iteradores externos y si
el mimo iterador la controla de conocen como iteradores internos. Cuáles son más
flexibles? Los externos.
- Iteradores internos: cómo parametrizar el iterador con la operación
que aplicar a cada elemento?
template<class Item>
class ListTraverser{
public:
ListTraverser(List<Item*> aList);
bool Traverse();
protected:
virtual bool processItem(const Item);
private:
ListIterator<Item> _iterator;
}
Metodologías de diseño y programación
Iterator(cont.)
template <class Item>
ListTraverser<Item>::ListTraverser(
List<Item>* aList):_iterator(aList){}
template <class Item>
bool ListTraverser<Item>::Traverse(){
bool result = false;
for( iterator.First(); !iterator.IsDone(); _iterator.Next()){
result = ProcessItem(_iterator.CurrentItem() );
if( result == false ) break;
}
return result;
}
Nota: Internamente usa un iterador externo para su recorrido.
Metodologías de diseño y programación
Iterator (cont.)
Ejemplo: Usarlo para imprimir los primeros diez empleados de una listade empleados.
class PrintNEmployees: public ListTraverser<Employee*>{
public:
PrintNEmployees(List<Employee*>* aList, int n):
ListTraverser<Employee*>(aList), _total(n), _count(0){}
protected:
bool ProcessItem(const Employee*);
private:
int _total;
int _count; };
bool PrintNEmployees::ProcessItem(const Employee* e){
_count++;
e->Print();
return _count < _total; }
Metodologías de diseño y programación
Iterator (cont.)
Uso:
List<Employess*>* employees;
// ....
PrintNEmployees pa(employees,10);
pa.traverse();
El beneficio es que la lógica entera de la iteración puede ser reusada.
Nota: Los iteradores internos pueden ser extendidos para procesar
sólo los elementos si satisfacen un cierto test. Para esto junto al
método para procesar Item, debe existir uno llamado TestItem(...).
Metodologías de diseño y programación
Composite
Modela objetos en estructuras de árbol para representar
jerarquías parte-todo.
- Los clientes deben ser capaces de ignorar la diferencia
entre objetos compuestos y objetos individuales.
Estructura:
Metodologías de diseño y programación
Composite
Implementación: Notar que este patrón incluye una contradicción.
- El patrón hace que los clientes se olviden de las diferencias entre
clases primitivas y clases compuestas. Para esto todas tienen la
misma interfaz. Problema? Este diseño va en contra del principio que dice
que cada clase debe tener las operaciones que tienen significado para ella.
En este caso, getChild, add, remove solo tienen sentido para las subclases
de Composite.
- Qué implementación default podemos dar para cada una?
+ getChild: Si definimos que una hoja(Leaf) como una componente
sin hijos, podemos definir una operación default de acceso a los hijos
que nunca retorna hijos.
+ add, remove? No se ve de manera natural
Metodologías de diseño y programación
Composite
- Veamos ventajas/desventajas del diseño en general:
+ definir estas operaciones en la raiz de la jerarquía (Component)
da transparencia pues todo se maneja de manera uniforme, pero es
menos segura pues los clientes pueden querer hacer add y remove de
algo que no tiene sentido.
+ definirlas sólo en la clase Composite es más seguro pero se pierde
uniformidad.
+ Conflicto entre transparencia y seguridad. Si optamos por seguridad
en algún momento hay que usar cast: (Composite) componente. Esto
tambien puede llevar a errores por eso este patrón decidió enfatizar
transparencia.
Recomendación:
optar por seguridad agregando un método
getComposite() que retorna null si es hoja y retorna this si es compuesto.
El cliente entonces agrega una nueva componente con add solo si
corresponde.
Metodologías de diseño y programación
Composite
class Component{
public:
// ...
virtual Composite* getComposite(){return 0;}
};
class Composite: public Component{
public:
void add(Component*);
// ...
virtual Composite* getComposite(){ return this;}
};
class Leaf: public Component{
// ...
}
Metodologías de diseño y programación
Composite
Uso:
Composite* aComposite = new Composite();
Leaf* aLeaf = new Leaf();
Component* acomponent;
Composite* test;
aComponent = aComposite;
if( test = aComponent->getComposite() ){
test->add(new Leaf());
}
aComponent = aLeaf;
if( test = aComponent->getComposite() ){
test->add(new Leaf()); // No se agrega
}
Metodologías de diseño y programación
Descargar