2012-09-04 75 views
6

所以我決定使用工廠設計模式和依賴注入。依賴注入/繼承設計模式的構造器參數過多

class ClassA 
{   
    Object *a, *b, *c; 
    public: 
    ClassA(Object *a, Object *b, Object *c) : 
    a(a), b(b), c(c) {} 
}; 

class ClassB : public ClassA 
{   
    Object *d, *e, *f; 
    public: 
    ClassB(Object *a, Object *b, Object *c, Object *d, Object *e, Object *f) : 
    ClassA(a, b, c), d(d), e(e), f(f) {} 
}; 

現在,問題是classB對構造函數的參數太多。這是一個單獨的繼承層示例,但是當繼承層開始變得更深,並且每個圖層類需要構建更多的對象時,頂層的構造函數最終需要太多參數才能創建!

我知道我可以使用setter而不是構造函數,但是有沒有其他方法?

+12

我的一般方法是儘可能地避免繼承,並儘量讓每個類都集中在一個責任上。你可以單獨構造'ClassA',然後初始化'ClassB'並引用它,而不是通過繼承將它們緊密耦合? –

+2

Mike Seymour是對的。優先考慮具有更緊密耦合的is-a關係(繼承)的關係(組合)。也許如果你解釋你想做什麼,我們可以告訴你如何更好地完成它? – metal

+2

我沒有看到任何與繼承有關的主要問題。它應該在需要的地方使用。它不應該像其他一切一樣被濫用。 –

回答

1

這是C++問題之一(如果這可以稱爲問題)。除了試圖保持ctor的參數數量最小之外,它沒有解決方案。

的方法之一是使用道具結構喜歡:

struct PropsA 
{ 
    Object *a, *b, *c; 
}; 

class ClassA 
{ 
    ClassA(PropsA &props, ... other params); 
}; 

這似乎是顯而易見的,但我沒有用幾次。在很多情況下,事實證明一些參數是相關的。在這種情況下,爲它們定義一個結構是有意義的。

這種最糟糕的噩夢是薄包裝類。基地的方法和數據字段可以直接訪問,而所有的土地必須被複制。當有10多個Ctors時,創建一個包裝器開始成爲一個問題。

+0

在大多數情況下,通過向其中推入相關方法來使「PropsA」成爲一個完整的類是有意義的。否則,你最終會得到一個不太面向對象的數據結構。 – casablanca

+1

我把應用程序的要求,開發速度和代碼FAR FAR的清晰度放在了「面向對象」的花哨事物之前。 –

+1

OOP不是「幻想」,但它是您選擇或離開的選擇。我很滿意某人只是以程序化的方式來使用結構和函數,但如果你打算使用類,那就付出努力去做正確的事情,否則你最終會陷入一團糟,其中一半的代碼是面向對象的,其餘的是程序化的 - 我認爲這不會導致很明確。 – casablanca

6

安裝程序不建議這樣的事情,因爲它們導致部分構造的對象,這是非常容易出錯。構建需要許多參數的對象的常見模式是使用構建器。 ClassBBuilder的責任是創建ClassB對象。您將ClassB構造函數設爲私有,並只允許構建器使用朋友關係對其進行調用。現在,製造商可以看看在某種程度上是這樣

ClassBBuilder { 
    public: 
    ClassBBuilder& setPhoneNumber(const string&); 
    ClassBBuilder& setName(consg string&); 
    ClassBBuilder& setSurname(const string&); 
    ClassB* build(); 
} 

而且使用Builder喜歡這樣的:

ClassB* b = ClassBBuilder().setName('alice').setSurname('Smith').build(); 

build()方法所需的所有參數設置檢查,它要麼返回正確構造的對象或空值。創建部分構建的對象是不可能的。你仍然有一個有很多參數的構造函數,但它是私有的,只在一個地方被調用。客戶不會看到它。 Builder方法也很好地記錄了每個參數的含義(當你看到ClassB('foo','bar')時,你需要檢查構造函數來確定哪個參數是一個名稱,哪個參數是姓氏)​​。

+0

這可能比setters更好一些,但你有效地延遲檢查是否有足夠的信息來構造運行時對象。我不認爲這是一個簡單的構造函數的幾個參數的改進。 –

+0

如果您想要編譯時間執行,您仍然可以從構建器中獲益。當類構造函數使用默認值取多個可選參數時,就是這種情況。然後,可以使用構建器的setter來設置這些可選參數,並使build()方法獲取所有必需的參數。這會讓你編譯時執行所有必需的參數都被傳遞,但是比調用一個構造函數更具可讀性和更少錯誤傾向,這個構造函數需要很多參數,其中一些參數需要一些參數,這些參數可以使用默認值。 –

+1

我喜歡這種方法。構建該對象的客戶端的代碼更清晰。雖然確實會丟失編譯時檢查,但build()函數仍然允許在代碼中進行驗證檢查。 –

1

我想你所描述的是在C++中的一個問題 - 事實上,C++反映了你的設計表現相當不錯的依賴關係:

  1. 構建ClassA類型的對象,你需要有三個Object實例(a,bc)。
  2. 要構建類型爲ClassB的對象,還需要有三個Object實例(d,ef)。
  3. 類型ClassB的每個對象都可以視爲類型爲ClassA的對象。

這意味着,用於構造類型的對象ClassB需要提供其所需ClassA接口的實現3個Object對象,然後另一三個用於ClassB接口的實現。

我相信實際問題在這裏是你的設計。你可以考慮不同的方法來解決此問題:

  1. 不要讓ClassB繼承ClassA。取決於您是否需要對任一類型的對象進行同質訪問(例如,因爲您有一個集合ClassA*,並且此集合還可能包含指向ClassB的指針),可能會有也可能不是選項。
  2. 尋找總是一起出現的物體。就像 - 也許前兩個對象傳遞給構造函數(abde)代表某種配對。也許一個對象標識符或類似的東西?在這種情況下,爲此可能會引入專用摘要(讀取:類型)。