我尝试按照此示例在我的QT项目中实施工厂方法模式:https://www.codeproject.com/Articles/363338/Factory-Pattern-in-Cplusplus
#include <QCoreApplication>
#include <QDebug>
class IAnimal
{
public:
virtual int GetNumberOfLegs() const = 0;
virtual void Speak() = 0;
virtual void Free() = 0;
};
typedef IAnimal* (__stdcall *CreateAnimalFn)(void);
// IAnimal implementations
class Cat : public IAnimal
{
public:
int GetNumberOfLegs() const { return 4; }
void Speak() { qDebug() << "Meow" << endl; }
void Free() { delete this; }
static IAnimal * __stdcall Create() { return new Cat(); }
};
class Dog : public IAnimal
{
public:
int GetNumberOfLegs() const { return 4; }
void Speak() { qDebug() << "Woof" << endl; }
void Free() { delete this; }
static IAnimal * __stdcall Create() { return new Dog(); }
};
工厂类:
// Factory for creating instances of IAnimal
class AnimalFactory
{
private:
AnimalFactory();
AnimalFactory(const AnimalFactory &) { }
AnimalFactory &operator=(const AnimalFactory &) { return *this; }
typedef QMap<QString,CreateAnimalFn> FactoryMap;
FactoryMap m_FactoryMap;
public:
~AnimalFactory() { m_FactoryMap.clear(); }
static AnimalFactory *Get()
{
static AnimalFactory instance;
return &instance;
}
void Register(const QString &animalName, CreateAnimalFn pfnCreate);
IAnimal *CreateAnimal(const QString &animalName);
};
AnimalFactory::AnimalFactory()
{
Register("Cat", &Cat::Create);
Register("Dog", &Dog::Create);
}
void AnimalFactory::Register(const QString &animalName, CreateAnimalFn pfnCreate)
{
m_FactoryMap[animalName] = pfnCreate;
}
IAnimal *AnimalFactory::CreateAnimal(const QString &animalName)
{
FactoryMap::iterator it = m_FactoryMap.find(animalName);
if( it != m_FactoryMap.end() )
return it.value();
return NULL;
}
但是我遇到了这样的错误:
cannot convert 'IAnimal* (__attribute__((__stdcall__)) *)()' to 'IAnimal*' in return
return it.value();
只有现有的anwser(Insert function pointer into QMap (Qt))建议将Create()函数设置为静态,这似乎没有帮助。
我将非常感谢任何建议。
答案 0 :(得分:0)
这有点复杂。您正在编写C ++,因此您不应该复制Java。 C ++在这里更具表现力。
Create
/ Free
方法 - 编译器可以自动为您生成它们。 override
,包括析构函数,但不是virtual
,因为它违反了DRY。#include <QtCore>
class IAnimal {
public:
virtual int GetNumberOfLegs() const = 0;
virtual QString Speaks() = 0;
virtual ~IAnimal() {}
};
class Cat : public IAnimal {
public:
int GetNumberOfLegs() const override { return 4; }
QString Speaks() override { return QStringLiteral("Meow"); }
static auto className() { return "Cat"; }
};
class Dog : public IAnimal {
public:
int GetNumberOfLegs() const override { return 4; }
QString Speaks() override { return QStringLiteral("Woof"); }
static auto className() { return "Dog"; }
};
现在我们可以拥有一个通用工厂。请注意,所有理智的C ++容器类型都可以管理其数据。您不需要在销毁时明确清除它们。我们正在利用C ++ 11。 Register
方法只接受从Interface
派生的类型,并且该方法使用lambda表达式自动生成构造函数。
应通过在main()
中实例化来明确控制实例的生命周期。
#include <type_traits>
#include <typeindex>
#include <map>
template <class Interface> class Factory {
template <class C, class T = void> struct enable_if_I :
std::enable_if<std::is_base_of<Interface, C>::value, T> {};
using create_fn = Interface* (*)();
std::map<QByteArray, create_fn, std::less<>> m_creators;
std::map<std::type_index, QByteArray> m_names;
static Factory *&instance_ref() { // assume no inline static yet
static Factory *m_instance;
return m_instance;
}
Factory(const Factory &) = delete;
Factory &operator=(const Factory &) = delete;
public:
Factory() {
Q_ASSERT(!instance());
instance_ref() = this;
}
virtual ~Factory() { instance_ref() = {}; }
通常,注册需要派生类的类型和名称。这无关于该类是否有className()
成员的预设。工厂存储工厂功能和名称。这样可以在不使用className
作为接口的虚拟方法的情况下查找名称。
template <class T> typename enable_if_I<T>::type Register(const QByteArray &name) {
m_creators[name] = +[]()->Interface* { return new T(); };
m_names[{typeid(T)}] = name;
}
当知道类名时,我们可以利用它们来注册一个或多个类,只给出它们的类型。
template <class T1> typename enable_if_I<T1>::type Register() {
this->Register<T1>(T1::className());
}
template <class T1, class T2, class...T> typename enable_if_I<T1>::type Register() {
this->Register<T1>(T1::className());
this->Register<T2, T...>();
}
实例创建方法经过优化,不会复制给定的名称,无论格式如何。这就是我们将std::map<K, V, std::less<>>
地图与透明比较器一起使用的原因。 QByteArray
提供了operator<
,它在右侧采用了各种类型,为了利用它,密钥的类型(此处为:name)必须到达比较器。
template <typename T> static Interface *CreateA(T &&t) {
return instance() ? instance()->Create(std::forward<T>(t)) : nullptr;
}
Interface *Create(QLatin1String name) const { return Create(name.data()); }
template <typename T> Interface *Create(T &&name) const;
static const QByteArray &NameOfA(const Interface * obj);
const QByteArray &NameOf(const Interface *) const;
static Factory *instance() { return instance_ref(); }
};
template <class Interface>
template <typename T> Interface *Factory<Interface>::Create(T &&name) const {
auto it = m_creators.find(name);
return (it != m_creators.end()) ? it->second() : nullptr;
}
namespace detail {
const QByteArray & null() { static const QByteArray n; return n; }
}
template <class Interface>
const QByteArray &Factory<Interface>::NameOfA(const Interface *obj) {
return instance() ? instance()->NameOf(obj) : detail::null();
}
template <class Interface>
const QByteArray &Factory<Interface>::NameOf(const Interface *obj) const {
auto it = m_names.find(typeid(*obj));
return (it != m_names.end()) ? it->second : detail::null();
}
泛型工厂接受接口和具体类型,并在构造函数中将它们全部注册。这使得建造工厂变得简单。
template <class Interface, class ...Types>
class GenericFactory : public Factory<Interface> {
public:
GenericFactory() {
this->template Register<Types...>();
}
};
using AnimalFactory = GenericFactory<IAnimal, Cat, Dog>;
使用示例,带有断言以指示所需行为。请注意,要销毁对象,只需要delete
个实例。编译器将生成调用。
int main() {
Q_ASSERT(!AnimalFactory::instance());
{
AnimalFactory animals;
Q_ASSERT(AnimalFactory::instance());
auto *dog1 = AnimalFactory::CreateA("Dog");
Q_ASSERT(dynamic_cast<Dog*>(dog1));
Q_ASSERT(AnimalFactory::NameOfA(dog1) == Dog::className());
Q_ASSERT(dog1->Speaks() == QStringLiteral("Woof"));
auto *dog2 = AnimalFactory::CreateA(QLatin1String("Dog"));
Q_ASSERT(dynamic_cast<Dog*>(dog2));
auto *cat = AnimalFactory::CreateA("Cat");
Q_ASSERT(dynamic_cast<Cat*>(cat));
Q_ASSERT(cat->Speaks() == QStringLiteral("Meow"));
Q_ASSERT(AnimalFactory::NameOfA(cat) == Cat::className());
delete cat;
delete dog2;
delete dog1;
}
Q_ASSERT(!AnimalFactory::instance());
}