2010-11-29 40 views
6

J'ai conçu ce qui suit pour un problème:conception en utilisant la composition et les interfaces en 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 
} 
} 

animaux, HornedAnimal et TailedAnimal sont principalement utilisés comme modèles de données.

Étant donné que Java ne prend pas en charge l'héritage multiple, j'ai de la difficulté à créer Rhinoceros qui est un animal à cornes et à queue. Après avoir demandé autour, quelqu'un a recommandé d'utiliser la composition et les interfaces. Je suis venu avec ce qui suit:

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? 
} 

Mon interface a des getters et des setters. y-a-t-il un moyen d'éviter ça? En supposant qu'il n'y a actuellement aucun moyen d'abstraire le comportement de Tails and Horns, et ils sont principalement utilisés comme détenteurs de données. Comment puis-je déterminer si mon animal est corné ou à queue?

+0

3. 'isinstance IHorned' – khachik

+1

Khachik, je crois qu'il est « instanceof IHorned » –

+0

Eh oui, merci pour réponse à 3. –

Répondre

4

Je pense que vous devez éviter les setters en général. Si vous le pouvez, utilisez des objets immuables et initialisez ses données privées dans son constructeur.

Pour distinguer les animaux, j'ai utilisé un autre modèle, le visiteur. C'est bavard, mais vous n'avez pas à tester directement quel animal vous traitez.

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

Le modèle des visiteurs est en effet souvent (comme dans ce cas) un très souple, en particulier parce qu'il évite mauvaises vérifications 'instanceof'. – Mot

1

J'ai modifié ma réponse précédente. J'ai pensé à quelque chose de bien mieux. Voir la révision pour ce poste si vous êtes curieux.

Utilisez le modèle de spécification. Cela correspond beaucoup à la facture ici - bien plus que Decorator. Vous avez demandé à "vérifier" si un animal était corné. Le motif décorateur offre de la transparence, alors que dans cette situation, vous semblez demander une discrimination.

Le modèle Spécification encapsule la connaissance de la façon d'évaluer certains critères. Dans notre cas, nous voudrions quelque chose comme:

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. 
    } 

} 

Maintenant, vous pouvez définir votre hiérarchie animale comme vous le souhaitez. La seule chose que vous devez maintenant faire est de comprendre ce que rend un animal à cornes. Votre réponse à cette question va dans la classe Spécification. Alors votre fonction principale est facile comme tarte.

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

Je suggère un schéma de stratégie ici. En bref:

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

En utilisant ce que vous encapsulant le comportement dans la mise en œuvre concrète des interfaces, andmay part facile exactement le même comportement pour quelques animaux, ainsi que le changer lors de l'exécution.

+1

+1 pour les noms de méthodes –

+0

hey, je l'ai eu en premier;). En tout cas, cette réponse est plus "compacte" que la mienne –