2011-02-01 30 views
19

只有當您正在做的對象類型爲switch/if語句爲已選中時,「用條件替換多態」纔是優雅的。作爲一個例子,我有一個Web應用程序,它讀取一個名爲「action」的查詢字符串參數。動作可以有「查看」,「編輯」,「排序」等值。那麼我怎麼用多態來實現呢?那麼,我可以創建一個名爲BaseAction的抽象類,並從中派生ViewAction,EditAction和SortAction。但是我不需要一個條件來決定實例化哪種類型的BaseAction風格?我不知道如何完全替代多態性的條件。如果有的話,條件只是被推到了鏈條的頂端。用多態性代替條件 - 理論上不錯但不實際

編輯:

public abstract class BaseAction 
{ 
    public abstract void doSomething(); 
} 

public class ViewAction : BaseAction 
{ 
    public override void doSomething() { // perform a view action here... } 
} 

public class EditAction : BaseAction 
{ 
    public override void doSomething() { // perform an edit action here... } 
} 

public class SortAction : BaseAction 
{ 
    public override void doSomething() { // perform a sort action here... } 
} 


string action = "view"; // suppose user can pass either "view", "edit", or "sort" strings to you. 
BaseAction theAction = null; 

switch (action) 
{ 
    case "view": 
     theAction = new ViewAction(); 
     break; 

    case "edit": 
     theAction = new EditAction(); 
     break; 

    case "sort": 
     theAction = new SortAction(); 
     break; 
} 

theAction.doSomething(); // So I don't need conditionals here, but I still need it to decide which BaseAction type to instantiate first. There's no way to completely get rid of the conditionals. 
+0

請提供一些更多的代碼,如果你正在尋找一個多態性教程,你可能會發現它在網絡上的任何地方。請舉一些例子。 – amirouche 2011-02-01 19:18:50

+0

無法在此處添加所有代碼。我在下面提供了一個示例(查找Charles) – Charles 2011-02-01 20:09:11

+0

您可以使用反射api替換剩餘的條件。根據字符串在運行時確定要實例化的基本動作的類型。有一個倒下來,你的字符串應該與你的基本動作的確切名稱相匹配。您還可以使用哈希映射將基本操作映射到字符串,並在運行時進行選擇。 – 2016-03-21 05:14:15

回答

23

你是對的 - 「條件越來越被推到鏈條的頂端」 - 但是沒有「正義」。它非常強大。正如@thkala所說,你只需做出一次;從那裏出來,對象知道如何去做它的業務。你描述的方法--BaseAction,ViewAction和其他 - 是一個好方法。試試看看你的代碼變得多少。

當你有一個工廠方法需要一個像「View」這樣的字符串並返回一個Action,並且你調用它時,你已經隔離了你的條件。那很棒。你不能正確地欣賞力量,直到你嘗試了它 - 所以給它一個鏡頭!

7

有幾件事情要考慮:

  • 你只實例化對象的每一次。一旦你這樣做了,就不需要關於它的類型的更多條件。

  • 即使在一次性實例中,如果您使用了子類,您將刪除多少條件?使用條件語句像這樣的代碼很容易被完全相同的條件一次又一次又一次的 ...

  • 當你需要在未來foo行動值會發生什麼?你需要修改多少個地方?

  • 如果您需要的barfoo稍有不同,該怎麼辦?通過課程,您只需從FooAction繼承BarAction,覆蓋您需要更改的一件事。

從長遠看面向對象的代碼通常比程序代碼更易於維護 - 大師們不會有任何問題,但對於我們其餘的人有的差異。

+0

我可以看到下游的好處。但是,我仍然需要一個開關箱來決定創建哪種類型。 – Charles 2011-02-01 20:11:30

3

你的例子並不需要多態,它可能不會被建議。儘管用多態調度替換條件邏輯的原意是正確的。

下面是區別:在你的例子中,你有一個小的固定(和預先確定)的行動。此外,「排序」和「編輯」行爲幾乎沒有什麼共同之處,這些行爲並沒有強烈關聯。多態性過度構建您的解決方案。另一方面,如果你有很多具有特殊行爲的對象,那麼對於一個共同的概念,多態性就是你想要的。例如,在遊戲中可能有很多玩家可以「激活」的對象,但每個對象的反應都不相同。你可以用複雜的條件來實現這個(或者更可能是switch語句),但是多態會更好。多態性允許您引入不屬於您原始設計的新對象和行爲(但符合其精神)。

在你的例子中,對於支持視圖/編輯/排序動作的對象進行抽象仍然是一個好主意,但也許不會自己抽象這些動作。這是一個測試:你是否想要把這些行爲放在一個集合中?可能不是,但你可能有一個支持它們的對象列表。

3

有幾種方法可以將輸入字符串轉換爲給定類型的對象,而條件語句絕對是其中之一。根據實現語言的不同,還可以使用switch語句,該語句允許將期望的字符串指定爲索引,並創建或獲取相應類型的對象。仍然有更好的方法來做到這一點。

的查找表可以被用來輸入串映射到所需的對象:

action = table.lookup (action_name); // Retrieve an action by its name 
if (action == null) ...    // No matching action is found 

初始化代碼將需要創建所需的對象的護理,例如

table ["edit"] = new EditAction(); 
table ["view"] = new ViewAction(); 
... 

這是可以擴展以涵蓋更多細節的基本方案,例如操作對象的附加參數,在使用它們進行表查找之前對操作名稱進行規範化,使用整數而不是字符串來替換具有純數組的表,以標識請求的操作等

1
public abstract class BaseAction 
{ 
    public abstract void doSomething(); 
} 

public class ViewAction : BaseAction 
{ 
    public override void doSomething() { // perform a view action here... } 
} 

public class EditAction : BaseAction 
{ 
    public override void doSomething() { // perform an edit action here... } 
} 

public class SortAction : BaseAction 
{ 
    public override void doSomething() { // perform a sort action here... } 
} 


string action = "view"; // suppose user can pass either 
         // "view", "edit", or "sort" strings to you. 
BaseAction theAction = null; 

switch (action) 
{ 
    case "view": 
     theAction = new ViewAction(); 
     break; 

    case "edit": 
     theAction = new EditAction(); 
     break; 

    case "sort": 
     theAction = new SortAction(); 
     break; 
} 

theAction.doSomething(); 

所以我不需要條件在這裏,但我仍然需要它來決定首先實例化哪個BaseAction類型。沒有辦法完全擺脫條件。

0

多態性是一種綁定方法。這是一種稱爲「對象模型」的特例。對象模型用於操縱複雜的系統,如電路或繪圖。考慮存儲/編組文本格式的東西:項目「A」,連接到項目「B」和「C」。現在你需要知道什麼是連接到A.一個人可能會說,我不打算爲此創建一個對象模型,因爲我可以在解析時單數數據。在這種情況下,你可能是對的,你可能離開對象模型。但是如果你需要用進口設計做很多複雜的操作呢?你會以文本格式操縱它還是通過調用java方法發送消息,並且引用java對象更方便?這就是爲什麼有人提到你只需要翻譯一次。

1

我一直在想這個問題可能比我遇到的其他開發者更多。他們中的大多數完全不知道維護長嵌套if-else語句或切換案例的成本。我完全理解你的問題,在你的案例中應用名爲「用多態性替代條件」的解決方案。只要已經選擇了對象,您就會成功注意到多態性的作用。在這個階段也有人說,這個問題可以歸結爲關鍵[關鍵] - > [類]。這裏是例如解決方案的AS3實現。

private var _mapping:Dictionary; 
private function map():void 
{ 
    _mapping["view"] = new ViewAction(); 
    _mapping["edit"] = new EditAction(); 
    _mapping["sort"] = new SortAction(); 
} 

private function getAction(key:String):BaseAction 
{ 
    return _mapping[key] as BaseAction; 
} 

運行,將你喜歡:

public function run(action:String):void 
{ 
    var selectedAction:BaseAction = _mapping[action]; 
    selectedAction.apply(); 
} 

在ActionScript3中有一種叫做getDefinitionByName全局函數(鍵:字符串)類。這個想法是使用您的鍵值來匹配表示解決方案的類的名稱與您的條件。在你的情況下,您需要將「視圖」更改爲「ViewAction」,「編輯」更改爲「EditAction」,然後「排序」更改爲「SortAtion」。無需使用查找表記住任何內容。該功能運行將是這樣的:

public function run(action:Script):void 
{ 
    var class:Class = getDefintionByName(action); 
    var selectedAction:BaseAction = new class(); 
    selectedAction.apply(); 
} 

不幸的是你失去編譯該解決方案檢查,但你得到的靈活性,增加了新的行動。如果你創建一個新的密鑰,你唯一需要做的就是創建一個適當的類來處理它。

即使你不同意我的意見,也請留下評論。

7

即使最後答案是一年前,我想作一些評論在這個題目/評論。

答案審查

我@CarlManaster同意關於編碼switch聲明一次,以避免所有處理重複代碼的衆所周知的問題,在這種情況下,涉及條件語句(其中一些由@thkala提及)。

我不相信通過@KonradSzałwiński或@AlexanderKogtenkov提出的做法符合這一情況的原因有兩個:

首先,從你描述的問題,你並不需要動態改變之間的映射動作的名稱和處理動作的實例。

注意這些解決方案允許這樣做(通過簡單地指定動作名稱到一個新的操作實例),而靜態的,基於交換的解決方案不(映射被硬編碼)。 此外,如果不應該執行某個操作(switch語句的default部分),您仍然需要一個條件來檢查映射表中是否定義了給定的鍵。

其次,在這個特殊的例子,dictionaries真的隱藏switch語句的實現。更重要的是,使用default子句讀取/理解switch語句可能會更容易,而不是必須從思維上執行從映射表返回處理對象的代碼,包括處理未定義的鍵。

有你就可以擺脫所有條件句的方式,包括switch語句:

卸下switch語句(不使用條件語句的話)

如何創建正確的行動目標行動名稱?

我會在語言無關所以這個答案並沒有得到長,但關鍵是要實現類是對象太

如果您已經定義了一個多態層次結構,那麼引用BaseAction的具體子類是沒有意義的:爲什麼不要求它通過名稱返回正確的實例?

這通常是你寫的switch語句(比方說,一個工廠方法)來實現...但這又如何:

public class BaseAction { 

    //I'm using this notation to write a class method 
    public static handlingByName(anActionName) { 
     subclasses = this.concreteSubclasses() 

     handlingClass = subclasses.detect(x => x.handlesByName(anActionName)); 

     return new handlingClass(); 
    } 
} 

那麼,這是什麼方法呢?

首先,檢索這個的所有具體子類(它指向BaseAction)。在你的例子中,你會收到一個集合ViewAction,EditActionSortAction

請注意,我說具體的子類,不是所有的子類。如果層次更深,具體的子類將始終是層次結構(葉)底部的子類。那是因爲他們是唯一不應該抽象並提供真正實施的人。

其次,獲取第一個子類,它回答它是否可以通過名稱處理動作(我使用的是lambda/closure風格的表示法)。該handlesByName類方法的ViewAction示例實現會是什麼樣子:

public static class ViewAction { 

    public static bool handlesByName(anActionName) { 
     return anActionName == 'view' 
    } 

} 

第三,我們發送新到處理的動作,有效地創建它的一個實例類的消息。

當然,你必須處理這個情況,即任何子類都沒有通過它的名字來處理動作。包括Smalltalk和Ruby在內的許多編程語言允許將detect方法傳遞給第二個lambda/closure,只有在沒有任何子類匹配條件時纔會進行評估。 另外,你將不得不處理不止一個子類通過其名稱處理動作的情況(可能這些方法之一是以錯誤的方式編碼的)。

結論這種方法的

一個優勢是,新的行動可以通過書面形式(而不是修改)現有代碼的支持:只要創建的BaseAction一個新的子類,並正確地實現handlesByName類方法。它有效地支持通過添加新概念來添加新功能,而無需修改現有的功能。很顯然,如果新功能需要將新的多態方法添加到層次結構中,則需要進行更改。另外,您可以使用您的系統反饋提供開發人員:「所提供的操作不由任何BaseAction的子類處理,請創建一個新的子類並實現抽象方法」。對我而言,模型本身告訴你什麼是錯的事實(而不是試圖在思維上執行查找表)爲增加價值和明確指示必須完成的工作提供了指導。

是的,這聽起來可能是過度設計。請保持開放的態度,並意識到解決方案是否過度設計必須與您使用的特定編程語言的開發文化相結合。例如,.NET人可能不會使用它,因爲.NET不允許您將類視爲真實對象,而另一方面,該解決方案在Smalltalk/Ruby文化中使用。

最後,使用常識和口味來事先確定一個特定的技術是否真正解決了您的問題,然後再使用它。這很誘人,但是應該評估所有的權衡(文化,開發者的資歷,抗拒變化,開放的態度等)。

0

您可以將字符串和相應的操作類型存儲在哈希映射中的某處。

public abstract class BaseAction 
{ 
    public abstract void doSomething(); 
} 

public class ViewAction : BaseAction 
{ 
    public override void doSomething() { // perform a view action here... } 
} 

public class EditAction : BaseAction 
{ 
    public override void doSomething() { // perform an edit action here... } 
} 

public class SortAction : BaseAction 
{ 
    public override void doSomething() { // perform a sort action here... } 
} 


string action = "view"; // suppose user can pass either 
         // "view", "edit", or "sort" strings to you. 
BaseAction theAction = null; 

theAction = actionMap.get(action); // decide at runtime, no conditions 
theAction.doSomething(); 
相關問題