2010-04-23 4 views
6

Bei der Arbeit entwickeln wir eine PHP-Anwendung, die später in Java neu programmiert werden würde. Mit einigen Grundkenntnissen von Java versuchen wir, alles so zu gestalten, dass es ohne Probleme neu geschrieben werden kann. Ein interessantes Problem trat auf, als wir versuchten, composite pattern mit einer großen Anzahl von Methoden in Leafs zu implementieren.Multiple-Leaf-Methoden Problem in Composite-Muster

Was wir erreichen wollen (nicht mit Hilfe von Schnittstellen, es ist nur ein kleines Beispiel):

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

Es ist wie so ziemlich klassisches Composite-Muster scheint, aber Problem ist, dass wir sehr viele Blatt Klassen haben und jeder von ihnen könnte ~ 5 Methoden haben (von denen wenige anders als andere sein könnten). Eine unserer Lösungen, die bisher die beste zu sein scheint und möglicherweise funktioniert, verwendet __call magic method, um Methoden in Blättern aufzurufen. Leider wissen wir nicht, ob es in Java ein Äquivalent dazu gibt.

Die eigentliche Frage ist also: Gibt es eine bessere Lösung dafür, Code zu verwenden, der schließlich leicht in Java umcodiert werden könnte? Oder empfehlen Sie eine andere Lösung? Vielleicht gibt es ein anderes, besseres Muster, das ich hier verwenden könnte.

Falls etwas unklar ist, einfach fragen und ich werde diesen Beitrag bearbeiten.

Edit:

tatsächliches Problem ist, dass nicht jedes Blatt Klasse enthält zum Beispiel Verfahren Baz. Wenn wir einfach foreach verwenden würden, um Baz in jeder Klasse aufzurufen, würde es viele Fehler geben, da es bestimmte Klassen gibt, die diese Methode nicht enthalten. Klassische Lösung wäre, jede einzelne Methode aus jeder einzelnen Blattklasse in die Klasse Composite zu implementieren, jede mit einer anderen Implementierung. Aber das würde unsere zusammengesetzte Klasse enorm und chaotisch machen mit der Menge an Methoden, die wir verwenden.

So übliche Lösung würde so aussehen (Composite-Klasse):

class Composite implements Fooable, Bazable { 
    ... 

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

    public function Baz() { 
     ... 
    } 
} 

unseren Code verhindern echtes Chaos zu werden, wir denken über etwas wie:

class Composite { 
    ... 

    public function __call() { 
     // implementation 
    } 
} 

Aber wir aren‘ t wirklich sicher, ob es eine gute Lösung ist und ob es auch in Java etwas Ähnliches gibt (wie schon vor dem Bearbeiten gefragt).

+0

Bitte beschreiben Sie das Problem besser. Zusammengesetztes Muster ist für die Interaktion mit mehreren Objekten so, als wären sie eins oder dasselbe, unabhängig von der Struktur, die sie zusammensetzen. Was wollen Sie mit einem Verbundmuster erreichen? –

+0

Ich wäre wirklich interessiert zu sehen, warum du es nicht von Anfang an in Java machst. –

+0

@ Gabriel: Bearbeitet. Ich hoffe es hilft. @Ionut: Bürokratie :) –

Antwort

2

In Java könnten Sie die visitor Muster mit berücksichtigen, wobei Sie einen Besucher Objekt zu jedem Knoten im Baum übergeben, und der Knoten macht einen Rückruf den Besucher Klasse zu bestimmen, welches Verhalten durchgeführt werden sollte.

Dadurch wird vermieden, dass der Typ jedes Knotens umgewandelt oder explizit überprüft wird.

/** 
* 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 für eine Design-Lösung, nicht ein Reflexionshack :) –

2

Besucher Entwurfsmuster ist eine ziemlich gute Lösung. Aber Sie müssen mögliche Änderungen in der Struktur berücksichtigen, z.B. Mit der neuen Leaf-Klasse implementieren Sie applyVisitor und fügen die visit * -Methode jedem anderen Besucher hinzu, den Sie erstellt haben. So hilft Ihnen der Besucher wirklich, das Verhalten strukturierter Objekte zum Preis dieser Struktur hinzuzufügen, ohne sich allzu oft zu ändern. Wenn sich die Struktur häufig und die Algorithmen nicht so sehr ändern, könnten Sie verschiedene Kombinationen für Objekte mit denselben Schnittstellen in Betracht ziehen. Wenn Sie die schmutzige Weise tun möchten, wie Sie zurzeit in PHP tun, schauen Sie sich Java Reflection API an.Eine gute Lösung wäre imho dynamische Aufrufe (wie in Ruby oder Python). Sie können diese simulieren, aber das wäre viel Arbeit ... Also meine Antwort ist, verwenden Sie den Besucher mit Vorsicht oder betrachten Sie verschiedene Composites für Objekte mit unterschiedlichem Verhalten.