2012-04-28 28 views
7

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

例如,类似于下面的内容,其中执行方法的选择仅依赖于类型为Actor的对象的一个​​字段。

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

将成为

actor.act(); 

与行为方法将在演员的子类覆盖。

然而,在运行时实例化的子类来决定最明显的方法看起来非常类似于原始:

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

因此它似乎没有什么真的已经取得了;逻辑刚刚被移动。

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

我是否在推翻这个?如果我在其他地方做了几乎相同的事情,那么看起来原来的改变就没有意义了。

回答

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的类型。

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

对不起,澄清,我想选择哪个子类在运行时实例化,而不是在编译时,如你的例子。这需要一个大开关或if-else块,我想避免这种情况。我开始认为这不是必要的,因为只有在创作时才需要它。 – flowsnake 2012-04-28 23:05:47

+0

嗯..实际上工厂几乎没有w /编译vs运行时实例本身,而是创建Actor的责任和控制。例如,如果上述的Actor的创建发生在Event中,它是不是运行时? – 2012-04-29 00:15:27

+0

你在做什么是“决定”集中创造Actors ..你想要演员来自一个地方,在这种情况下......你无法绕过切换......因为这是一个决定最终用户。 – 2012-04-29 00:19:24

1

我相信,你可以用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; 
    }  
} 
+0

我确实预先看过这个,但我认为这可能是我想做的事情不必要的复杂。我不确定。 – flowsnake 2012-04-28 23:08:04

2

switch语句不是纯粹的邪恶。这是真的重复,你正在寻找消除更好的设计。很多时候,你会发现相同的switch语句出现在代码中不同的地方 - 不一定是做同样的事情,而是开启相同的数据。通过引入多态性,您可以将这些开关合并为同一对象的不同方法。

此做两件事情,第一它减少几个切换到一个开关工厂的里面拉在一起摊开,可能依赖于类似的数据的逻辑。该数据将变成您的对象中的成员变量。

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

+0

我其实确实考虑过这样做,但后来开始阅读如何反思应该是最后的手段。 – flowsnake 2012-04-29 04:34:46

+0

反射是一个强大的工具,有很多方法可以在不牺牲性能的情况下使用它。我在回答中描述的例子只会在启动时使用一次反射,然后使用'HashMap'。 – 2012-04-29 05:20:21