2010-11-29 7 views
6

entworfen ich folgende für ein Problem:Entwerfen Sie mit Zusammensetzung und Schnittstellen in Java

class Animal { 
// ... 
} 
class Guppy extends Animal { ... } 
class Pigeon extends Animal { ... } 

class TailedAnimal extends Animal { 
// ... 
} 
class Dog extends TailedAnimal { ... } 
class Cat extends TailedAnimal { ... } 

class HornedAnimal extends Animal { 
// ... 
} 
class Ram extends HornedAnimal { ... } 

public static void main(String[] args) { 
Animal a = getSomeAnimal(); 
a.doSomething(); 
if (a instanceof TailedAnimal) { 
    // do something 
} 
if (a instanceof HornedAnimal) { 
    // do something else 
} 
} 

Tier, HornedAnimal und TailedAnimal werden hauptsächlich als Datenmodelle verwendet.

Da Java keine Mehrfachvererbung unterstützt, habe ich Probleme beim Erstellen von Rhinoceros, das ein Horned and Tailed-Tier ist. Nachdem Sie sich erkundigt hatten, empfahl jemand die Verwendung von Komposition und Interfaces. Ich habe folgendes gefunden:

class Animal { 
// ... 
} 
class Guppy extends Animal { ... } 
class Pigeon extends Animal { ... } 
class Ram extends Animal implements IHorned { ... } 
class Cat extends Animal implements ITailed { ... } 
class Dog extends Animal implements ITailed { 
BasicTail t = new BasicTail(); 
public Object getTail() { 
    return t.getTail(); 
} 
public void setTail(Object in) { 
    t.setTail(in); 
} 
} 

interface ITailed { 
public Object getTail(); 
public void setTail(Object in); 
//... 
} 

class BasicTail implements ITailed { 
Object myTail; 
public Object getTail() { return myTail; } 
public void setTail(Object t) { myTail = t; } 
} 

interface IHorned { 
// getters and setters 
} 

public static void main(String[] args) { 
Animal a = getSomeAnimal(); 
a.doSomething(); 
    // how do I check if a is horned or tailed? 
} 

Meine Schnittstelle hat Getter und Setter. Gibt es eine Möglichkeit, dies zu vermeiden? Angenommen, es gibt derzeit keine Möglichkeit, das Verhalten von Tails und Horns zu abstrahieren, und sie werden hauptsächlich als Datenspeicher verwendet. Wie kann ich feststellen, ob mein Tier Gehörnte oder Tailed ist?

+0

3. 'isinstance IHorned' – khachik

+1

Khachik, ich glaube, es ist‚Instanceof IHorned‘ –

+0

Yup, vielen Dank für Antwort auf 3. –

Antwort

4

Ich denke, Sie müssen Setter im Allgemeinen vermeiden. Wenn Sie können, verwenden Sie unveränderliche Objekte und initialisieren Sie ihre privaten Daten in ihrem Konstruktor.

Um Tiere zu unterscheiden, benutzte ich ein anderes Muster, den Besucher. Es ist ausführlich, aber Sie müssen nicht direkt testen, welches Tier Sie verarbeiten.

public class Animals { 
private Animals() { 
} 

interface Animal { 
    void accept(final AnimalProcessor visitor); 
} 

interface AnimalProcessor { 
    void visitTailed(final TailedAnimal tailedAnimal); 

    void visitHorned(final HornedAnimal hornedAnimal); 
} 

interface TailedAnimal extends Animal { 
    void moveTail(); 
} 

interface HornedAnimal extends Animal { 
    void hitWithHorns(); 
} 

static class Dog implements TailedAnimal { 
    public void moveTail() { 
     //To change body of implemented methods use File | Settings | File Templates. 
    } 

    public void accept(final AnimalProcessor visitor) { 
     visitor.visitTailed(this); 
    } 
} 

static class Cat implements TailedAnimal { 
    public void moveTail() { 
     //To change body of implemented methods use File | Settings | File Templates. 
    } 

    public void accept(final AnimalProcessor visitor) { 
     visitor.visitTailed(this); 
    } 
} 

static class Ram implements HornedAnimal { 
    public void hitWithHorns() { 
     //To change body of implemented methods use File | Settings | File Templates. 
    } 

    public void accept(final AnimalProcessor visitor) { 
     visitor.visitHorned(this); 
    } 
} 

static class Rhinoceros implements HornedAnimal, TailedAnimal { 
    public void hitWithHorns() { 
     //To change body of implemented methods use File | Settings | File Templates. 
    } 

    public void moveTail() { 
     //To change body of implemented methods use File | Settings | File Templates. 
    } 

    public void accept(final AnimalProcessor visitor) { 
     visitor.visitTailed(this); 
     visitor.visitHorned(this); 
    } 
} 

public static void main(String[] args) { 
    Collection<Animal> animals = new ArrayList<Animal>(Arrays.asList(new Dog(), new Cat(), new Rhinoceros())); 
    for (final Animal animal : animals) { 
     animal.accept(new AnimalProcessor() { 
      public void visitTailed(final TailedAnimal tailedAnimal) { 
       // you do what you want when it's a tailed animal 
      } 

      public void visitHorned(final HornedAnimal hornedAnimal) { 
       // you do what you want when it's a horned animal 
      } 
     }); 
    } 
} 
} 
+0

Das Besuchermuster ist oft in der Tat (wie in diesem Fall) sehr flexibel ein, vor allem, weil es vermeidet böse 'instanceof' Prüfungen. – Mot

1

Ich habe meine vorherige Antwort bearbeitet. Ich dachte an etwas viel Besseres. Wenn Sie neugierig sind, sehen Sie sich die Überarbeitung dieses Posts an.

Verwenden Sie das Spezifikationsmuster. Es passt hier sehr viel - viel mehr als Dekorateur. Du hast gefragt, ob ein Tier gehörnt sein sollte. Decorator Muster bietet Transparenz, während in dieser Situation scheinen Sie nach Diskriminierung zu fragen.

Das Spezifikationsmuster enthält Wissen darüber, wie einige Kriterien bewertet werden. In unserem Fall möchten wir etwas wie:

public interface Specification { 

    public boolean isSatisfiedBy(Animal aCriteria); 

} 

public class HornedAnimalSpecification implements Specification { 

    @Override 
    public boolean isSatisfiedBy(Animal aCriteria) { 
     //Right here is where the heart of your problem 
     //can be solved. 
     // 
     //Reserved conquering grounds. 
    } 

} 

Jetzt können Sie Ihre Tierhierarchie definieren, wie Sie wollen. Das einzige, was Sie jetzt tun müssen, ist herauszufinden, was macht ein gehörntes Tier. Ihre Antwort auf diese Frage bezieht sich auf die Spezifikation Klasse. Dann ist Ihre Hauptfunktion einfach wie Kuchen.

public class Zoo { 

    public static void main(String[] args) { 
     Animal ram = getHornedAnimal(); //Instantiate however you'd like. 
     Specification specification = new HornedAnimalSpecification(); 

     if (specification.isSatisfiedBy(ram)) { 
      //Bingo, it's horned. 
     } else { 
      //Not horned! 
     } 
    } 

} 
8

Ich würde Strategie Muster hier vorschlagen. Kurz gesagt:

interface TailedAnimal { 
    void moveTail(); 
} 
interface HornedAnimal { 
    void hitWithHorn(); 
} 
class Rhinoceros() implements TailedAnimal, HornedAnimal { 
    private TailedAnimal tail; //Instantiate it somehow e.g. constructor, setter 
    private HornedAnimal horn; //Instantiate is somehow e.g. constructor, setter 
    public void moveTail() { 
     tail.moveTail(); 
    } 
    public void hitWithHorn() { 
     horn.hitWithHorn(); 
    } 
} 

Durch diese Verwendung kapseln Sie Verhalten in konkreter Umsetzung der Schnittstellen, andmay leicht Aktie genau das gleiche Verhalten für ein paar Tiere, als auch die Änderung zur Laufzeit.

+1

+1 für die Methodennamen –

+0

hey, ich habe es zuerst;). Wie auch immer, diese Antwort ist "kompakter" als meine –