Java 创建对象和多态性

Java 创建对象和多态性,java,design-patterns,polymorphism,Java,Design Patterns,Polymorphism,我希望避免使用带标签的类和大if-else块或switch语句,而是使用带有类层次结构的多态性,我认为这是更好的做法 例如,类似下面的内容,其中执行方法的选择仅依赖于类型为Actor的对象的一个字段 switch(actor.getTagField()) { case 1: actor.act1(); break; case 2: actor.act2(); break; [...] } 将成为 actor.act(); act方法将在Actor的子类中被重写 但是,

我希望避免使用带标签的类和大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的每个子类实现一个工厂类,但对于这样一个简单的问题来说,这似乎相当麻烦


我是不是想得太多了?如果我在其他地方做了几乎相同的事情,那么做最初的更改似乎没有意义。

我相信你可以用

这是一个例子:

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;
    }
}
我们有两个类扩展了
计算机

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;
    }    
}
问题是“如果”你们需要一个工厂。工厂的目的是管理实例的创建,而不是管理相关实例的行为

否则,您只需查看基本继承。类似于

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 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..
I make funny noises..
编辑:

如果您需要集中创建参与者..又称工厂,您将无法摆脱某种切换逻辑--在这种情况下..为了可读性,我通常使用枚举:

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 act..
I make funny noises..
I do fancy stunts..
I make funny noises..

切换语句并不是纯粹的邪恶。您希望通过更好的设计消除的真正的重复。通常情况下,您会发现相同的switch语句出现在代码中不同(很远)的地方——不一定做相同的事情,而是切换相同的数据。通过引入多态性,可以将这些开关作为同一对象的不同方法拉到一起

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


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

抱歉,澄清一下,我想选择在运行时实例化哪个子类,而不是像您的示例中那样在编译时实例化。这需要一个大的开关或if-else块,这是我想要避免的。我开始认为这是没有必要的,因为它在创建时只需要一次。嗯……事实上,工厂在编译和运行时实例化本身方面几乎没有什么工作,而是负责和控制参与者的创建。例如,如果上述参与者的创建发生在一个事件中,那么它不是运行时吗?您所做的是“决定”集中创建参与者..即您希望参与者来自一个地方,在这种情况下..你无法回避切换…因为这是最终用户的决定。你正确地指出..如果你将切换逻辑放在方法级别..那么如果参与者获得其他方法,则需要复制切换逻辑;是的,这就是我所缺少的,因为我的例子过于简单。感谢您的帮助,我最终在实际代码中使用了if-else块。枚举并不适合我的具体项目,但总的来说,这仍然是一个好主意。我确实事先查过了,但我认为这对于我想做的事情来说可能是不必要的复杂。我不确定。我确实考虑过做这样的事情,但后来开始阅读如何反思是最后的手段。反射是一个强有力的工具,有办法在不牺牲性能的情况下使用它。我在回答中描述的示例只在启动时使用反射一次,然后使用
HashMap