Реализация Factory Design Pattern в C ++ с использованием Qt Creator

Я пытаюсь создать все шаблоны творческого проектирования, и у меня возникли некоторые проблемы с методом абстрактной фабрики. Обычно я программирую на Python, но слышал, что C ++ хорош для того, чтобы действительно понимать шаблоны проектирования явно, что оказывается правильным, ошибочно.

Я в основном следую этому руководству Учебное пособие.

Мой вопрос заключается в том, как бы изменить текущий код для правильной реализации метода абстрактной фабрики в C ++ с фабричным производителем, поскольку я считаю, что это как-то связано с дополнительным уровнем абстракции моего Factory Producer, который вызывает мои проблемы. Я включил весь свой код.

Я попытался сделать простую диаграмму, чтобы объяснить лучше, но это не самый формальный UML, но по сути это то, что я хотел бы сделать. мой main.cpp Файл должен лучше иллюстрировать функционал, который я пытаюсь понять, как сделать.

Абстрактная фабрика UML Diagram

Постскриптум Я просто пытаюсь улучшить работу, чтобы подать заявку на работу, поэтому, пожалуйста, дайте мне знать, если у вас есть какие-либо отзывы о написании лучших вопросов SO, стиля кодирования, именования переменных или даже соглашений C ++.

Главный

main.cpp — Основной файл реализации

Испытания создания абстрактной фабрики, абстрактного меча и абстрактного воина.

#include "factoryproducer.h"
using namespace std;

int main(int argc, char *argv[]) {
AbstractFactory* warriorFactory = FactoryProducer::createFactory("warriorfactory");

Warrior* tinyWarrior = warriorFactory->createWarrior("tinywarrior");
Warrior* normalWarrior = warriorFactory->createWarrior("normalwarrior");
Warrior* largeWarrior = warriorFactory->createWarrior("largewarrior");
Warrior* giantWarrior = warriorFactory->createWarrior("giantwarrior");

cout<<tinyWarrior->getName().toStdString()<<endl;
cout<<normalWarrior->getName().toStdString()<<endl;
cout<<largeWarrior->getName().toStdString()<<endl;
cout<<giantWarrior->getName().toStdString()<<endl;

AbstractFactory* SwordFactory = FactoryProducer::createFactory("swordfactory");

Sword* tinySword = swordFactory->createSword("tinysword");
Sword* normalSword = swordFactory->createSword("normalsword");
Sword* largeSword = swordFactory->createSword("largesword");
Sword* giantSword = swordFactory->createSword("giantsword");

cout<<tinySword->getName().toStdString()<<endl;
cout<<normalSword->getName().toStdString()<<endl;
cout<<largeSword->getName().toStdString()<<endl;
cout<<giantSword->getName().toStdString()<<endl;

return a.exec();
}

Абстрактный Фабричный Класс

abstractfactory.h — Заголовочный файл Abstract Factory

#ifndef ABSTRACTFACTORY_H
#define ABSTRACTFACTORY_H

#include "warrior.h"#include "sword.h"#include <QString>

class AbstractFactory {
public:
// Public Methods
~AbstractFactory();
virtual Warrior* createWarrior(QString warriorType) = 0;
virtual Sword* createSword(QString swordType) = 0;
};

#endif // ABSTRACTFACTORY_H

abstractfactory.cpp Файл реализации Abstract Factory

#include "abstractfactory.h"
AbstractFactory::~AbstractFactory(){}

Фабрика Производитель Класс

factoryproducer.h — Заголовочный файл Factory Producer

#ifndef FACTORYPRODUCER_H
#define FACTORYPRODUCER_H

#include "abstractfactory.h"#include "warriorfactory.h"#include "swordfactory.h"#include <QString>

class FactoryProducer {
public:
// Public Methods
static AbstractFactory* createFactory(QString factoryType);
};

#endif // FACTORYPRODUCER_H

factoryproducer.cpp — Файл реализации Factory Producer

#include "factoryproducer.h"
AbstractFactory* AbstractFactory::createFactory(QString factoryType) {
if (factoryType == nullptr) {
return nullptr;
}

if (QString::compare(factoryType, "WARRIORFACTORY", Qt::CaseInsensitive) == 0) {
return new WarriorFactory();
}

if (QString::compare(factoryType, "SWORDFACTORY", Qt::CaseInsensitive) == 0) {
return new SwordFactory();
}

return nullptr;
}

Класс абстрактного воина

warrior.h — Заголовочный файл Abstract Warrior

#ifndef WARRIOR_H
#define WARRIOR_H

#include "tinywarrior.h"#include "normalwarrior.h"#include "largewarrior.h"#include "giantwarrior.h"#include <QString>

class Warrior {
public:
// Public Methods
~Warrior();
virtual QString getName() = 0;
virtual QString getPicture() = 0;
};

#endif // WARRIOR_H

warrior.cpp — Файл реализации Abstract Warrior

#include "warrior.h"
Warrior::~Warrior(){}

Класс абстрактного меча

sword.h — Заголовочный файл Abstract Sword

#ifndef SWORD_H
#define SWORD_H

#include "tinysword.h"#include "normalsword.h"#include "largesword.h"#include "giantsword.h"#include <QString>

class Sword {
public:
// Public Methods
~Sword();
virtual QString getName() = 0;
virtual QString getPicture() = 0;
};

#endif // SWORD_H

sword.cpp — Файл реализации Abstract Sword

#include "sword.h"
Sword::~Sword(){}

Класс бетона TinySword

tinysword.h — Заголовочный файл «Конкретный крошечный меч»

#ifndef TINYSWORD_H
#define TINYSWORD_H

#include "sword.h"
class TinySword : public Sword {
public:
// Public Methods
TinySword();
~TinySword();
QString getName();
QString getPicture();
private:
// Private Member Variables
QString m_name;
QString m_picture;
};

#endif // TINYSWORD_H

tinysword.cpp — Конкретный файл реализации Tiny Sword

#include "tinysword.h"
TinySword::TinySword(){
m_name = "Tiny Sword";
m_picture = "";
}

TinySword::~TinySword(){}

QString TinySword::getName() {
return m_name;
}

QString TinySword::getPicture() {
return m_picture;
}

Конкретный нормальный меч

normalsword.h — Заголовочный файл «Конкретный нормальный меч»

#ifndef NORMALSWORD_H
#define NORMALSWORD_H

#include "sword.h"
class NormalSword : public Sword {
public:
// Public Methods
NormalSword();
~NormalSword();
QString getName();
QString getPicture();
private:
// Private Member Variables
QString m_name;
QString m_picture;
};

#endif // NORMALSWORD_H

normalsword.cpp — Файл реализации Concrete Normal Sword

#include "normalsword.h"
NormalSword::NormalSword() {
m_name = "Normal Sword";
m_picture = "";
}

NormalSword::~NormalSword(){}

QString NormalSword::getName() {
return m_name;
}

QString NormalSword::getPicture() {
return m_picture;
}

Конкретный Класс Большого Меча

largesword.h — Заголовочный файл «Конкретный большой меч»

#ifndef LARGESWORD_H
#define LARGESWORD_H

#include "sword.h"
class LargeSword : public Sword {
public:
// Public Methods
LargeSword();
~LargeSword();
QString getName();
QString getPicture();
private:
// Private Member Variables
QString m_name;
QString m_picture;
};

#endif // LARGESWORD_H

largesword.cpp — Файл реализации Concrete Large Sword

#include "largesword.h"
LargeSword::LargeSword() {
m_name = "Large Sword";
m_picture = "";
}

LargeSword::~LargeSword(){}

QString LargeSword::getName() {
return m_name;
}

QString LargeSword::getPicture() {
return m_picture;
}

Конкретный класс меча-гиганта

giantsword.h — Заголовочный файл «Конкретный гигантский меч»

#ifndef GIANTSWORD_H
#define GIANTSWORD_H

#include "sword.h"
class GiantSword : public Sword {
public:
// Public Methods
GiantSword();
~GiantSword();
QString getName();
QString getPicture();
private:
// Private Member Variables
QString m_name;
QString m_picture;
};

#endif // GIANTSWORD_H

giantsword.cpp — Файл реализации Бетонного Гигантского Меча

#include "giantsword.h"
GiantSword::GiantSword() {
m_name = "Giant Sword";
m_picture = "";
}

GiantSword::~GiantSword(){}

QString GiantSword::getName() {
return m_name;
}

QString GiantSword::getPicture() {
return m_picture;
}

Бетонный класс Tiny Warrior

tinywarrior.h — Конкретный заголовочный файл Tiny Warrior

#ifndef TINYWARRIOR_H
#define TINYWARRIOR_H

#include "warrior.h"
class TinyWarrior : public Warrior {
public:
// Methods
TinyWarrior();
~TinyWarrior();
QString getPicture();
private:
// Private Member Variables
QString m_name;
QString m_picture;
};

#endif // TINYWARRIOR_H

tinywarrior.cpp — Конкретный файл реализации Tiny Warrior

#include "tinywarrior.h"
TinyWarrior::TinyWarrior(){
m_name = "Tiny Warrior";
m_picture = ":/images/tiny-warrior.png";
}

TinyWarrior::~TinyWarrior(){}

QString TinyWarrior::getName() {
return m_name;
}

QString TinyWarrior::getPicture() {
return m_picture;
}

Конкретный класс воина

normalwarrior.h — Конкретный заголовочный файл Normal Warrior

#ifndef NORMALWARRIOR_H
#define NORMALWARRIOR_H

#include "warrior.h"
class NormalWarrior : public Warrior {
public:
// Public Methods
NormalWarrior();
~NormalWarrior();
QString getName();
QString getPicture();
private:
// Private Member Variables
QString m_name;
QString m_picture;
};

#endif // NORMALWARRIOR_H

normalwarrior.cpp Конкретная реализация Normal Warrior

#include "normalwarrior.h"
NormalWarrior::NormalWarrior() {
m_name = "Normal Warrior";
m_picture = ":/images/normal-warrior.png";
}

NormalWarrior::~NormalWarrior(){}

QString NormalWarrior::getName() {
return m_name;
}

QString NormalWarrior::getPicture() {
return m_picture;
}

Бетон Большой Воин Класс

largewarrior.h Заголовочный файл Concrete Large Warrior

#ifndef LARGEWARRIOR_H
#define LARGEWARRIOR_H

#include "warrior.h"
class LargeWarrior : public Warrior {
public:
// Methods
LargeWarrior();
~LargeWarrior();
QString getName();
QString getPicture();
private:
// Private Member Variables
QString m_name;
QString m_picture;
};

#endif // LARGEWARRIOR_H

largewarrior.cpp Файл реализации Concrete Large Warrior

#include "largewarrior.h"
LargeWarrior::LargeWarrior(){
m_name = "Large Warrior";
m_picture = ":/images/large-warrior.png";
}

LargeWarrior::~LargeWarrior(){}

QString LargeWarrior::getName() {
return m_name;
}

QString LargeWarrior::getPicture() {
return m_picture;
}

Класс Бетонный Гигант Воин

giantwarrior.h — Заголовочный файл Concrete Giant Warrior

#ifndef GIANTWARRIOR_H
#define GIANTWARRIOR_H

#include "warrior.h"
class GiantWarrior : public Warrior {
public:
// Methods
GiantWarrior();
~GiantWarrior();
QString getName();
QString getPicture();
void setXPosition(int x);
int getXPosition();
private:
// Private Member Variables
QString m_name;
QString m_picture;
};

#endif // GIANTWARRIOR_H

giantwarrior.cpp — Файл реализации Concrete Giant Warrior

#include "giantwarrior.h"
GiantWarrior::GiantWarrior(){
m_name = "Giant Warrior";
m_picture = ":/images/giant-warrior.png";
}

GiantWarrior::~GiantWarrior(){}

QString GiantWarrior::getName() {
return m_name;
}

QString GiantWarrior::getPicture() {
return m_picture;
}

-1

Решение

Данный пример из TutorialsPoint вводит в заблуждение.
Например, что возвращает ваш WarriorFactory, если вызывается createSword? Возврат nullptr не вариант из-за этого:

void doSomethingWithFactory(AbstractFactory* f)
{
Warrior* w = f->createWarrior();
Sword* s = f->createSword();
}

На самом деле вам нужно знать, был ли вам дан воин или фабрика мечей, и поэтому вы ничего не абстрагируете.

Я кодировал пример, который использует абстрактный EnemyFactory, который создает монстров и воинов. В зависимости от конкретной фабрики эти враги бывают сильными или слабыми.
EnemyFactory
Эта фабрика всегда создает врагов, но их атрибуты различаются в зависимости от конкретной фабрики.

1

Другие решения

Очевидно, я не могу говорить о вашем потенциальном будущем работодателе, но лично я не был бы в восторге от кода, который вы производите. Независимо от того, работает ли он по назначению или нет, с ним возникает ряд проблем:

  • это слишком многословно, слишком много кода, который делает слишком мало, это имеет несколько последствий — вы теряете время как разработчик, вы создаете код, который не является чистым и кратким, и который будет трудно поддерживать

  • такие вещи, как cout<<tinySword->getName().toStdString()<<endl; — не делай этого, используй пробел

  • либо используйте Qt, либо используйте std:: — нет смысла смешивать два, делать избыточные преобразования и так далее

  • Ваш код включает в себя множество сравнений строк, это довольно медленные операции, которых лучше избегать, когда это возможно, строки имеют смысл, если вы используете модульную архитектуру плагинов, где плагины реализуют типы, которых нет в ядре, но не в вашем конкретном сценарии

  • нам действительно нужен отдельный класс для каждого крошечного варианта? Маленький меч, большой меч, огромный меч? Как насчет одного Sword класс, с хорошим uint size; член и размер перечисления?

  • ваш сценарий также не требует абстрактных фабрик, вам лучше использовать одну универсальную фабрику, особенно в случае управления игровыми объектами

  • сравнивая QString в nullptr, действительно?

  • писать геттеры и сеттеры — теперь это круто, верно? Я имею в виду, почему просто сделать их общедоступными, поскольку у вас есть средства доступа для чтения и записи, когда вы можете сделать их приватными, и написать дополнительный код, чтобы сделать приватный член де-факто публичным. Вот что такое ООП … Вопреки распространенному мнению, я бы сказал, что написание сеттеров и геттеров оправдано только тогда, когда это связано с большей сложностью, чем прямой доступ к членам.

  • у вас есть только геттеры для имени и изображения, подразумевая, что это статические конструкции. В таком случае, зачем даже хранить их как переменные-члены? Излишняя память?

Казалось бы, ваша огромная стена кода легко может быть сведена к чему-то столь же функциональному, гораздо более эффективному, гораздо более читабельному и обслуживаемому.

2