2010-04-23 10 views
6

En el trabajo, estamos desarrollando una aplicación PHP que luego se volvería a programar en Java. Con algunos conocimientos básicos de Java, estamos tratando de diseñar todo para ser fácilmente reescrito, sin ningún tipo de dolores de cabeza. Surgió un problema interesante cuando intentamos implementar composite pattern con una gran cantidad de métodos en hojas.Problema de métodos de hoja múltiple en el patrón compuesto

¿Qué estamos tratando de lograr (no el uso de interfaces, es sólo un ejemplo rápido):

class Composite { 
    ... 
} 


class LeafOne { 
    public function Foo(); 

    public function Moo(); 
} 


class LeafTwo { 
    public function Bar(); 

    public function Baz(); 
} 


$c = new Composite(Array(new LeafOne(), new LeafTwo())); 

// will call method Foo in all classes in composite that contain this method 
$c->Foo(); 

// same with Bar 
$c->Bar(); 

Parece como patrón Composite clásico más o menos, pero el problema es que vamos a tener bastante muchas clases de hojas y cada uno de ellos podría tener ~ 5 métodos (de los cuales pocos podrían ser diferentes a otros). Una de nuestras soluciones, que parece ser la mejor hasta ahora y podría funcionar, es usar __call magic method para llamar a métodos en hojas. Desafortunadamente, no sabemos si existe un equivalente en Java.

Así que la verdadera pregunta es: ¿hay una mejor solución para esto, utilizando un código que eventualmente sería reconfigurado fácilmente en Java? ¿O recomiendas otra solución? Tal vez haya un patrón diferente y mejor que podría usar aquí.

En caso de que haya algo poco claro, solo pregunte y voy a editar esta publicación.

Editar:

problema real es que no todas las clases de hoja contiene, por ejemplo, el método de Baz. Si utilizáramos foreach simple para llamar a Baz en cada clase, daría lugar a un montón de errores, ya que hay ciertas clases que no contienen este método. La solución clásica sería tener todos los métodos de cada clase de hoja implementados en clase compuesta, cada uno con una implementación diferente. Pero esto haría que nuestra clase compuesta fuera enorme y desordenada con la cantidad de métodos que usamos.

solución tan habitual sería el siguiente (clase Compuesto):

class Composite implements Fooable, Bazable { 
    ... 

    public function Foo() { 
     foreach($this->classes as $class) { 
      $class->Foo(); 
     } 
    } 

    public function Baz() { 
     ... 
    } 
} 

Para evitar que nuestro código para que sea verdadero desastre, estábamos pensando en algo como:

class Composite { 
    ... 

    public function __call() { 
     // implementation 
    } 
} 

Pero nosotros aren' t realmente seguro si es una buena solución y si hay algo similar también en Java (como se preguntó antes de editar).

+0

Por favor describa mejor el problema. El patrón compuesto es para interactuar con objetos múltiples como si fueran uno o lo mismo, independientemente de la estructura que compongan. ¿Qué quieres lograr usando un patrón compuesto? –

+0

Estaría realmente interesado en ver por qué no lo estás haciendo en Java desde el principio. –

+0

@Gabriel: Editado. Espero eso ayude. @Ionut: Burocracia :) –

Respuesta

2

Dentro de Java se podría considerar el uso del patrón de visitor mediante el cual se pasa un objeto visitante a cada nodo en el árbol y el nodo hace una devolución de llamada a la clase de visitantes para determinar qué comportamiento se debe realizar.

Esto evita cualquier conversión o comprobación explícita del tipo de cada nodo.

/** 
* Visitor capable of visiting each node within a document. 
* The visitor contains a callback method for each node type 
* within the document. 
*/ 
public interface DocumentNodeVisitor { 
    void visitWord(Word word); 
    void visitImage(Image img); 
} 

/** 
* Base interface for each node in a document. 
*/ 
public interface DocumentNode { 
    void applyVisitor(DocumentVisitor v); 
} 

/** 
* Conrete node implementation representing a word. 
*/  
public class Word implements DocumentNode { 
    private final String s; 

    public Word(String s) { this.s = s; } 

    public String getValue() { return this.s; } 

    public void applyVisitor(DocumentVisitor v) { 
    // Make appropriate callback to visitor. 
    v.visitWord(this); 
    } 
} 

/** 
* Conrete node implementation representing an image. 
*/   
public class Image implements DocumentNode { 
    public void applyVisitor(DocumentVisitor v) { 
    // Make appropriate callback to visitor. 
    v.visitImage(this); 
    } 
} 

public class Paragraph implements DocumentNode { 
    private final List<DocumentNode> children; 

    public Paragraph() { 
    this.children = new LinkedList<DocumentNode>(); 
    } 

    public void addChild(DocumentNode child) { 
    // Technically a Paragraph should not contain other Paragraphs but 
    // we allow it for this simple example. 
    this.children.add(child); 
    } 

    // Unlike leaf nodes a Paragraph doesn't callback to 
    // the visitor but rather passes the visitor to each 
    // child node. 
    public void applyVisitor(DocumentVisitor v) { 
    for (DocumentNode child : children) { 
     child.applyVisitor(v); 
    } 
    } 
}  

/** 
* Concrete DocumentVisitor responsible for spell-checking. 
*/ 
public class SpellChecker implements DocumentVisitor 
    public void visitImage(Image i) { 
    // Do nothing, as obviously we can't spellcheck an image. 
    } 

    public void visitWord(Word word) { 
    if (!dictionary.contains(word.getValue()) { 
     // TODO: Raise warning. 
    } 
    } 
} 
+0

+1 por una solución de diseño, no es un hack de reflexión :) –

2

Visitor design pattern es una solución bastante buena. Pero debe considerar posibles cambios en la estructura, p. La nueva clase Leaf le permitirá implementar applyVisitor y agregar el método de visita * a todos los demás visitantes que haya creado. Entonces Visitor realmente lo ayuda a agregar comportamiento a los objetos estructurados al precio de esa estructura que no cambia con demasiada frecuencia. Si la estructura cambia a menudo y los algoritmos no tanto, puede considerar tener diferentes materiales compuestos para objetos con las mismas interfaces. Si desea hacerlo de la manera sucia como lo hace actualmente en PHP, mire la API de reflexión de Java.Una buena solución serían las llamadas dinámicas imho (como en Ruby o Python). Puedes simular eso, pero eso sería mucho trabajo ... Entonces mi respuesta es usar el Visitante con cuidado o considerar Composites diferentes para objetos con diferente comportamiento.

Cuestiones relacionadas