2009-12-17 12 views
7

yo soy un novato en los patrones de diseño y aquí está mi preguntapregunta patrón

si tenemos una clase abstracta con pocas clases que implementan y cada una de estas clases tiene diferentes atributos.

ahora tengo otra (clase Manager) que contiene una matriz de la clase abstracta y quiero poner un método de búsqueda en ella ... ¿cómo puedo hacer que sin poner a las clases concretas?

Tengo 2 ideas:

Primera Uno: la adición de un nivel extra de interfaces que va con el código para no interactuar (es decir, en lugar de fundición a clase concreta i se echo a una interfaz.) aplicación regla ... pero de esta manera cuando agrego otra clase voy a tener que hacer una interfaz para ella y que también tendrá que editar el gerente (cliente), que tampoco parece muy bueno.

Segunda solución: que se vea un tanto extraña y aún necesita mejoras, pero su objetivo principal es hacer que el administrador o cualquier otro cliente trabaja con clase abstracta sin saber cualquier cosa sobre que se extiende o sus atributos.

la solutin es tan folows: cada nuevo elemento añadido tendrá que overide una interfaz que hace cumplir para generar una ULA completa de sus campos, por ejemplo, un objeto coche tendrán que volver un mapa hash tener la

folowing

campo: {FieldType, ValorDelCampo}

ejemplo

  • modelo: {texto, "ford"}
  • manifactureDate: {Date "12/01/89"}

y cada objeto tendrá también para poner en práctica un método llamado compareFields que tienen un mapa de hash como este y compararlo con su campo y devolver verdadero o falso.

ahora de esta manera he resuelto muchos problemas -por la interfaz gráfica de usuario que sólo tendrán que hacer un motor de visualización de este HashMap que puede mostrar cualquier artículo sin tener que saber lo que su tipo. (De nuevo la interfaz gráfica de usuario es otro cliente de la clase abstracta)

-para la búsqueda puedo conseguir un mapa hash que contiene los campos que el usuario entra en en el formulario de búsqueda y lazo en los elementos abstractos e invocar al comparar fieldmethod

todavía no cómo manejará el objeto complejo (que tiene otro objeto como sus atributos)

no sé qué tipo de patrón es esto .. es sólo una idea que lo pensaba.

EDIT: ejemplo concreto

si tengo una clase de elemento abstracto con un coche y autobús y barco que lo implementa ,, y cada una de estas clases tiene diferentes atributos .... cómo puede un gerente para la búsqueda de gestor de tráfico de ejemplo para un determinado elemento utilizando la clase abstracta sin poner al coche o autobús ... realmente lo siento por la pregunta larga

+0

¿Huh? Estoy confundido. Qué idioma estás usando? –

+1

No importa qué idioma use. Los patrones de diseño no les importa si pueden implementarse en la mayoría de los idiomas. – JonH

+0

estoy de acuerdo con jonH ,,, pero de cualquier manera podría usar java –

Respuesta

9

encapsulación

El principio OO de estados de encapsulación que se opone no debe exponer su estado al exterior. Si su objeto epxose información interna rompe la encapsulación. Lo que todavía está bien según el diseño de OO es pasar el criterio de búsqueda al objeto y dejar que decida si coinciden.

interface IVehicle{ 
    bool doesMatch(Map<String,String> searchCriterion) 
} 

Puede tener un interator en todos los vehículos y recuperar los que coincidan sin romper la encapsulación. La implementación particular de vehículos todavía se puede volver a implementar como se desee.

Visitante

De lo contrario, sugeriría nos fijamos en el patrón Visitor. La idea es atravesar todo el objeto y tener una clase extra que maneje el tratamiento para cada tipo específico. Esto también rompe la encapsulación pura (porque el objeto necesita exponer sus datos al visitante), pero es muy elegante.

class VehicleSearchVisitor 
{ 
    Map<String,String> searchCriterion; 
    void visit(Car car) {...} 
    void visit(Bike bike) { ... } 
    .... 
} 

Meta de programación

La idea del objeto que son auto-descripción es otro concepto, que se llama meta-programación. La capa de presentación luego introspect otro objeto para saber cómo manejarlos. Esto tradicionalmente se considera una técnica avanzada de OO. Podría crear anotaciones personalizadas para describir el campo de su clase, de modo que la capa de presentación pueda representar dinámicamente la etiqueta adecuada. La misma idea se usa, por ejemplo, con las anotaciones de hibernación. Meta-programación debe hacerse con cuidado de lo contrario se encuentra con otro problema.

Insteanceof

Uso insteanceof es también una forma de introspección (porque piden el objeto de su clase) y por lo general no se recomienda. No porque sea incorrecto en sí mismo, sino porque tiende a ser abusado. Siempre que sea posible, confíe en los principios tradicionales de OO. Usar instanceof abusivamente es un code smell.

En general, yo recomendaría utilizar un visitante para la búsqueda, y no use la meta-programación para la capa de presentación y en su lugar cree una página simple por tipo de vehículo.

+1

Iba a recomendar la primera respuesta, tiene más sentido para mí. De esta forma, cada vehículo decide si coincide con los criterios de búsqueda. –

4

usted parece estar describiendo lo que Steve Yegge llama al Universal design pattern.

El uso de pares clave-valor con una GUI puede funcionar en casos simples, pero es difícil que se vea bien. A menos que su jerarquía de objetos sea profunda y deba ser muy extensible, le conviene hacer una forma separada para cada clase concreta, ya que esto es menos trabajo y se verá mejor. Todavía puede reutilizar componentes de GUI compartidos, por lo que agregar una nueva clase concreta debería ser bastante simple.

6

Ok, por lo que está ampliando la clase en lugar de implementar una interfaz. Si tuviera clases Bus, Car, Truck, Train y todas implementaron IVehicle, que requería una función que devolviera un valor ordenable/de búsqueda, podría hacer referencia a todas ellas como type IVehicle y llamar a ese método en todas ellas .

ActionScript 3 Código:

package com.transportation.methods { 
    public interface IVehicle { 
    function getSpeed():Number; 
    function getOtherSortableOrSearchableValue():*; 
    } 
} 

y

public class Car extends Sprite implements IVehicle 

Usted está obligado a definir getSpeed ​​() y getOtherSortableValue() en la clase de coches, y puede referirse a ella como ya sea un automóvil o un vehículo intravertebral. Dado que todos los modos de transporte en mi ejemplo implementarán IVehicle, siempre que los mencione como vehículos IV puede llamar a esas dos funciones.

+0

este es mi segundo método (el método del mapa hash) ¿no? –

+0

Bastante, sí. – Aaron

+0

Esto realmente ignora una de las restricciones establecidas originalmente, que es que cada vehículo puede tener diferentes atributos. Entonces, un automóvil podría tener un getSpeed ​​(), pero quizás un pogo stick no lo haría.Un pogo stick podría tener un getBounceHeight(), pero esto no tiene sentido para un automóvil. La idea del mapa hash manejaría estas situaciones, pero esta respuesta particular no lo haría. –

0

No veo el idioma que está utilizando, pero ¿por qué no hacer que la clase abstracta implemente la interfaz? De esta forma, no importará la cantidad de clases concretas que tenga, siempre que todas ellas hereden de la clase abstracta, que implementa la interfaz.

Así es como la jerarquía se vería si está usando Java:

public interface IVehicle {/*your code goes here*/} 

public abstract class AbstractVehicle implements IVehicle{/*your code goes here*/} 

public class Car extends AbstractVehicle{/*your code goes here*/} 

Estos serían todos, por supuesto, ser definido en diferentes archivos.

+0

si agregué una nueva clase para bote, por ejemplo, y tiene atributos que no son coman con coche y no se pueden agregar a la interfaz del vehículo ... ¿Qué puedo hacer entonces? –

+0

@Ahmed Kotb: una forma de hacerlo sería agregar un método getAttributes() a la interfaz que devuelve un HashMap que contiene los pares clave/valor de los atributos. Las clases llamantes podrían entonces consultar el HashMap para qué atributos (claves) existen, si es necesario. Su método search() podría definirse en la clase abstracta para hacer esta comprobación antes de que ocurra la búsqueda real. – ssakl

+0

sí, lo que estaba tratando de hacer en la segunda sugerencia, pero quería saber si había algo mejor o si esto ya era un patrón de diseño que puedo leer sobre –

0

cómo puede un gerente para la búsqueda de gestor de tráfico de ejemplo para un determinado elemento utilizando la clase abstracta sin poner al coche o autobús

La forma en que lo haría que es para la clase abstracta para tener una método abstracto que devuelve un valor de ID (por ejemplo, un valor enum) que indica qué tipo concreto es el objeto, y tiene que anular cada clase concreta para devolver su valor de ID.

O trataría de usar las funciones RTTI de C++, o la función instanceof() de Python.

¿Eso ayuda, o estoy respondiendo la pregunta incorrecta?

+0

Todas esas soluciones no son muy OO-ish y rompen LSP. –

+1

sí, haré eso cuando no tenga otras opciones, ya que el uso de instancia significa que la clase manager debe conocer las clases concretas, lo que no creo que sea lo mejor en oop como martinho dijo –

2

Esto me parece como un tipo de patrón de repositorio "centralizado". No creo que sea una buena idea.

Qué recomienda usted que se hace es, en lugar de tener una forma centralizada de búsqueda generalizado, debe tener varios repositorios especializados: uno para los coches, uno para barcos, uno para aviones, etc.

Cada uno de los repositorios conoce los detalles de los objetos respectivos.A continuación, puede factorizar las operaciones comunes entre estos repositorios especializados, a una clase o interfaz base del Repositorio. Cuando no te importan los detalles, usas la interfaz de Repositorio base. Cuando te importan los detalles, utilizas el Repositorio especializado.

He aquí un ejemplo simple en Java (estoy dejando getters/setters por falta - no hacer sus campos pública):

interface Vehicle { int id; int maxSpeed; } 
class Car implements Vehicle { int doors; int id; int maxSpeed; } 
class Boat implements Vehicle { int buoyancy; int id; int maxSpeed; } 
class Plane implements Vehicle { int wingSpan; int id; int maxSpeed; } 

interface VehicleRepository<T extends Vehicle> { 
    T getFastest(); 
    T getSlowest(); 
    T getById(int id); 
} 

interface CarRepository inherits VehicleRepository<Car> { 
    List<Car> getCarsWithTwoDoors(); 
} 

interface BoatRepository inherits VehicleRepository<Boat> { 
    Boat getMostBuoyantBoat(); 
} 

interface PlaneRepository inherits VehicleRepository<Plane> { 
    List<Plane> getPlanesByWingspan(); 
} 
+0

sí, esa fue mi primera sugerencia ... al crear un nivel extra de interfaces creo que el único problema aquí cuando agregue otro elemento como cohetes, tendré que crear un repositorio de cohetes, ¿no? –

+0

Sí. Y no creo que haya nada de malo en eso, porque no tienes que cambiar las clases existentes para agregar cohetes. Si usa una cosa centralizada, tendrá que cambiarla cada vez que agregue algo más. –

2

Esto suena como un trabajo para el patrón de visitante. Tienes una colección de objetos abstractos y no sabes lo que es cada uno. Usando Visitor puedes iterar sobre todos los objetos y realizar una acción específica para cada uno. El libro GOF Patterns proporciona buenos detalles sobre el patrón Visitor, pero intentaré proporcionar un buen ejemplo de Java aquí.

public class Vehicle { 
    public void accept(VehicleVisitor visitor) { 
     visitor.visit(this); 
    } 
} 

public interface VehicleVisitor { 
    public void visit(Vehicle vehicle); 
    public void visit(Car car); 
    public void visit(Bus bus); 
    public void visit(Truck truck); 
    // Augment this interface each time you add a new subclass. 
} 

public class Car extends Vehicle { 
    public void accept(VehicleVisitor visitor) { 
     visitor.visit(this); 
    } 
} 

public class Bus extends Vehicle { 
    public void accept(VehicleVisitor visitor) { 
     visitor.visit(this); 
    } 
} 

public class Truck extends Vehicle { 
    public void accept(VehicleVisitor visitor) { 
     visitor.visit(this); 
    } 
} 

public class VehicleSearch implements VehicleVisitor { 
    protected String name; 
    public List<Vehicle> foundList = 
     new ArrayList<Vehicle>(); 
    public VehicleSearch(String name) { 
     this.name = name; 
    } 
    public void visit(Vehicle vehicle) { 
     return; 
    } 
    public void visit(Car car) { 
     if (car.getModel().contains(name)) { 
      foundList.add(car); 
     } 
    } 
    public void visit(Bus bus) { 
     if (bus.getManufacturerModel().contains(name)) { 
      foundList.add(bus); 
     } 
    } 
    public void visit(Truck truck) { 
     if (truck.getLineModel().contains(name)) { 
      foundList.add(truck); 
     } 
    } 
} 

public class Manager { 
    protected List<Vehicle> vehicleList; 
    public List<Vehicle> search(String name) { 
     VehicleSearch visitor = 
      new VehicleSearch(name); 
     for (Vehicle vehicle : vehicleList) { 
      vehicle.accept(visitor); 
     } 
     return visitor.foundList; 
    } 
} 

A primera vista, sí que podría simplemente añadir un método de búsqueda a la clase de vehículo y llamar a que para cada miembro de la lista, pero el patrón visitante le permite definir múltiples operaciones sobre la lista de vehículos de tal manera que no tiene que agregar un nuevo método a la clase de vehículo para cada uno.

Una desventaja del patrón Visitor, sin embargo, es que el visitante en sí necesita ser cambiado cuando agrega una nueva clase de objeto que se está visitando. En este ejemplo, si agrega un vehículo RocketShip al sistema, deberá agregar un método visit(RocketShip rocketShip) al visitante.

Puede mitigar este problema creando una VehicleVisitorTemplate con todos los métodos de visita anulados. Sus operaciones subclasifican la clase de plantilla y solo necesitan anular los métodos necesarios. Cuando necesite agregar un nuevo método de clase y visita, lo agregará a la interfaz y a la clase de plantilla, y luego todas las demás operaciones no necesitan actualizarse a menos que sea necesario.