2010-08-03 34 views
6

Điều này tương tự như một trong những câu hỏi khác của tôi, nhưng đủ khác nhau tôi nghĩ để đảm bảo một câu hỏi mới.Câu hỏi về các nhà máy trừu tượng và tiêm

Về cơ bản tôi đang viết giao diện người dùng và giao diện người dùng của tôi có các nút có thể được chọn. Khi một nút được chọn, giao diện người dùng kết thúc với một lớp cơ sở nút trừu tượng "INode". Từ đây tôi nhận được một nhà máy bằng cách làm node-> getFactory(), và từ đây tôi có thể tạo các hộp thoại hoặc khung nhìn thích hợp cho nút đó vì nhà máy chính xác được trả về bởi nút cụ thể (ví dụ factory-> createAddDialog(), factory- > createView (nút), v.v.).

Câu hỏi của tôi là cố gắng tìm cách tốt nhất để nhà máy đó truy cập vào nút ở địa điểm đầu tiên.

Cho đến nay tôi đã nghĩ đến 3 cách:

1) Tiêm nhà máy đúng khi tôi tạo nút:

AreaNode *node = new AreaNode(new AreaNodeFactory()); 

Vì vậy, định nghĩa của AreaNode là:

AreaNode : public INode 
{ 
    AreaNode(INodeAbstractFactory *injectedFactory) 
    { 
     m_injectedFactory = injectedFactory; 
    } 

    INodeAbstractFactory* getFactory() 
    { 
     return m_injectedFactory; 
    } 

    INodeAbstractFactory* m_injectedFactory; 
}; 

2) Tiêm một nhà máy tổng quát hơn và cho phép nút lấy nhà máy từ nhà máy đó:

AreaNode : public INode 
{ 
    AreaNode(IFactory *injectedFactory) 
    { 
     m_injectedFactory = injectedFactory; 
    } 

    INodeAbstractFactory* getFactory() 
    { 
     return m_injectedFactory->getAreaNodeFactory(); 
    } 

    IFactory* m_injectedFactory; 
} 

3) Chỉ cần tạo các nhà máy bê tông (mặc dù điều này loại bỏ các phạm vi cho việc sử dụng các nhà máy khác nhau cho cùng một nút có lẽ để thử nghiệm hay cho những thay đổi sau):

AreaNode : public INode 
{ 
    INodeAbstractFactory* getFactory() 
    { 
     return new AreaNodeFactory(); 
    } 
} 

suy nghĩ hiện nay về các tùy chọn này:

Tùy chọn 1: Có thể là một chút lộn xộn - Tôi phải đảm bảo rằng tôi luôn cung cấp cho nhà máy chính xác loại đó, hoặc có thể tôi chỉ sử dụng một nhà máy khác để bơm đúng nhà máy cho tôi.

Tùy chọn 2: Buộc nút biết về triển khai nhà máy trừu tượng đủ để có thể gọi getAreaNodeFactory, điều này có thể không phải là điều xấu. Nó ít nhất giúp đảm bảo nhà máy chính xác/giống nhau sẽ luôn được tìm nạp (giả sử nhà máy tổng quát hơn được triển khai đúng cách).

Tùy chọn 3: Điều này có chút hạn chế vì tôi sẽ không thể trao đổi lớp học và tôi không quan tâm đến nút phải biết về việc triển khai cụ thể của nhà máy - mặc dù trong trường hợp này có thể không phải là quá nhiều của một vấn đề (từ cuối cùng nổi tiếng!).

Bất kỳ suy nghĩ nào về điều này?

Cảm ơn.

EDIT: Rất tiếc, đã bỏ lỡ các khai báo biến trong bài đăng gốc, đã sửa.

EDIT: Một vấn đề khác với tùy chọn 2 là tôi phải triển khai "getFactory" trong mọi loại nút. Ít nhất với tùy chọn 1, lớp cơ sở chỉ có thể trả lại lớp nhà máy trừu tượng tiêm mỗi lần ..

+0

Tôi thậm chí không chắc chắn các nút nên tham khảo các nhà máy. Giao diện người dùng nên tham khảo một nhà máy trừu tượng, và nó nên sử dụng đa hình để đảm bảo nhà máy bê tông chính xác được sử dụng, tùy thuộc vào loại nút được truyền vào các phương thức tạo. Ngoài ra tôi cho rằng tùy chọn 1 trông ok. Nhưng tôi vẫn tin rằng các nút không nên biết, hoặc giữ một tham chiếu đến bất kỳ nhà máy nào. – Kurt

+0

@Kurt: trong sự vắng mặt của công văn kép, và tránh việc sử dụng một lớp khách truy cập cho mỗi giao diện người dùng "hành động" tôi muốn thực hiện trên nút tùy thuộc vào loại nút, những gì có thể là một giải pháp tốt hơn? Các loại hành động tôi muốn thực hiện trên một nút là dọc theo dòng: createAddDialog (parentWidget), createView (parentWidget), createMiniView (parentWidget), vv Tôi chỉ có thể đặt các phương thức bên trong các lớp nút cụ thể, tức là areaNode -> createView (cha mẹ) - nhưng điều đó không làm được gì nhiều cho việc chia tách. – Mark

+0

Mặc dù tôi đang cố gắng tránh sử dụng hàng loạt mẫu khách truy cập, cách duy nhất tôi có thể thấy để giữ nút hoàn toàn khỏi giao diện người dùng, là sử dụng một khách truy cập để giải quyết lớp nhà máy phù hợp và sau đó tôi có thể tạo các thành phần giao diện người dùng của mình .. – Mark

Trả lời

2

Làm thế nào về

class FactoryBase { /* interface */ } 
template <typename NodeType> class Factory : public FactoryBase { 
    // Default implementation. 
} 
// Add appropriate specializations for all relevant nodetypes, if needed. 
template <typename NodeType> inline Factory<NodeType> getFactory(NodeType*) { 
    return Factory<NodeType>(); 
} 

class AreaNode : public Node { 
    FactoryBase getFactory() { return getFactory(this); } 
}; 

Template trích lập luận sẽ đảm bảo rằng các loại nhà máy có nguồn gốc từ this. Điều này sẽ ngăn chặn các lỗi thủ công ở đó. Nhưng nói chung, tôi sẽ không bận tâm với công khai phơi bày một nhà máy ở tất cả. Chỉ cần thêm các bit sau vào lớp cơ sở:

class Node { 
public: 
    std::Auto_ptr<View> createView() { 
    this->getFactory()->createView(this); 
    } // etc. 
private: 
    virtual FactoryBase* getFactory() = 0; 
}; 
+0

Tôi đã cố gắng giữ cho các nút không có các phương thức như vậy - dường như có một chút sai lầm đối với các nút "biết" về các phương thức như "createView", nhưng có lẽ tôi sai. – Mark

+0

Các loại nút riêng lẻ sẽ không. Họ chỉ tham khảo các nhà máy. Các chi tiết API được giới hạn trong lớp cơ sở, và ở đó nó tồn tại để chuyển 'this' đến phương thức factory. – MSalters

+0

Được rồi, tôi hiểu ý của bạn là gì - mặt khác có nhận xét của Kurt ở trên rằng các nút không nên tham chiếu đến bất kỳ nhà máy nào - vì vậy tôi lại bị rách. Ban đầu tôi sử dụng khách truy cập để thực hiện công văn kép, nhưng nó chỉ có quá cồng kềnh để sử dụng chúng cho mỗi hành động, và họ đã rất lớn do số lượng các loại nút. – Mark

2

Làm thế nào về.

template<typename Factory> 
class AreaNode : public INode 
{ 
public: 
     virtual ~AreaNode(){} 

     AreaNode() : pFactory_(new Factory()) 
     {   
     } 

     const shared_ptr<IFactory>& GetFactory() 
     { 
      return pFactory_; 
     } 
private:   
     shared_ptr<IFactory> pFactory_; 
}; 

EDIT:

Hoặc tùy thuộc vào hoàn cảnh của bạn.

template<typename Factory> 
class Node 
{ 
public: 
     virtual ~Node(){} 

     Node() : pFactory_(new Factory()) 
     {   
     } 

     const shared_ptr<IFactory>& GetFactory() 
     { 
      return pFactory_; 
     } 
private:   
     shared_ptr<IFactory> pFactory_; 
}; 

class AreaNode : public Node<AreaNodeFactory> 
{ 
    // Implementation 
}; 

// OR 

typedef Node<AreaNodeFactory> AreaNode; 
+0

ý tưởng hay - mặc dù bạn nghĩ gì về toàn bộ ý tưởng về các nút thậm chí biết về các nhà máy? Kurt ở trên nghĩ rằng đó là một ý tưởng tồi, tuy nhiên tôi không thể nghĩ ra một cách gọn gàng xung quanh các nút biết ít nhất * một cái gì đó * mà không cần đến mô hình khách truy cập. – Mark

+0

Không có ví dụ nào thực sự hiệu quả. Trong trường hợp đầu tiên, bạn kết thúc với một loại AreaNode riêng biệt cho mỗi Nhà máy - loại Nhà máy phải là nội bộ và không cần phải được loại tiếp xúc với loại của lớp đó. Trong trường hợp thứ hai, không có phiên bản Node nào chia sẻ một lớp cơ sở chung. –

+0

Ví dụ đầu tiên có thể được cải thiện bằng cách chỉ làm cho các nhà xây dựng templated - đó là phần duy nhất của lớp cần biết loại nhà máy. –

1

Phụ thuộc vào mục đích của "nút" đối tượng

Nếu một nhà máy nên tiêm vào một nút và nút bê tông không nhất thiết phải làm bất cứ điều gì với loại bê tông của nhà máy sau đó tất cả các mã liên quan đến nhà máy có thể được chuyển vào lớp nút cơ sở đơn giản hóa mọi thứ.

class INode //well, it's not "interface" in clear sense 
{ 
public: 
    void setFactory(Factory* f) { this->f = f; } 
    Factory* getFactory() const { return f; } 
private: 
    Factory* f; 
}; 

Bây giờ, chức năng của nút bê tông và nhà máy bê tông được thực hiện trực giao với nhau và có thể được kết hợp tự do theo bất kỳ cách nào.

Nếu một loại bê tông của nút được liên kết với một loại nhà máy cụ thể, có thể bạn nên tránh các nhà máy ở tất cả và tạo chế độ xem trực tiếp với nút :: phương thức createView. Phụ thuộc vào việc các nhà máy có được sử dụng trong một số ngữ cảnh khác hay không.

Cũng xem xét việc này (không phải là rất cách OOP-ish):

typedef boost::function0<Factory*> Node; 
std::map<UIItem*,Node> nodes; 
nodes[area_item1] = &getAreaFactory; 
nodes[area_item2] = &getAreaFactory; 
nodes[region_item1] = &getRegionFactory; 
... 
void OnSelect(UIItem* i) 
{ 
    ... 
    View* v = nodes[i]()->createView(); 
    ... 
} 

Có lẽ, nó phù hợp với mọi nhu cầu của bạn)

Các vấn đề liên quan