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

Введение: у меня инженер-механик, но я посещал занятия по программированию встраиваемого программного обеспечения (на милом маленьком роботе) с намерением улучшить некоторые навыки программирования, которые у меня уже были. Тем не менее, класс был в значительной степени неудовлетворительным в том, чего я надеялся достичь (в основном, он преподавал основы 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, чтобы выполнить некоторые действия Калмана). фильтрация).

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


person Sanchises    schedule 18.10.2015    source источник
comment
Я бы сказал, что если вы считаете, что объектно-ориентированный язык каким-то образом указывает на интерфейсы (т.е. полиморфизм времени выполнения), вам, вероятно, следует оставить это для C++. В областях, где лучше всего подходит C++, я думаю, что эта модель объектно-ориентированного программирования редко бывает самой полезной.   -  person sehe    schedule 19.10.2015
comment
@sehe Я не говорю, что мне обязательно нужен объектно-ориентированный код - просто этот код был нерешительной попыткой ООП, которая никогда не объяснялась более подробно, чем классы. Вещи могут быть публичными и частными. Повеселись!   -  person Sanchises    schedule 19.10.2015
comment
Я думаю, когда вы сделаете веселую часть, вы узнаете все остальное. Либо так, либо читать книги. К сожалению, я не думаю, что SO - хорошее место для получения руководств.   -  person sehe    schedule 19.10.2015
comment
@sehe Нам было очень весело, но я не чувствую, что многому научился на курсе. Кроме того, я не прошу учебник, а просто указатель того, какие шаблоны проектирования я должен попытаться исследовать - я действительно могу прочитать соответствующую литературу, если знаю, где искать.   -  person Sanchises    schedule 19.10.2015


Ответы (1)


Нет, шаблона проектирования для таких проектов не существует.

Шаблоны проектирования не являются целью.

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

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

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

  • объявить классы в отдельных заголовках
  • используйте форвардные определения, чтобы уменьшить связанность заголовков
  • переместить реализации в соответствующие исходные файлы
  • держите нежелательные зависимости реализации вне файла заголовка. При желании используйте здесь идиому 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>;
person sehe    schedule 18.10.2015
comment
Спасибо за ваш обширный ответ. На самом деле нам удалось решить некоторые циклические ссылки, действительно, только #include-ing в исходных файлах, но я не смог решить одну проблему: ссылки должны быть инициализированы сразу, поэтому я не мог правильно инициализировать объекты, если конструкторы были Sensor(Drive & drive) и Drive(Sensor & sensor); может ли композиция шаблона решить это, или я должен войти в ад указателя? - person Sanchises; 19.10.2015
comment
О да. Я забыл про ссылки. Я бы использовал (умные) указатели, которые точно выражают семантику владения. При желании создайте фабричную функцию, которая создает (наборы) объектов. Но на самом деле, если у Диска есть - у Сенсоров есть Диск, это запах кода. Используйте разделение на основе ответственности, чтобы устранить цикл. - person sehe; 19.10.2015
comment
По сути, на этом этапе вы можете опубликовать (урезанную версию) свой код на Code Review. - person sehe; 19.10.2015