比工厂更好的设计模式
A better design pattern than factory?
在我现在创建的代码中,我有一个对象可以属于两种离散类型,按序列号区分。像这样:
class Chips {
public:
Chips(int shelf) {m_nShelf = shelf;}
Chips(string sSerial) {m_sSerial = sSerial;}
virtual string GetFlavour() = 0;
virtual int GetShelf() {return m_nShelf;}
protected:
string m_sSerial;
int m_nShelf;
}
class Lays : Chips {
string GetFlavour()
{
if (m_sSerial[0] == '0') return "Cool ranch";
else return "";
}
}
class Pringles : Chips {
string GetFlavour()
{
if (m_sSerial.find("cool") != -1) return "Cool ranch";
else return "";
}
}
现在,实现这一点的明显选择是使用工厂设计模式。手动检查哪个序列属于哪个类类型不会太困难。
但是,这需要一个知道所有其他类并按名称引用它们的类,这几乎不是真正的通用类,特别是如果我最终不得不添加一大堆子类。
更复杂的是,我可能不得不在一个对象周围保留一段时间,然后才能知道它的实际序列号,这意味着我可能必须编写充满虚拟函数的基类,而不是保持抽象,并在我获得序列号时以某种方式将其替换为其中一个子类的实例。这也不太理想。
工厂设计模式真的是解决这个问题的最佳方法,还是有人有更好的主意?
您可以创建一个只知道 Base 类的工厂,如下所示:
将纯虚拟方法添加到基类:virtual Chips* clone() const=0;
并为所有派生实现它,就像operator=
一样,但返回指向新派生的指针。(如果你有析构函数,它也应该是虚拟的)
现在,您可以定义工厂类:
Class ChipsFactory{
std::map<std::string,Chips*> m_chipsTypes;
public:
~ChipsFactory(){
//delete all pointers... I'm assuming all are dynamically allocated.
for( std::map<std::string,Chips*>::iterator it = m_chipsTypes.begin();
it!=m_chipsTypes.end(); it++) {
delete it->second;
}
}
//use this method to init every type you have
void AddChipsType(const std::string& serial, Chips* c){
m_chipsTypes[serial] = c;
}
//use this to generate object
Chips* CreateObject(const std::string& serial){
std::map<std::string,Chips*>::iterator it = m_chipsTypes.find(serial);
if(it == m_chipsTypes.end()){
return NULL;
}else{
return it->clone();
}
}
};
使用所有类型初始化工厂,您可以从中获取初始化对象类型的指针。
从评论中,我认为您正在追求这样的东西:
class ISerialNumber
{
public:
static ISerialNumber* Create( const string& number )
{
// instantiate and return a concrete class that
// derives from ISerialNumber, or NULL
}
virtual void DoSerialNumberTypeStuff() = 0;
};
class SerialNumberedObject
{
public:
bool Initialise( const string& serialNum )
{
m_pNumber = ISerialNumber::Create( serialNum );
return m_pNumber != NULL;
}
void DoThings()
{
m_pNumber->DoSerialNumberTypeStuff();
}
private:
ISerialNumber* m_pNumber;
};
(由于这是一个关于更高级概念的问题,因此防止空/无效指针问题留给读者练习。
为什么要在这里为继承而烦恼?据我所知,所有 Chips 实例的行为都是相同的。这种行为是味道由序列号定义。
如果序列号只改变了几件事,那么您可以使用简单的映射在运行时根据序列号注入或查找行为(std::function)(为什么要使事情复杂化!通过这种方式,常见行为通过序列号映射在不同芯片之间共享。
如果序列号改变了很多东西,那么我认为你的设计有点倒退。在这种情况下,您真正拥有的是定义芯片配置的序列号,您的设计应该反映这一点。喜欢这个:
class SerialNumber {
public:
// Maybe use a builder along with default values
SerialNumber( .... );
// All getters, no setters.
string getFlavour() const;
private:
string flavour;
// others (package colour, price, promotion, target country etc...)
}
class Chips {
public:
// Do not own the serial number... 'tis shared.
Chips(std::shared_ptr<SerialNumber> poSerial):m_poSerial{poSerial}{}
Chips(int shelf, SerialNumber oSerial):m_poSerial{oSerial}, m_nShelf{shelf}{}
string GetFlavour() {return m_poSerial->getFlavour()};
int GetShelf() {return m_nShelf;}
protected:
std::shared_ptr<SerialNumber> m_poSerial;
int m_nShelf;
}
// stores std::shared_ptr but you could also use one of the shared containers from boost.
Chips pringles{ chipMap.at("standard pringles - sour cream") };
这样,一旦您拥有一组产品的序列号,产品行为就不会改变。唯一的变化是封装在序列号中的"配置"。意味着Chips
类不需要更改。无论如何,有人需要知道如何构建类。当然,您也可以基于模板的注入,但您的代码需要注入正确的类型。
最后一个想法。如果 SerialNumber
ctor 采用字符串(例如 XML 或 JSON),那么在经理类型的人员定义配置后,您可以让程序在运行时读取配置。这会将业务需求与代码分离,这将是面向未来的可靠方法。
哦。。。我建议不要使用匈牙利语符号。如果更改对象或参数的类型,还必须更改名称。更糟糕的是,您可能会忘记更改它们,其他人会做出不正确的假设。除非您使用 vim/记事本进行编程,否则 IDE 将以更清晰的方式为您提供该信息。
@user1158692 - 实例化Chips
的一方只需要知道我提出的一个设计中的SerialNumber
,并且该建议的设计规定SerialNumber
类负责配置Chips
类。在这种情况下,使用Chips
的人应该知道序列号,因为他们的亲密关系。类之间的亲密关系正是应该通过构造函数注入它的原因。当然,如有必要,将其更改为使用二传手是非常非常简单的,但由于所代表的关系,我不鼓励这样做。
我真的怀疑在不知道序列号的情况下创建芯片实例是绝对必要的。我想这是一个应用程序问题,而不是类设计所需的问题。此外,如果没有序列号,该类不是很有用,如果您确实允许在没有序列号的情况下构造该类,那么您要么需要使用默认版本(要求 Chips 知道如何构造其中一个或使用全局引用!),否则您最终会通过大量检查污染类。
至于你对shared_ptr的投诉...您究竟如何提出澄清所有权语义和责任?也许原始指针将是您的解决方案,但这是危险且不清楚的。shared_ptr清楚地让设计师知道他们不拥有指针,也不对它负责。
- 有没有比在库中添加一个并非由所有派生类实现的新虚拟函数更好的设计实践
- 在C++中创建观察器设计模式的好方法
- 设计许多单例代码结构的更好方法
- 在我的情况下,多重继承是一种好的设计模式吗?
- 什么是包裹着色器参数值的类的好设计模式
- 使用Emscripten C Web Worker的大型阵列有效传输:JavaScript设计更好
- 两个附带的类层次结构-一个好的设计模式
- C++设计:子类,还是有更好的方法
- 找到数据集C 模式的更好方法
- 子类列表"master copies",寻找更好的设计
- 哪种设计更好:提供对所属对象的直接访问,或者为所属对象提供所属对象转发方法
- 部分专业化消歧优先链的更好模式
- 如何更好地实现我的C++设计模式
- 比工厂更好的设计模式
- 虚函数的参数未在所有子类中使用;有什么更好的设计方法吗?
- 为可变函数设计一个更好的API
- GetLastError()是一种设计模式吗?这是好的机制吗
- 显示列表在使用一次时是否比固定模式更好
- 对于可转换类型,设计比循环依赖项更好
- 更好的设计模式用于读取其他进程内存