2010-09-08 13 views
5

Angenommen, ich eine Klassenhierarchie in Java haben:versand design muster?

interface Item { ... }; 
class MusicBox implements Item { ... }; 
class TypeWriter implements Item { ... }; 
class SoccerBall implements Item { ... }; 

und ich habe eine andere Klasse im gleichen Paket:

class SpecialItemProcessor { 
    public void add(Item item) 
    { 
     /* X */ 
    } 
} 

wo ich etwas will anders für jeden Elementtyp tun, aber ich don Ich möchte diese Aktion in den verschiedenen Item Klassen (MusicBox, TypeWriter, SoccerBall) definieren.

Eine Möglichkeit, dies zu handhaben ist:

class SpecialItemProcessor { 
    public void add(Item item) 
    { 
     if (item instanceof MusicBox) 
     { 
      MusicBox musicbox = (MusicBox)item; 
      ... do something ... 
     } 
     else if (item instanceof MusicBox) 
     { 
      TypeWriter typewriter = (TypeWriter)item; 
      ... do something ... 
     } 
     else if (item instanceof SoccerBall) 
     { 
      SoccerBall soccerball = (SoccerBall)item; 
      ... do something ... 
     } 
     else 
     { 
      ... do something by default ... 
     } 
    } 
} 

Dies funktioniert, aber es scheint wirklich klobig. Gibt es einen besseren Weg, wenn ich von Sonderfällen weiß? (natürlich, wenn Item enthält eine Methode doSomethingSpecial dann kann ich einfach die Methode dieses Artikels anrufen, ohne sich darum zu kümmern, welcher Typ es ist, aber wenn ich nicht möchte, dass die Unterscheidung innerhalb des Elements selbst geschieht, wie gehe ich damit um?)

+0

Keine Antwort ich weiß, aber in ActionScript 3 können Sie anscheinend eine Klasse aus einer Zeichenfolge instanziieren (dh; "com.djw.MusicBox" kann eine MusicBox instanziieren), ist so etwas ist vielleicht in Java möglich? Nur ein Vorschlag! – danjah

+0

@dan: 'Klasse # forName()'. Ich stelle jedoch den Wert hier in Frage. – BalusC

Antwort

2

Ich denke, ich werde die Idee der Umkehrung der Steuerung verwenden und die visitor pattern:

interface Item { 
    public void accept(Visitor visitor); 
    ... 

    public interface Visitor { 
     public void visit(Item item); 
    } 
} 


class MusicBox implements Item { 
    public interface Visitor extends Item.Visitor { 
     public void visitMusicBox(MusicBox item); 
    } 
    ... 
    @Override public accept(Item.Visitor visitor) 
    { 
     if (visitor instanceof MusicBox.Visitor) 
     { 
      ((MusicBox.Visitor)visitor).visitMusicBox(this); 
     } 
    } 
} 

class TypeWriter implements Item { 
    public interface Visitor extends Item.Visitor { 
     public void visitTypeWriter(TypeWriter item); 
    } 
    ... 
    @Override public accept(Item.Visitor visitor) 
    { 
     if (visitor instanceof TypeWriter.Visitor) 
     { 
      ((TypeWriter.Visitor)visitor).visitTypeWriter(this); 
     } 
    } 
} 

class SoccerBall implements Item { 
    public interface Visitor extends Item.Visitorr { 
     public void visitSoccerBall(SoccerBall item); 
    } 
    ... 
    @Override public accept(Item.Visitor visitor) 
    { 
     if (visitor instanceof SoccerBall.Visitor) 
     { 
      ((SoccerBall.Visitor)visitor).visitSoccerBall(this); 
     } 
    } 
} 

und führen Sie dann die folgende, die zumindest den instanceof einen Scheck pro add() Aufruf reduziert :

class SpecialItemProcessor 
    implements 
     MusicBox.Visitor, 
     TypeWriter.Visitor, 
     SoccerBall.Visitor, 
     Item.Visitor 
{ 
    public void add(Item item) 
    { 
     item.accept(this); 
    } 
    @Override public void visitMusicBox(MusicBox item) 
    { 
     ... 
    } 
    @Override public void visitTypeWriter(TypeWriter item) 
    { 
     ... 
    } 
    @Override public void visitSoccerBall(SoccerBall item) 
    { 
     ... 
    } 
    @Override public void visit(Item item) 
    { 
     /* not sure what if anything I should do here */ 
    } 
} 
0

Sie könnten ein Brückenmuster für Item erstellen, in dem die andere Seite die zugehörigen Prozesse ausführen würde, wenn add() aufgerufen wird. Sie können der Mischung auch eine Factory-Methode hinzufügen.

class SpecialItemProcessor { 
    public void add(Item item) 
    { 
    Process p = Item.createCorrespondingProcessor(p); 
    p.doWhenAddin(); 
    } 
} 

Hoffe das hilft.

+0

Was ist das für eine Syntax? Und wie benutzt du 'p', bevor du es anfänglich zugewiesen hast? – jjnguy

+0

Dies ist nicht PHP ... Richtige Version: 'p.doWhenAddin()' – TheLQ

+0

Sorry, ich warf endlich in C++ Syntax. Dieser Quellcode ist Java. Die Create-Entsprechende-Prozessor-Methode ist eine Factory-Methode, die die entsprechende Klasse entsprechend der Klassenhierarchie von Prozessoren erstellt, die mit der Klassenhierarchie von Items verknüpft ist. – Baltasarq

7

In Java können Sie mehrere Dispatch mit einem Besucher (ähnlich) Muster tun. Die Item-Implementierungen müssen die Verarbeitungslogik nicht enthalten, sie benötigen lediglich eine Methode des Typs accept().

public interface Item { 
/** stuff **/ 

void processMe(ItemProcessor processor); 

} 

public interface ItemProcessor { 

void process(MusicBox box); 

void process(SoccerBall ball); 

//etc 

} 

public class MusicBox implements Item { 

    @Override 
    public void processMe(ItemProcessor processor) { 
    processor.process(this); 
    } 

} 

public class ItemAddingProcessor implements ItemProcessor { 

    public void add(Item item) { 
    item.processMe(this); 
    } 

    @Override 
    public void process(MusicBox box) { 
    //code for handling MusicBoxes 
    //what would have been inside if (item instanceof MusicBox) {} 
    } 

//etc 
} 
+0

Hmm. Gibt mir einige Ideen, aber in Ihrem Code hat die Schnittstelle Item jetzt eine Abhängigkeit von ItemProcessor, die eine Abhängigkeit von den MusicBox- und SoccerBall-Klassen hat (also nicht sicher, ob das überhaupt kompiliert würde). –

+0

Es würde kompilieren, Sie können Abhängigkeiten zwischen haben Klassen in Java. Allerdings können Sie keine Abhängigkeiten zwischen .jars haben, daher funktioniert es nicht, wenn sich Item und ItemProcessor in verschiedenen .jars befinden. – amorfis

+0

@Jason: Ja, es kompiliert :) Es ist nur eine Möglichkeit, um Dinge herum zu drehen, so dass statt einer Methode, die versucht, herauszufinden, wie man mit der bestimmten Art von Item umzugehen, Sie die verschiedenen Arten von Items in sich übergeben eine Methode, die ihre Handhabung hat. Die Kompilierzeit-Kopplung kann ein Vorteil darin sein, dass wenn Sie diese if-else-Instanz in vielen Bereichen der Anwendung blockieren, es viel einfacher ist, eine zu verpassen, wenn Sie einen neuen Elementtyp erstellen, als wenn sie alle eine Schnittstelle implementieren das hat sich geändert. – Affe

0

Warum nicht einige Callback-Funktion zu Item-Schnittstelle definieren?

Dann in jeder Klasse, die Element implementiert, wie MusicBox, sollte es die Callback-Funktion implementieren.

public class MusicBox { 
    @override 
    public void onCallBack() { 
    // business logic 
    ... 
    ... 
    } 
} 

Dann könnten Sie einen Dispatcher erstellen, den Sie "SpecialItemProcessor" nennen.

public SpecialItemProcessor { 
    private final Item _item; 

    public SpecialItemProcessor(Item item) { 
    _item = item; 
    } 

    public dispatch() { 
    _item.onCallBack() 
    } 
} 

Und dann, in der Client-Klasse, die die SpecialItemProcessor enthält könnte die Methode nur nennen, wie:

public void XXXX() { 
    .... 
    SpecialItemProcessor specialItemProcessor = new SpecialItemProcessor(new MusicBox()); 
    specialItemProcessor.dispatch(); 
    .... 
} 

Eigentlich in C++, ist diese Bindung Dynamisch. Und deshalb gibt es eine reine abstrakte Klasse ...

+1

Lesen Sie die Frage: "wo ich für jeden Gegenstandstyp etwas anderes machen möchte, ** aber ich möchte diese Aktion nicht in den verschiedenen Gegenstandsklassen ** (MusicBox, TypeWriter, SoccerBall) definieren. " –