C Многоразовый дизайн класса модуля

#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 и базу данных. Если у вас есть более мягкие ограничения, вы можете переместить зависимости в решение для внедрения зависимостей и вместо этого использовать указатель.