2012-11-01 47 views
7

我有一個C背景,並且是C++上的一個newb。我有一個基本的設計問題。我有一個類(我稱之爲「廚師」 B/C我有這個問題似乎很類似於此,無論是在複雜性和問題的方面),基本上就像在僞代碼這個C++重構怪物類的幫助

class chef 
    { 
    public: 
      void prep(); 
      void cook(); 
      void plate(); 

    private: 
      char name; 
      char dish_responsible_for; 
      int shift_working; 
      etc... 
    } 

,這得到實施沿線:

int main{ 
    chef my_chef; 
    kitchen_class kitchen; 
    for (day=0; day < 365; day++) 
     { 
     kitchen.opens(); 
     .... 

     my_chef.prep(); 
     my_chef.cook(); 
     my_chef.plate(); 

     .... 

     kitchen.closes(); 
     } 
    } 

這裏的廚師類似乎是一個怪物類,並有可能成爲一個。廚師也似乎違反了單一職責原則,所以我們反而應該是這樣:

class employee 
    { 
    protected: 
     char name; 
     int shift_working; 
    } 

    class kitchen_worker : employee 
    { 
    protected: 
     dish_responsible_for; 
    } 

    class cook_food : kitchen_worker 
    { 
    public: 
     void cook(); 
     etc... 
    } 
    class prep_food : kitchen_worker 
    { 
    public: 
     void prep(); 
     etc... 
    } 

 class plater : kitchen_worker 
    { 
    public: 
     void plate(); 
    } 

等等

我承認仍然如何掙扎在運行時執行它,以便例如如果打印機(或「以打電話的廚師」的身份)決定通過晚餐服務中途回家,那麼廚師必須進行新的轉換。

這似乎與我有一個更廣泛的問題有關,如果同一個人在這個例子中總是做準備,烹飪和電鍍,那麼讓這個層次的層次結構模擬單個廚師的真正實際優勢是什麼呢?我認爲這會導致「擔心增加班級」的事情,但同時,現在或者在可預見的將來,我認爲維持廚師班的整體工作是非常麻煩的。我還認爲,對於一個天真的代碼閱讀者來說,在廚師對象中看到三種不同的方法並繼續前進,這對於真正意義上的事情來說更加容易。

我的理解是,如果我們添加諸如「cut_onions()」,「cut_carrots()」等方法,可能會威脅到變得笨拙......也許每個方法都有自己的數據,但似乎可以處理這些方法通過使prep()函數更加模塊化。此外,似乎SRP採用其合乎邏輯的結論會創建一個「onion_cutters」「carrot_cutters」等類別......我仍然很難看到它的價值,因爲程序必須確保同一位員工切割洋蔥和胡蘿蔔,這有助於保持各種方法的狀態變化相同(例如,如果員工切割洋蔥的手指,他不再有資格切胡蘿蔔),而在怪物對象廚師類中,似乎所有這些都得到了照顧。

當然,我明白,這並不意味着有一個有意義的「面向對象的設計」,但在我看來,如果我們必須爲每個廚師的任務有不同的對象(這看起來不自然,同一個人正在完成所有三個功能),那麼似乎在概念模型上優先考慮軟件設計。如果我們想擁有可能是不同的人的「meat_chef」「sous_chef」「three_star_chef」,我覺得面向對象的設計在這裏很有幫助。此外,與運行時問題有關的是,在單一責任原則的嚴格應用下,似乎存在複雜性的開銷,必須確保構成基類員工的基礎數據得到改變,並且這種改變是反映在隨後的時間步驟中。

因此,我很想留下它或多或少的原樣。如果有人能夠澄清爲什麼這是一個壞主意(如果你有關於如何最好地繼續進行的建議),我會非常感激。

+0

有時映射現實世界中的角色/職責/任務的代碼對象是行不通的。也許你需要一些通用的功能,採取一個人和一個行動。該功能可讓人員應用該操作。每個類接口上的 –

+0

模塊可能會給你更多的線索?像電影機能做什麼? cook_food可以做什麼?他們需要繼承還是僅僅是一個技能(函數調用)? – billz

+0

看看構圖方法。或者,也許你需要一個狀態模式? –

回答

3

爲了避免現在和未來濫用職業女性,只有當存在關係時,才應該使用它。作爲你自己,「廚師是廚師」。它在現實生活中顯然沒有意義,也不在代碼中。 「cook_food」是一個動作,所以創建一個動作類和子類可能是有意義的。

有一個新的類只是爲了添加新的方法,如cook()prep()對原始問題不是一個真正的改進 - 因爲您所做的全部都是將該方法包裝在類中。你真正想要做的是做一個抽象來做這些動作 - 所以回到動作類。

class action { 
    public: 
     virtual void perform_action()=0; 
} 

class cook_food : public action { 
    public: 
     virtual void perform_action() { 
      //do cooking; 
     } 
} 

然後可以給廚師一個按您指定的順序執行的操作列表。比如說,一個隊列。

class chef { 
    ... 
     perform_actions(queue<action>& actions) { 
      for (action &a : actions) { 
       a.perform_action(); 
      } 
     } 
    ... 
} 

這通常被稱爲Strategy Pattern。它通過允許您在不修改現有類的情況下添加新操作來促進開放/關閉原則。


你可以使用另一種方法是Template Method,在那裏你指定的抽象的一系列步驟,並使用子類實現爲每一個具體行爲。

class dish_maker { 
    protected: 
     virtual void prep() = 0; 
     virtual void cook() = 0; 
     virtual void plate() = 0; 

    public: 
     void make_dish() { 
      prep(); 
      cook(); 
      plate(); 
     } 
} 

class onion_soup_dish_maker : public dish_maker { 
    protected: 
     virtual void prep() { ... } 
     virtual void cook() { ... } 
     virtual void plate() { ... } 
} 

另一個密切相關的模式,這可能是適合這是Builder Pattern

這些模式也可以減少Sequential Coupling反模式,因爲這一切都太容易忘記調用一些方法,或致電他們以正確的順序,特別是如果你多次做這些。你也可以考慮將你的kitchen.opens()和closes()放入類似的模板方法中,而不必擔心被調用的closes()。


論onion_cutter和carrot_cutter創建單獨的類,這是不是真正的SRP的合乎邏輯的結論,但實際上它違反了 - 因爲你讓它們負責逃課,和控股一些關於他們正在切割的信息。切割洋蔥和胡蘿蔔都可以抽象爲一個切割動作 - 並且您可以指定要切割的對象,並且如果您需要每個對象的特定代碼,則可以爲每個單獨的類添加重定向。

一步就是創建一個抽象來說可切割的東西。 關係子類化是候選人,因爲胡蘿蔔可裁剪。

class cuttable { 
    public: 
     virtual void cut()=0; 
} 

class carrot : public cuttable { 
    public: 
     virtual void cut() { 
      //specific code for cutting a carrot; 
     } 
} 

切割動作可以採取可切割的對象,並執行任何常見的切割作用這是適用於所有cuttables,也可以將各個對象的具體切的行爲。

class cutting_action : public action { 
    private: 
     cuttable* object; 
    public: 
     cutting_action(cuttable* obj) : object(obj) { } 
     virtual void perform_action() { 
      //common cutting code 
      object->cut(); //specific cutting code 
     } 
} 

+0

謝謝你。我打算跟進一個問題。因此,如果我們遵循策略模式方法,可以說每個廚師都有cook_food()和plate_dish()類中使用的許多數據成員(例如tasting_spoon,salt_shaker等)。這似乎是廚師需要cook_food()和cook_food()需要廚師。如何避免說cook_food()和類廚師之間的循環依賴關係,而不必爲cook_food()類提供大量參數?從設計的角度來看,如果這些課程是高度耦合的,對它們進行分離沒有缺點嗎? – user1790399

+0

避免循環依賴的一種方法是創建cook_food類可以使用的廚師界面,以及實際的廚師類可以實現的界面。只要界面只說明cook_food瞭解廚師所需的細節,就沒有問題。 (或者你可以反過來做,併爲廚師製作烹飪界面)。沒有單一的正確方法,但將每個任務分離到自己的課堂當然是有好處的。 –

+0

對不起,一個類能夠使用一個接口意味着什麼?這是否意味着如果我們使用廚師類的接口(例如,cook_food(chef-> interface_for_cooking()))與interface_for_cooking()組成,如get_salt_shaker(),get_tasting_spoon()等調用,就可以這樣做。 。?爲了跟隨這個例子,爲cook_food()和plate_dish()類提供兩個單獨的接口是不是一個好主意? – user1790399