假设我有两组关联类型,例如 Animal
及其 Offspring
:
/* Animal types */
struct Animal
{
virtual string getType() const = 0;
};
struct Cat : public Animal
{
virtual string getType() const { return "Cat"; }
};
struct Dog : public Animal
{
virtual string getType() const { return "Dog"; }
};
/* Offspring types */
struct Offspring
{
virtual string getType() const = 0;
};
struct Kitten : public Offspring
{
virtual string getType() const { return "Kitten"; }
};
struct Puppy : public Offspring
{
virtual string getType() const { return "Puppy"; }
};
我正在尝试实现一个工厂,给定一个 Animal
将返回一个关联的 Offspring
类型的对象(例如,如果 Animal
实际上是一个Dog
,工厂将返回一个Puppy
)。
我第一次尝试实现这样的工厂是这样的:
// First attempt at OffspringFactory
class OffspringFactory1
{
static Offspring* createKitten() { return new Kitten(); }
static Offspring* createPuppy() { return new Puppy(); }
public:
// Create an Offspring according to the Animal type
static Offspring* getOffspring(const Animal& a)
{
// Static mapping of Animal types to Offspring factory functions
static map<string, Offspring* (*)()> factoryMap;
if (factoryMap.empty())
{
factoryMap["Dog"] = &createPuppy;
factoryMap["Cat"] = &createKitten;
}
// Lookup our Offspring factory function
map<string, Offspring* (*)()>::const_iterator fnIt = factoryMap.find(a.getType());
if (fnIt != factoryMap.end())
return fnIt->second();
else
throw "Bad animal type";
}
};
它工作正常,但我求助于基于字符串的映射,而不是纯粹基于类型的映射。在尝试转向更基于类型的实现时,我得出了这个结论:
// Second attempt at OffspringFactory
class OffspringFactory2
{
// Mapping Animal types to Offspring types
template<typename TAnimal> struct OffspringMapper;
template<>
struct OffspringMapper<Cat> {
typedef Kitten offspring_type;
};
template<>
struct OffspringMapper<Dog> {
typedef Puppy offspring_type;
};
// Factory method
template<typename TAnimal>
static Offspring* create() { return new OffspringMapper<TAnimal>::offspring_type(); }
public:
// Create an Offspring according to the Animal type
static Offspring* getOffspring(const Animal& a)
{
// Static mapping of Animal type strings to Offspring factory functions
static map<string, Offspring* (*)()> factoryMap;
if (factoryMap.empty())
{
factoryMap["Dog"] = &create<Dog>;
factoryMap["Cat"] = &create<Cat>;
}
// Lookup our Offspring factory function
map<string, Offspring* (*)()>::const_iterator fnIt = factoryMap.find(a.getType());
if (fnIt != factoryMap.end())
return fnIt->second();
else
throw "Bad animal type";
}
};
坦率地说,我不确定我在这里有什么改进:我仍然有我的字符串映射,以及更多行可读性较差的代码......
与第一个相比,第二个实现有什么优点吗?有什么方法可以去掉那个 map ?
最佳答案
这看起来像是双重 dispatch 的经典案例。在 C++ 中解决此问题的一种模式是 Visitor pattern .
class Offspring;
class OffspringFactory;
class Animal {
public:
// ... rest of Animal class ...
virtual Offspring* acceptOffspringFactory(OffspringFactory& factory)const = 0;
};
class OffspringFactory {
public:
Offspring* createCatOffspring()
{
return new Kitten;
}
// ... one createXOffspring() for each type of Animal
Offspring* getOffspring(const Animal& a)
{
return a.acceptOffspringFactory(*this);
}
};
Offspring* Cat::acceptOffspringFactory(OffspringFactory& factory)const
{
return factory.createCatOffspring();
}
// etc for rest of Animal classes
现在我再次查看你的问题,你没有指出工厂是抽象的,所以如果你可以添加像 @MooingDuck 提到的方法,你真的可以完全取消工厂。
关于c++ - 对象工厂中的类型映射,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7853384/