2010-04-23 8 views
6

Al lavoro, stiamo sviluppando un'applicazione PHP che verrà successivamente riprogrammata in Java. Con alcune conoscenze di base di Java, stiamo cercando di progettare tutto per essere facilmente riscritto, senza grattacapi. Un problema interessante è emerso quando abbiamo cercato di implementare composite pattern con un numero enorme di metodi in foglie.Problema di metodi a foglie multiple nel modello composito

Cosa stiamo cercando di realizzare (se non utilizzano interfacce, è solo un esempio veloce):

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(); 

Sembra praticamente composite classico, ma il problema è che avremo abbastanza molte classi di foglie e ognuno di essi potrebbe avere ~ 5 metodi (di cui pochi potrebbero essere diversi dagli altri). Una delle nostre soluzioni, che sembra essere la migliore finora e potrebbe effettivamente funzionare, utilizza __call magic method per chiamare i metodi in foglie. Sfortunatamente, non sappiamo se esiste un equivalente in Java.

Quindi la vera domanda è: c'è una soluzione migliore per questo, utilizzando il codice che potrebbe essere facilmente ricodificato in Java? O raccomandi qualche altra soluzione? Forse c'è qualche schema diverso e migliore che potrei usare qui.

Nel caso ci sia qualcosa di poco chiaro, basta chiedere e io modificherò questo post.

Edit:

problema reale è che non ogni classe foglia contiene, ad esempio, il metodo Baz. Se avessimo usato foreach semplice per chiamare Baz in ogni classe, daremmo un sacco di errori, dato che ci sono certe classi che non contengono questo metodo. La soluzione classica sarebbe quella di avere ogni singolo metodo da ogni classe foglia implementato in classe Composite, ognuno con un'implementazione diversa. Ma questo renderebbe la nostra classe composita enorme e caotica con la quantità di metodi che usiamo.

Così solita soluzione sarebbe simile a questa (classe Composite):

class Composite implements Fooable, Bazable { 
    ... 

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

    public function Baz() { 
     ... 
    } 
} 

Per evitare che il nostro codice per diventare vero e proprio disastro, abbiamo pensato a qualcosa di simile:

class Composite { 
    ... 

    public function __call() { 
     // implementation 
    } 
} 

Ma noi aren' Sono davvero sicuro che sia una buona soluzione e se c'è qualcosa di simile anche in Java (come richiesto già prima della modifica).

+0

Si prega di descrivere meglio il problema. Il pattern composito serve per interagire con più oggetti come se fossero uno o uno stesso, indipendentemente dalla struttura che compongono. Cosa vuoi ottenere usando il pattern composito? –

+0

Sarei davvero interessato a capire perché non lo stai facendo in Java dall'inizio. –

+0

@Gabriel: Modificato. Spero che sia d'aiuto. @Ionut: Burocrazia :) –

risposta

2

In Java si potrebbe considerare l'uso del modello visitor cui si passa un oggetto visitatore di ogni nodo nell'albero e il nodo fa una callback alla classe visitatore per determinare quale comportamento dovrebbe essere eseguita.

Ciò evita qualsiasi trasmissione o verifica esplicita del tipo di ciascun 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 per una soluzione di design, non un hack riflesso :) –

2

Il modello di progettazione del visitatore è una soluzione abbastanza buona. Ma devi considerare possibili cambiamenti nella struttura, ad es. la nuova classe Leaf ti consentirà di implementare applyVisitor e aggiungere il metodo visit * a ogni altro visitatore che hai creato. Quindi, Visitor ti aiuta davvero ad aggiungere un comportamento agli oggetti strutturati al prezzo di quella struttura che non cambia troppo spesso. Se la struttura cambia spesso e gli algoritmi non così tanto, potresti considerare di avere compositi diversi per oggetti con le stesse interfacce. Se vuoi farlo nel modo sporco come fai attualmente in PHP, guarda l'API di riflessione Java.La soluzione ideale sarebbe chiamata imho dynamic (come in Ruby o Python). Puoi simularli, ma sarebbe molto lavoro ... Quindi la mia risposta è usare il visitatore con cura o considerare diversi materiali compositi per oggetti con un comportamento diverso.