【问题标题】:Design using composition and interfaces in Java在 Java 中使用组合和接口进行设计
【发布时间】:2011-05-17 06:49:41
【问题描述】:

我为一个问题设计了以下内容:

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 不支持多重继承,我很难创建犀牛,它是一种有角和有尾的动物。四处打听后,有人建议使用组合和接口。我想出了以下几点:

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

我的界面有 getter 和 setter。有没有办法避免这种情况?假设目前没有办法抽象 Tails 和 Horns 的行为,它们主要用作数据持有者。如何确定我的动物是有角的还是有尾的?

【问题讨论】:

  • khachik,我相信它是 'instanceof IHorned'

标签: java inheritance composition


【解决方案1】:

我已经编辑了我之前的答案。我想到了更好的东西。如果您好奇,请参阅这篇文章的修订版。

利用规范模式。它非常符合这里的要求 - 远远超过装饰器。您要求“检查”动物是否有角。装饰器模式提供了透明度,而在这种情况下,您似乎在要求区分。

规范模式封装了如何评估某些标准的知识。在我们的例子中,我们想要这样的东西:

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

}

【讨论】:

    【解决方案2】:

    我认为您通常必须避免使用二传手。如果可以,请使用不可变对象,并将其私有数据初始化到其构造函数中。

    为了区分动物,我使用了另一种模式,访客模式。它很冗长,但您不必直接测试您正在处理的动物。

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

    【讨论】:

    • 访问者模式确实经常(如本例所示)是一种非常灵活的模式,尤其是因为它避免了讨厌的instanceof 检查。
    【解决方案3】:

    我会在这里建议策略模式。简而言之:

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

    通过使用它,您可以将行为封装在接口的具体实现中,并且可以轻松地为一些动物共享完全相同的行为,并在运行时更改它。

    【讨论】:

    • 嘿,我先得到它;)。无论如何,这个答案比我的更“紧凑”
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-29
    • 1970-01-01
    • 1970-01-01
    • 2016-05-16
    • 2014-01-11
    相关资源
    最近更新 更多