2012-11-08 39 views
4

在基类中创建派生类我的问题与Need a design pattern to remove enums and switch statement in object creation的问题差不多一样但是我没有看到抽象工厂模式在这里很合适。根据参数

我目前正在计划重构/重新实现一些现有的DAL/ORM混合库。某处在现有的代码有代码看起来像这样:

class Base 
{ 
    static Base * create(struct Databasevalues dbValues) 
    { 
    switch(dbValues.ObjectType) 
    { 
    case typeA: 
     return new DerivedA(dbValues); 
     break; 
    case typeB: 
     return new DerivedB(dbValues); 
     break; 
    } 
    } 
} 

class DerivedA : public Base 
{ 
    // ... 
} 

class DerivedB : public Base 
{ 
    // ... 
} 

所以负责数据库的通信库填充一个结构与数据库有关的实体的所有信息,然后在上面创建()方法被调用,以实际创建ORM中的相应对象。 但我不喜欢基类知道所有派生类的想法,我也不喜欢switch语句。我也想避免创建另一个类来创建这些对象。你对目前的方法有什么看法?你将如何实现这个功能?

回答

1

不管你做什么,你都需要switch-case或其他一些只隐藏类似逻辑的构造。

但是,您可以并且应该做的是从您的Base中删除创建方法 - 您完全正确,它不应该知道它的派生方法。该逻辑属于另一个实体,例如工厂或控制器。

+0

上面提出的什么解决方案打破了LSP,以及如何? –

+0

对不起,你是对的,我错过了静态关键字。编辑我的答案。 – SomeWittyUsername

3

假设您使用映射替换交换机,如map<ObjectType, function<Base* (DatabaseValues&)>>

现在,工厂(可能或不可能生活在基类中)不需要知道所有的子类。

但是,地图必须以某种方式填充。这意味着要么填充它(所以你的知道所有的子类问题刚刚从一个地方推到另一个地方),或者你需要子类来使用静态初始化在地图中注册他们的工厂函数。

6

这已经在这里讨论了数百万次。如果你不想创建一个单独的工厂类,你可以这样做。

class Base 
{ 

public: 
    template <class T> 
    static void Register (TObjectType type) 
    { 
     _creators[type] = &creator<T>; 
    } 

    static Base* Create (TObjectType type) 
    { 
     std::map <TObjectType, Creator>::iterator C = _creators.find (type); 
     if (C != _creators.end()) 
      return C->second(); 

     return 0;   
    } 

private: 
    template <class T> 
    static Base* creator() 
    { 
     return new T; 
    } 

private: 
    typedef Base* (::*Creator)(); 
    static std::map <TObjectType, Creator> _creators; 
}; 

int main() 
{ 
    Base::Register <Derived1> (typeA); 
    Base::Register <Derived2> (typeB); 

    Base* a = Base::Create (typeA); 
    Base* b = Base::Create (typeB); 
} 
+0

你不能命名一个函数register,它是一个保留关键字。 –

+0

@RondogiannisAristophanes,好点,固定 – 2012-11-08 16:43:27

1

只是不要使用枚举。它们不是面向对象的构造,这就是为什么JAVA在开始时没有它们(不幸的是,压力太大而无法添加它们)。

考虑,而不是这样的枚举:

enum Types { 
    typeA, 
    typeB 
}; 

这种结构,这不需要开关(另一种非OO建设在我看来)和地图:

types.h中

class Base; 
class BaseFactory { 
public: 
    virtual Base* create() = 0; 
};  
class Types { 
public: 
    // possible values 
    static Types typeA; 
    static Types typeB; 
    // just for comparison - if you do not need - do not write... 
    friend bool operator == (const Types & l, const Types & r) 
    { return l.unique_id == r.unique_id; } 
    // and make any other properties in this enum equivalent - don't add them somewhere else 
    Base* create() { return baseFactory->create(); } 

private: 
    Types(BaseFactory* baseFactory, unsigned unique_id); 
    BaseFactory* baseFactory; 
    unsigned unique_id; // don't ever write public getter for this member variable!!! 
}; 

Types.cpp

#include "Types.h" 
#include "Base.h" 
#include "TypeA.h" 
#include "TypeB.h" 

namespace { 
    TypeAFactory typeAFactory; 
    TypeBFactory typeAFactory; 
    unsigned unique_id = 0; 
} 
Types Types::typeA(&typeAFactory, unique_id++); 
Types Types::typeA(&typeBFactory, unique_id++); 

所以你的例子(如果你真的需要这个功能,那么):

class Base 
{ 
    static Base * create(struct Databasevalues dbValues) 
    { 
    return dbValues.ObjectType.create(); 
    } 
}; 

缺少的部分应该很容易实现。

+0

这是我个人的观点,枚举和开关不是面向对象的结构。对于枚举降低 - 你可以自由地在这个主题中有你自己的意见;) – PiotrNycz

+0

这个解决方案不会在我的特定环境中工作,因为DAL(因此结构)是纯C的。但是,无论如何感谢你的想法。这可能在未来派上用场 – sigy