7

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

这是我想太多了吗?如果我只是在其他地方做几乎相同的事情,那么进行原始更改似乎没有意义。

4

3 回答 3

4

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

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

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

编辑:

如果您需要集中创建 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..
于 2012-04-28T22:52:45.317 回答
3

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

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

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

于 2012-04-29T00:49:33.683 回答
2

我相信你可以用抽象工厂模式来做到这一点......

这是一个例子:

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;
    }    
}
于 2012-04-28T22:42:09.770 回答