Дизайн с использованием композиции и интерфейсов в Java - PullRequest
7 голосов
/ 29 ноября 2010

Я разработал следующее для проблемы:

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 и TailedAnimal используются в основном в качестве моделей данных.

Поскольку Java не поддерживает множественное наследование, у меня проблемы с созданием Rhinoceros, который является рогатым и хвостатым животным. После расспросов кто-то рекомендовал использовать композицию и интерфейсы. Я придумал следующее:

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

В моем интерфейсе есть геттеры и сеттеры. Есть ли способ избежать этого? Предполагая, что в настоящее время нет способа абстрагировать поведение Tails и Horns, и они используются в основном в качестве держателей данных. Как определить, является ли мое животное рогатым или хвостатым?

Ответы [ 3 ]

8 голосов
/ 29 ноября 2010

Я бы предложил схему стратегии здесь. Короче говоря:

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

Используя это, вы инкапсулируете поведение в конкретной реализации интерфейсов и можете легко использовать точно такое же поведение для нескольких животных, а также изменять его во время выполнения.

4 голосов
/ 29 ноября 2010

Я думаю, что вы должны избегать сеттеров вообще.Если вы можете, используйте неизменные объекты и инициализируйте свои личные данные в своем конструкторе.

Чтобы различать животных, я использовал другой шаблон, посетительский.Это многословно, но вам не нужно напрямую проверять, какое животное вы обрабатываете.

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
            }
        });
    }
}
}
1 голос
/ 29 ноября 2010

Я отредактировал свой предыдущий ответ.Я думал о чем-то намного лучше.Посмотрите редакцию этого поста, если вам интересно.

Воспользуйтесь шаблоном спецификации.Это очень хорошо подходит здесь - гораздо больше, чем декоратор.Вы попросили «проверить», если животное было рогатым.Шаблон декоратора обеспечивает прозрачность, в то время как в этой ситуации вы, похоже, просите о различении.

Шаблон спецификации содержит знания о том, как оценивать некоторые критерии.В нашем случае мы бы хотели что-то вроде:

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

}

Теперь вы можете определять свою иерархию животных так, как хотите.Единственное, что вам теперь нужно сделать, это выяснить , что делает животное рогатым.Ваш ответ на этот вопрос входит в класс спецификаций.Тогда ваша основная функция проста как пирог.

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

}
...