2009-09-16 107 views
2

下面是我設計的問題描述。有一個類A(單例),用於創建和維護類B的對象。但是有這樣的場景,如果某個特定的條件碰到類B的對象,它必須創建另一個類B的對象。但是我需要創建對象由A級C++設計問題



class B; 
class A { 

    private: 
      A(); 
      class A *ptr; 
    public: 
     A & GetAInstance() 
     { 
      if (!ptr) 
       ptr = new A; 
      return ptr; 
     } 
     void CreateBInstances(std::string name) 
     { 
       map[name] = new B(name); 
     } 
}; 

Class B { 
     public: 
      B(std::string name) { } 
     public: 
      void checkCondition() 
      { 
       if (condition == true) 
       { 
        // So here the contidition is hit, I want to create 
        // another object of class B, but I want to intimate 
        // class A to do this job for me 
       } 
      } 
}; 
 

做,我想了解和尋找更好的方法來做到這一點骯髒的工作。提前致謝。

+2

爲什麼不能簡單地調用從B A :: CreateBInstances ::校驗條件? – Ozan 2009-09-16 04:13:15

+0

如果類A是一個Singleton,爲什麼你不能只獲得單個實例並再次使用CreateBInstance? – 2009-09-16 04:13:58

+0

這些建議忽略了基本的軟件依賴管理 - 它們在兩個類之間創建循環依賴關係。 – SingleShot 2009-09-16 04:20:41

回答

5

A :: GetAInstance應該是靜態的。

那麼你應該能夠只是做以下

if (condition) 
{ 
    A::GetAInstance().CreateBInstance(name); 
} 
+1

如果你爲我工作,我不會讓你這樣做。糟糕的設計。 – SingleShot 2009-09-16 05:01:37

+3

同意了,但不知道什麼Ganesh的代碼實際上做... – Martin 2009-09-16 05:11:59

+0

你爲什麼說這是不好的設計?如果A是單例,那麼實例應該是靜態的,CreateBinstance可以完全是靜態函數。 – piotr 2009-09-16 10:07:04

1

東西不聞說得對整個設計,但其很難說有什麼,而不必提供更多的細節。無論如何,你可以做這樣的事情可避免循環依賴,但仍然有B呼叫了答:

class BRegistry 
{ 
    virtual void CreateBInstances(std::string& name) = 0; 
    ... 
}; 

class A : public BRegistry 
{ 
    public: 

     virtual void CreateBInstances(std::string& name) { 
     map[name] = new B(name, *this); 
     } 

     // Singleton stuff... 

     ... 
}; 

class B 
{ 
    public: 

     B (std::string& name, BRegistry& registry) { 
     ... 
     } 

     void checkCondition() 
     { 
     if (condition == true) 
     { 
      registry.CreateBInstances(name); 
     } 
     } 

     ... 
}; 

基本上,我提取的適配,並有B之間使用的接口。 A在創建時傳遞給B,但從B的角度來看,它是接口。沒有周期。

只是一個評論。當我看到辛格爾頓的時候有99%的時候,這是一種不恰當的使用模式。通常它被誤用爲一個方便的全局變量,就像所有跳轉到B中使用它的評論所展示的那樣。

+0

你只是隱藏了循環依賴關係,它並沒有完全消失。 B仍然叫A創造B,你只是難以看清。語義問題不能用一些句法雜耍來解決。解釋你:如果你爲我工作,我不會讓你這樣做。我寧願沒有那麼好看的設計像宇宙小姐臉頰上的皮條客一樣脫穎而出,隱藏在幾層睫毛膏後面 – Pieter 2009-09-16 12:11:45

+0

用UML繪製它並按照箭頭。沒有依賴週期。 – SingleShot 2009-09-16 16:07:28

+0

@SingleShot正如我在上面的評論中提到的那樣,依賴性依然存在,它只是簡化爲對抽象的依賴。爲了說明,B有一個**使用依賴於BRegistry,BRegistry對B有一個**創建**依賴,這顯然是一個循環。 – 2009-09-16 22:31:42

2

當在實現機制中應用時,大部分有關循環依賴的鬆散都會混淆,而不是在程序包或模塊級別,還有一些來自Java測試框架的缺陷。由於Java對模因的影響,解決方案的表達方式與C++相反。目前還不清楚你是否試圖去除一個具體的循環依賴(代碼不能用C++編譯)或者有形而上的異議。

去耦混凝土循環依賴

一個成語在C++標準庫allocators被發現。

當你聲明的列表,以便:

std::list < int, my_allocator <int> > 

分配器有一個嵌套結構,它允許訪問不同的專業化的原始模板,以便在std::list實現可分配節點對象,而不僅僅是整數。

假設你有以下要求:

  • 註冊表是全局的程序(即你不需要每個對象類型不止一個註冊表,否則你需要像工廠模式SingleShot建議,儘管在C++中,你通常會使用模板而不是虛擬函數多態);因此我傾向於使用靜態工廠而不是單身。B類的
  • 對象應該只通過調用A::CreateInstance(name)
  • A作爲一個註冊表,所以反覆調用創建實例具有相同名稱返回相同的對象
  • 代碼正確編譯沒有具體的循環引用創建
  • 可以替換用於測試的註冊表或註冊類型的類型

此全局註冊表不需要任何關於它創建的類型的知識,除了它們提供了一個常量ructor採取參考一常量的std :: string:

#include <string> 
#include <map> 

template < class T = int > 
class Registry { 
    static std::map<std::string,T*> map; 

    public: 

    static T& CreateInstance (const std::string& name) { 
     typename std::map< std::string, T* >::iterator it = map.find (name); 

     if (it == map.end()) 
      return * (map [ name ] = new T (name)); 
     else 
      return *it->second; 
    } 

    public: 

    template < typename U > 
    struct rebind { 
     typedef Registry<U> other; 
    }; 
}; 

template < class T > 
std::map < std::string, T* > Registry<T>::map; 

相應的登記對象提供私有構造和具有的CreateInstance功能的朋友:

template < typename R = class Registry<> > 
class Registered { 
     typedef typename R::template rebind< Registered <R> > ::other RR; 

    private: 
     friend Registered<R>& RR::CreateInstance (const std::string& name); 

     explicit Registered (const std::string& name) { 
      // ... 
     } 

     Registered (const Registered<R>&) ; // no implementation 

    public: 
     void checkCondition() 
     { 
      bool condition = 7 > 5; 

      if (condition) 
      { 
       RR::CreateInstance ("whatever"); 
      } 
     } 

    // ... 
}; 

因爲rebind::other成語,你不必編寫Registered<Registry<Registered<Registry ...並避免具體的循環依賴。因爲默認Registry<int>從未實際使用,除了提供rebind,它沒有實例化,因此不報告,您可以不使用new int (name)構建一個int錯誤。

然後,您可以使用類型爲你的B和A:

typedef Registered<> B; 
typedef Registry<B> A; 

int main() { 
    B& b1 = A::CreateInstance("one"); // create a B 

    b1.checkCondition(); // uses A to create object internally 

    B b2("two"); // compile error - can only create B using A 

    return 0; 
} 

當然你也可以建立一個Registered<MyMockRegistry>進行測試,其他主要反對循環依賴類型。