Как создать слабую связь между частями проекта?

Введение: я из области машиностроения, но учился на программировании для встроенного программного обеспечения (на милом маленьком роботе) с намерением улучшить некоторые навыки, которые у меня уже были в программировании. Тем не менее, класс был в значительной степени неудовлетворительным в том, что я надеялся достичь (в основном, он преподавал основы C++ с некоторыми очень поверхностными композиционными шаблонами).

Вопрос: Нам сказали сделать наш код несколько объектно-ориентированным, определив классы для различных частей кода. Поскольку все части сильно зависели друг от друга, общая структура выглядела следующим образом (в основном класс Drive, Sensors и WorldModel с некоторыми зависимостями и класс Director, пытающийся заставить нашего робота решать поставленную задачу)

class Drive{
    void update();
    Drive(Sensors & sensors);
private:
    Sensors & sensors
};

class Sensors{
    void update();
}

class WorldModel {
    void update();
    WorldModel(Sensors & sensors, Drive & drive);
private:
    Sensors & sensors;
    Drive & drive;
};

class Director {
    void update();
    Director(Sensors & sensors, Drive & drive, WorldModel & worldmodel);
private:
    Sensors & sensors;
    Drive & drive;
    WorldModel & worldmodel;
};

На самом деле это очень сжатая версия. Однако мне кажется, что это на самом деле не столько объектно-ориентированный код, сколько Clumsily Split-Up Code ™. В частности, казалось почти невозможным сделать, например, Sensors класс получить данные из Drive класс без некоторой суеты в Director (например, сначала выполните функцию в классе Drive, чтобы получить заданное значение скорости, а затем предоставьте ее update() метод в Sensors класс, чтобы сделать некоторую фильтрацию Калмана).

Как создать проект в C++ с различными частями, сильно зависящими друг от друга, не становясь проблемой? Я прочитал SO-ответ об интерфейсах, но я не уверен, как применить это к этой проблеме - это даже способ пойти сюда? Существует ли шаблон проектирования (не обязательно объектно-ориентированный), который подходит для таких проектов, как этот?

1 ответ

Нет, шаблонов для проектов "как этот" нет.

Шаблоны дизайна не являются целью.

Итак, позвольте мне сформулировать несколько догадок:

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

Вот что я бы сделал:

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

    например, если вы используете библиотеку X для реализации Y::frobnicate не включать libX.h в вашем Y.h, Вместо этого, включите его в Y.cpp только.

    Если вы обнаружите, что вам нужно объявление члена класса, которое потребует libX.h в заголовке используйте идиому Pimpl.

Я не знаю, что еще вы могли бы хотеть здесь:)

Возможно, если вам нужны "интерфейсы", подумайте об использовании композиции шаблона. Политика, стратегия, государственные модели. Например, вместо

    #include <set>

    struct ISensors {
        virtual int get(int id) const = 0;
        virtual int set(int id, int newval) const = 0;
        virtual std::set<int> sensors() const = 0;
    };

    class Drive {
        void update();
        Drive(ISensors &sensors);

      private:
        ISensors &sensors;
    };

Вы могли бы рассмотреть

template <typename Sensors>
class Drive {
    void update();
    Drive(Sensors &sensors);

  private:
    Sensors &sensors;
};

Что оставляет вас свободным для реализации Sensors любым способом, который статически компилируется. "Ограничением" является то, что внедрение зависимостей должно быть статически определено / типизировано. Преимущество заключается в максимальной гибкости и отсутствии накладных расходов: например, у вас не было шаблонов виртуальных функций-членов, но вы можете использовать их как Sensors Политика:

struct TestSensors {
    int get(int)      { return 9;  } 
    int set(int, int) { return -9; } 

    template<typename OutputIterator>
    OutputIterator sensors(OutputIterator out) const {
        int available[] = { 7, 8, 13, 21 };
        return std::copy(std::begin(available), std::end(available), out);
    }
};

using TestDrive = Drive<TestSensors>;
Другие вопросы по тегам