开发者

Design using composition and interfaces in Java

I designed the following for a problem:

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

Animal, HornedAnimal and TailedAnimal are used mainly as data models.

Since Java does not support multiple inheritance, I have trouble creating Rhinoceros which is a Horned and Tailed animal. After asking around, someone recommended using composition and interfaces. I came up with the following:

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

My interface has getters and setters. Is there any way to avoid this? Assuming that there is currently no way to abstract the behaviour of Tails and Horns, and they're开发者_StackOverflow are being used mainly as data holders. How do I determine if my Animal is Horned or Tailed?


I'd suggest strategy pattern here. In short:

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 it somehow e.g. constructor, setter
    public void moveTail() {
        tail.moveTail();
    }
    public void hitWithHorn() {
        horn.hitWithHorn();
    }
}

By using this you encapsulate behavior in a concrete implementation of the interfaces, and may easily share exactly the same behavior for a few animals, as well as change it at run-time.


I think you must avoid setters in general. If you can, use immutable objects, and initialize its private data into its constructor.

To distinguish animals, I used another pattern, the visitor one. It's verbose, but you don't have to test directly what animal you're processing.

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


I've edited out my previous answer. I thought of something much better. See the revision for this post if you're curious.

Make use of the Specification pattern. It very much fits the bill here - way more than Decorator. You asked to "check" if an Animal was horned. Decorator pattern delivers transparency, while in this situation you appear to be asking for discrimination.

The Specification pattern encapsulates knowledge of how to evaluate some criteria. In our case, we'd want something like:

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

}

Now you can define your Animal hierarchy however you want. The only thing you now need to do is figure out what makes an animal horned. Your answer to that question goes into the Specification class. Then your main function is easy as pie.

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

}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜