简而言之,我想使用单个界面IProducer
来创建一个对象IProduct
。 IProduct
将拥有不同的组件,具体取决于创建它的接口。然后IProduct
接口将使用IConsumer
类。应根据IConsumer
的派生类型使用正确的IProduct
类(我不想自己进行类型检查)。
我本质上想要使用策略模式(单个接口背后的不同行为),但增加了返回特定于所使用的派生接口的对象的能力。我希望遵守Open / Close原则,并且在添加更多功能时不要更改任何现有类。
我想完成这样的事情(我确定语法在某个地方是错的,但请耐心等待):
class IProduct {
public:
int intData;
};
class ProductA : public IProduct {
public:
float floatData;
};
class ProductB : public IProduct {
public:
bool boolData;
};
class IProducer {
public:
virtual IProduct* produce(void) = 0;
};
class ProducerA : public IProducer {
public:
IProduct* produce(void) {
return new ProductA;
}
};
class ProducerB : public IProducer {
public:
IProduct* produce(void) {
return new ProductB;
}
};
class IConsumer {
public:
virtual void consume(IProduct* prod) = 0;
};
class ConsumerA : public IConsumer {
public:
void consume(IProduct* prod) {
//I want to access the float!
}
};
class ConsumerB : public IConsumer {
public:
void consume(IProduct* prod) {
//I want to access the bool!
}
};
void main() {
IProducer* producer = ProducerFactory::create("ProducerA");
IProduct* product = producer->produce();
IConsumer* consumer = ConsumerFactory::create("ConsumerA");
consumer->consume(product); //I would like the correct consumer to be used here to deal with the ProductA class
}
如果你认为有更好的方法可以解决这个问题,那我就听见了。谢谢你的帮助!
答案 0 :(得分:0)
您需要的是一个注册表,它将IProduct
实现映射到正确的IConsumer
实现。基本上它只是地图的抽象:
class ConsumerRegistry
{
std::map<size_t, std::shared_ptr<IConsumer>> m_consumers;
public:
// we are not responsible for products, so lets allow plain ptrs here for more flexibility and less overhead
std::shared_ptr<IConsumer> GetConsumer(IProduct* product)
{
auto it = m_consumers.find(typeid(product).hash_code());
if (it == m_consumers.end())
return nullptr;
else
return it->second;
}
template<typename P>
void RegisterConsumer(std::shared_ptr<IConsumer> consumer)
{
m_consumers.emplace(typeid(P).hash_code(), consumer);
}
template<typename P>
void UnregisterConsumer()
{
m_consumers.erase(typeid(P).hash_code());
}
};
全局公开此类(例如单例)或在需要它的上下文中使用它。您可以像这样注册消费者:
reg.RegisterConsumer<ProductA>(new ConsumerA());
reg.RegisterConsumer<ProductB>(new ConsumerB());
我们还可以在virtual void Register(ConsumerRegistry& reg) = 0;
内部使用IConsumer
方法,以便更安全地注册:
void ConsumerA::Register(ConsumerRegistry& reg, std::shared_ptr<IConsumer> self)
{
IConsumer::Register<ProductA>(reg, self);
}
// Required for friendship, can be static:
template<typename T>
void IConsumer::Register(ConsumerRegistry& reg, std::shared_ptr<IConsumer> self)
{
reg->RegisterConsumer<T>(self);
}
void ConsumberRegistry::RegisterConsumer(std::shared_ptr<IConsumer> consumer)
{
consumer->Register(*this, consumer);
}
将Register()
和低级RegisterConsumer()
方法设为私有,让ConsumerRegistry
和IConsumer
成为朋友。可以像这样使用:
reg.RegisterConsumer(new ConsumerA());
reg.RegisterConsumer(new ConsumerB());
答案 1 :(得分:0)
这是我正在考虑使用的解决方案。我很感激任何反馈。
我将使用访问者模式并像这样引入ProductVisitor
类:
class IProductVisitor {
public:
explicit IProductVisitor() {}
virtual ~IProductVisitor(){}
virtual void visitA(ProductA* model) = 0;
virtual void visitB(ProductB* model) = 0;
};
class ProductTypeVisitor : public IProductVisitor {
public:
typedef enum {Unknown, A, B} ProductType;
explicit ProductTypeVisitor() : modelType(Unknown) {}
virtual ~ProductTypeVisitor(){}
virtual void visitA(ProductA* product) {
modelType = A;
}
virtual void visitB(ProductB* product) {
modelType = B;
}
ProductType getProductType(void) {
return modelType;
}
ProductType modelType;
};
class IProduct {
public:
IProduct() : intData(3) {}
virtual ~IProduct(){}
int intData;
virtual void accept(IProductVisitor* v) = 0;
};
class ProductA : public IProduct {
public:
ProductA() : IProduct(), floatData(5.5) { }
virtual ~ProductA(){}
float floatData;
void accept(IProductVisitor* v) {
v->visitA(this);
}
};
class ProductB : public IProduct {
public:
ProductB() : IProduct(),boolData(false) { }
virtual ~ProductB(){}
bool boolData;
void accept(IProductVisitor* v) {
v->visitB(this);
}
};
在制作我的工厂ConsumerFactor
时,我将使用ProductTypeVisitor
类来确定产品的类,动态地正确投射(基于enum
的状态) ,然后返回使用正确的产品初始化的消费者。
class ConsumerFactory {
public:
explicit ConsumerFactory(void) {}
IConsumer* createFromProduct(IProduct* product) {
ProductTypeVisitor visitor;
product->accept(&visitor);
ProductTypeVisitor::ProductType productType = visitor.getProductType();
IConsumer* consumerPtr;
switch (productType) {
case ProductTypeVisitor::A :
consumerPtr = new ConsumerA(dynamic_cast<ProductA*>(product));
break;
case ProductTypeVisitor::B :
consumerPtr = new ConsumerB(dynamic_cast<ProductB*>(product));
break;
default:
std::cout << "Product type undefined. (throw exception)" << std::endl;
break;
}
return consumerPtr;
}
private:
ProductTypeVisitor visitor;
};
最后,代码将如下所示:
IProducer* producer = new ProducerA;
IProduct* product = producer->produce();
ConsumerFactory factory;
IConsumer* consumer = factory.createFromProduct(product);
consumer->consume();
唯一指定的是ProducerA
。在我的情况下,这是唯一应该由用户指定的东西。此外,我将更改区域分为两类,ConsumerFactory
和IProductVisitor
(这些都是非常小的更改)。
如果有人可以提供改进或建议,我会全力以赴!
答案 2 :(得分:0)
这不是完整的解决方案(也许只是好奇心),但你总是可以在编译时跟踪类型,并使用桥接模板化调用将产品发送给正确的消费者。
#include <iostream>
template <class T>
class IProduct {
public:
virtual ~IProduct() {}
int intData;
typedef T consumer;
};
class ConsumerA;
class ProductA : public IProduct<ConsumerA> {
public:
float floatData;
};
class ConsumerB;
class ProductB : public IProduct<ConsumerB> {
public:
bool boolData;
};
template <class P, class C>
void apply(P* product, C* consumer) {
dynamic_cast<typename P::consumer*>(consumer)->consume(product);
}
template <class T>
class IConsumer {
public:
virtual void consume(IProduct<T>* prod) = 0;
};
class ConsumerA : public IConsumer<ConsumerA> {
public:
void consume(IProduct<ConsumerA>* prod) {
//I want to access the float!
std::cout << "ConsumerA" << std::endl;
std::cout << dynamic_cast<ProductA*>(prod)->floatData << std::endl;
}
};
class ConsumerB : public IConsumer<ConsumerB> {
public:
void consume(IProduct<ConsumerB>* prod) {
//I want to access the bool!
std::cout << "ConsumerB" << std::endl;
std::cout << dynamic_cast<ProductB*>(prod)->boolData << std::endl;
}
};
int main(int argc, char* argv[]) {
auto p_a = new ProductA;
auto c_a = new ConsumerA;
apply(p_a, c_a);
auto p_b = new ProductB;
auto c_b = new ConsumerB;
apply(p_b, c_b);
return 0;
}
答案 3 :(得分:0)
我在尝试解决类似问题时发现了这个问题。我最终使用了here(在语言中添加了c++17
)来解决我的问题。单个接口可以返回std::any
,您可以将其转换回基础类型(通过std::any_cast
)。从广义上讲,std::any
就像“隐藏”在void*
指针后面的类型,但是以一种类型安全的方式完成,并具有完整的语言和编译器支持。
请参阅: