#c
#c
Вопрос:
У меня есть много, много модулей, которые могут извлечь выгоду из использования одного и того же шаблона проектирования и совместно использовать общие классы. На уровне отдельных компонентов все имеет смысл, классы можно легко расширять. Но когда я пытаюсь связать их вместе в общий объект модуля, это похоже на шаблон, для которого полиморфизм не предназначен, и мне не хватает правильного шаблона или дизайна.
Начинаем со всех базовых классов, из которых будут расширяться все остальные классы. Это Module
клей и проблема. Module
будет содержать методы, предотвращающие дублирование кода, такие как AddComponent
.
// A physical interface (Ethernet, Bluetooth, etc)
class Interface {};
// A basic component
class Component {};
// An std::map wrapper for managing Components
class ComponentManager {};
// A place to store data
class Database {};
// A module to tie all things together
class Module {
public:
Interface interface;
ComponentManager manager;
Database db;
void AddComponent(Componentamp; c) {
manager.AddComponent(c);
db.InsertComponent(c);
}
};
Все в порядке, пока мы не захотим расширить все или большинство классов, а Module
также.
class EthInterface : public Interface {}; // cool
class UdpClientComponent : public Component {}; // cool
class UdpClientDatabase : public Database {}; // cool
//class UdpClientComponentManager : public ComponentManager {}; // 90% of the time won't need it
class UdpClientModule : public Module {
public:
EthInterface interface; // how to get an EthInterface instead of Interface?
UdpClientDatabase db; // how to get a UdpClientDatabase instead of Database?
};
Я пытаюсь понять, какой шаблон или дизайн или что здесь использовать. Я думаю, что шаблоны могут быть неправильным решением, потому что я упростил этот пример, и не думаю, что шаблоны с 5 или 6 T
секциями являются хорошим дизайном. Я действительно не понимаю, как спроектировать это с использованием ptr, потому что тогда я подключаю расширенные Module
ptr извне, и я хочу, чтобы это было автономным, чтобы люди могли просто писать UdpClientModule module
, и, так сказать, у них были батарейки в комплекте.
Комментарии:
1. «не думайте, что шаблоны с 5 или 6 Ts — это хороший дизайн».. Но тогда пользователи должны предоставить 5 или 6 членов или параметров (для подачи интерфейса модуля).
2. Хорошо, хорошо, это плохой дизайн для начала? У меня действительно есть этот повторяющийся шаблон, и я бы хотел не копировать и не вставлять его повсюду. Проблема только в моем
Module
.3. Я имел в виду, насколько
using UdpClientModule = Module<EthInterface, UdpClientDatabase, ..>;
хуже, чем ваш образец?4. Я не знаю 🙂 Просто хотел узнать, каков наилучший способ его разработки, и не был уверен, что шаблон — хорошая идея.
Ответ №1:
Это может быть ударом в темноте, но, возможно, это направит вас на поиск в другом направлении… Вы могли бы использовать шаблоны, переопределяя Module
, чтобы выглядеть примерно так:
template <class IFC, class COMP, class CM, class DB> class Module {
public:
IFC interface;
CM manager;
DB db;
void AddComponent(COMPamp; c) {
manager.AddComponent(c);
db.InsertComponent(c);
}
};
Но если вы пойдете по этому пути, вы должны убедиться, что IFC
, COMP
, CM
и DB
являются производными от Interface
, Component
, ComponentManager
и Database
и для этого вам нужны концепции. Я не знаю о вас, но это немного выше моей головы, поэтому я бы пошел другим путем:
class Module {
public:
Module(Interface amp;ifc, Database amp;_db) :
interface(ifc),
manager(), db(_db) {
}
void AddComponent(Componentamp; c) {
manager.AddComponent(c);
db.InsertComponent(c);
}
private:
Interface amp;interface;
ComponentManager manager;
Database amp;db;
};
class UdpClientModule : public Module {
public:
UdpClientModule() :
Module(ethInterface, udpClientDb),
ethInterface(),
udpClientDb() {
}
private:
EthInterface ethInterface;
UdpClientDatabase udpClientdb;
};
Он по-прежнему неуклюж, но, по крайней мере, приближает вас к тому месту, куда (я полагаю) вы хотите попасть.
Ответ №2:
Интерфейсы зависят от абстракции, вы хотите зависеть от конкретных типов и там, где ваш недостаток дизайна. Оставьте интерфейс для выполнения «интерфейса» и оставьте конкретные классы, имеющие дело с конкретными типами.
class Module {
public:
virtual ~Module() = default;
void AddComponent(Componentamp; c) {
manager().AddComponent(c);
db().InsertComponent(c);
}
virtual Interfaceamp; interface() = 0;
virtual ComponentManageramp; manager() = 0;
virtual Databaseamp; db() = 0;
};
class UdpClientModule : public Module {
public:
Interfaceamp; interface() override { return ethInterface; }
ComponentManageramp; manager() override { return udcClienddb; }
Databaseamp; db() override { return manager; }
void specialUdpMethod() const { /*...*/}
private:
EthInterface ethInterface;
UdpClientDatabase udpClientdb;
ComponentManager manager;
};
В этом случае вы заявляете, что каждый модуль должен предоставлять интерфейс, диспетчер компонентов e и базу данных. Если у вас есть более мягкие ограничения, вы можете переместить зависимости в решение для внедрения зависимостей и вместо этого использовать указатель.