【问题标题】:creating objects and polymorphism创建对象和多态
【发布时间】:2012-05-09 05:32:35
【问题描述】:

我想避免使用标记类和大 if-else 块或 switch 语句,而是使用具有类层次结构的多态性,我认为这是更好的做法。

例如,如下所示,执行方法的选择仅取决于 Actor 类型对象的一个​​字段。

switch(actor.getTagField())
{
    case 1: actor.act1(); break;
    case 2: actor.act2(); break;
    [...]
}

会变成

actor.act();

并且 act 方法将在 Actor 的子类中被覆盖。

但是,在运行时决定要实例化哪个子类的最明显方法看起来与原始方法非常相似:

Actor newActor(int type)
{
    switch(type)
    {
        case 1: return new Actor1();
        case 2: return new Actor2();
        [...]
    }
}

所以似乎什么都没有真正得到;逻辑刚刚被移动。

有什么更好的方法来做到这一点?我能想到的唯一方法是为 Actor 的每个子类实现一个工厂类,但对于这样一个简单的问题,这似乎相当麻烦。

我是不是想多了?如果我只是在其他地方做几乎相同的事情,似乎没有必要进行原始更改。

【问题讨论】:

    标签: java design-patterns polymorphism


    【解决方案1】:

    问题是“如果”你需要一个工厂。工厂旨在管理实例的创建,而不是相关实例的行为。

    否则,您只是在查看基本继承。有点像..

    class Actor{
      public void act(){
        System.out.println("I act..");
      }
    }
    
    class StuntActor extends Actor {
      public void act(){
        System.out.println("I do fancy stunts..");
      }
    }
    
    class VoiceActor extends Actor {
      public void act(){
        System.out.println("I make funny noises..");
      }
    }
    

    要使用,你可以直接实例化你需要的actor类型。

    Actor fred = new Actor();
    Actor tom = new VoiceActor();
    Actor sally = new StuntActor();
    
    fred.act();
    tom.act();
    sally.act();
    

    输出:

    I act..
    I make funny noises..
    I do fancy stunts..
    

    编辑:

    如果您需要集中创建 Actors..aka 相对于工厂,您将无法摆脱某种切换逻辑——在这种情况下..我通常会使用枚举来提高可读性:

    public class Actor{
      public enum Type{ REGULAR, VOICE, STUNT }
    
      public static Actor Create(Actor.Type type){
        switch(type) {
          case VOICE:
            return new VoiceActor();
          case STUNT:
            return new StuntActor();
          case REGULAR:
          default:
            return new Actor();
        }
      }
    
      public void act(){
        System.out.println("I act..");
      }
    }
    

    用法:

    Actor some_actor = Actor.Create(Actor.Type.VOICE);
    some_actor.act();
    

    输出:

    I make funny noises..
    

    【讨论】:

    • 抱歉,澄清一下,我想选择在运行时实例化哪个子类,而不是像您的示例那样在编译时实例化。这需要一个大的 switch 或 if-else 块,我想避免这种情况。我开始认为这没有必要,因为它只在创建时需要一次。
    • 嗯..实际上工厂与编译与运行时实例化本身几乎没有什么关系,而是创建 Actors 的责任和控制。例如,如果上面的 Actors 的创建发生在一个 Event 中,那它不是运行时的吗?
    • 您正在做的是“决定”集中创建 Actors..即您希望 Actors 来自一个地方,在这种情况下..您无法绕过切换。 ..因为这是最终用户的决定。
    • 您说得对。如果您将切换逻辑放在方法级别..那么如果Actor获得其他方法,则需要复制切换逻辑;即如果演员学会唱歌()。
    • 是的,这就是我所缺少的,因为我的示例过于简单。感谢您的帮助,我最终在我的实际代码中使用了 if-else 块。枚举并不真正适合我的特定项目,但总的来说这仍然是一个好主意。
    【解决方案2】:

    Switch 语句并不是纯粹的邪恶。您希望通过更好的设计来消除真正的重复。通常,您会发现相同的 switch 语句出现在代码中的不同(遥远)位置 - 不一定做同样的事情,而是打开相同的数据。通过引入多态性,您可以将这些开关作为同一对象的不同方法组合在一起。

    这做了两件事,首先它将工厂内的几个开关减少到一个开关它将可能依赖于相似数据的分散逻辑拉在一起。该数据将变成您对象中的成员变量。

    还值得注意的是,您并不总是在工厂的底层使用 switch 语句。也许您可以在启动时扫描类路径并构建实现接口的类型的HashMap。例如,考虑一个像 SMTP 这样的套接字协议的实现。您可以拥有名为HeloCommandMailFromCommand 等的对象,并通过将套接字命令与类名匹配来找到处理消息的正确对象。

    【讨论】:

    • 我确实考虑过做类似的事情,但后来开始阅读有关反思应该如何成为最后手段的文章。
    • Reflection 是一个强大的工具,并且有很多方法可以在不牺牲性能的情况下使用它。我在回答中描述的示例只会在启动时使用一次反射,然后使用HashMap
    【解决方案3】:

    我相信你可以用Abstract factory pattern...

    这是一个例子:

    abstract class Computer {
        public abstract Parts getRAM();
        public abstract Parts getProcessor();
        public abstract Parts getMonitor();
    }
    
    class Parts {
        public String specification;
        public Parts(String specification) {
            this.specification = specification;
        }
        public String getSpecification() {
            return specification;
        }
    }
    

    我们有两个类扩展Computer

    class PC extends Computer {
        public Parts getRAM() {
            return new Parts("512 MB");
        }
        public Parts getProcessor() {
            return new Parts("Celeron");
        }
        public Parts getMonitor() {
            return new Parts("15 inches");
        }
    }
    
    class Workstation extends Computer {
        public Parts getRAM() {
            return new Parts("1 GB");
        }
        public Parts getProcessor() {
            return new Parts("Intel P 3");
        }
        public Parts getMonitor() {
            return new Parts("19 inches");
        }
    }
    

    我们终于有了,

    public class ComputerType {
        private Computer comp;
        public static void main(String[] args) {
            ComputerType type = new ComputerType();
            Computer computer = type.getComputer("Workstation");
            System.out.println("Monitor: "+computer.getMonitor().getSpecification());
            System.out.println("RAM: "+computer.getRAM().getSpecification());
            System.out.println("Processor: "+computer.getProcessor().getSpecification());
        }    
    
        public Computer getComputer(String computerType) {
            if (computerType.equals("PC"))
                comp = new PC();
            else if(computerType.equals("Workstation"))
                comp = new Workstation();
            return comp;
        }    
    }
    

    【讨论】:

    • 我确实事先查过了,但我认为这对于我想做的事情来说可能是不必要的复杂。我不确定。
    猜你喜欢
    • 2015-08-12
    • 2014-10-08
    • 2013-12-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-14
    • 1970-01-01
    相关资源
    最近更新 更多