2012-04-28 71 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