2

Introduction: I come from a mechanical engineering background, but took a class in embedded software programming (on a lovely little robot) with the intention of improving some skills I had in programming already. However, the class was largely unsatisfactory in what I hoped to achieve (basically, it taught the basics of c++ with some very superficial composition patterns).

Question We were told to make our code somewhat object oriented by defining classes for various parts of the code. Since all the parts were very dependent of each other, the general structure looked as follows (basically, a Drive, Sensors and WorldModel class with some dependencies, and a Director class trying to make our robot solve the task at hand)

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;
};

This is actually an extremely condensed version. It seems to me however that this is not really object oriented code as much as Clumsily Split-Up Code™. In particular, it seemed almost impossible to make e.g. the Sensors class get data from the Drive class without some fudging around in the Director class (i.e., first perform a function in the Drive class to get the velocity setpoint, and then provide that to the update() method in the Sensors class to do some Kalman filtering).

How does one create a project in c++ with various parts being very dependent on each other, without this becoming a problem? I read an SO answer on interfaces but I'm not sure how to apply that to this problem - is that even the way to go here? Is there a design pattern (not necessarily an object oriented one) that is suitable for projects such as this one?

Community
  • 1
  • 1
Sanchises
  • 847
  • 4
  • 22
  • I'd say if you think "Object Oriented" is somehow indicative of "Interfaces" (i.e. runtime polymorphism) then you should probably leave that behind for C++. In the areas where C++ is best fit, I think this model of OO is rarely the most useful – sehe Oct 18 '15 at 21:07
  • @sehe I'm not saying I necessarily want object oriented code - it's just that this code was a half-hearted attempt at OOP which was never explained in further detail than "Classes. Things can be public and private. Have fun!" – Sanchises Oct 18 '15 at 21:11
  • I think when you do the "have fun" part, you'll find out the rest. Either that, or read some books. I don't think that SO is a good place to sollicit tutorials, sadly – sehe Oct 18 '15 at 21:12
  • @sehe We had a lot of fun, but I don't feel that I learnt a lot from the course. Also, I'm not asking for a tutorial, just a pointer as to what design patterns I should try to investigate - I can indeed read the relevant literature if I know where to look. – Sanchises Oct 18 '15 at 21:14

1 Answers1

3

No, there's not a design pattern for projects "like this".

Design patterns are not the goal.

So, let me put a few guesses straight:

  • you want light weight code (because otherwise you'd be using Java, right)
  • you want maintainable code (because otherwise, spaghetti would be fine)
  • you want idiomatic code

Here's what I'd do:

  • declare classes in separate headers
  • use forward defines to reduce header coupling
  • move implementations in the corresponding source files
  • keep unwanted implementation dependencies out of the header file. Optionally use the Pimpl Idiom here.

    e.g. if you use library X to implement Y::frobnicate don't include libX.h in your Y.h. Instead, include it in Y.cpp only.

    If you find that you need class member declaration that would require libX.h in the header, use the Pimpl Idiom.

I don't know what else you could want here :)

Maybe, if you need "interfaces" consider using template composition. Policy, strategy, state patterns. E.g. Instead of

    #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;
    };

You could consider

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

  private:
    Sensors &sensors;
};

Which leaves you free to implement Sensors in any which way that statically compiles. The "limitation" is that the injection of dependencies needs to be statically defined/typed. The benefit is ultimate flexibility and zero-overhead: e.g. you couldn't have virtual member function templates, but you can use this as a Sensors policy:

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>;
sehe
  • 374,641
  • 47
  • 450
  • 633
  • Thanks for your extensive answer. We actually did manage to solve some circular references by indeed only #include-ing in the source files, but I couldn't solve one problem: References need to be initialized right away, so I couldn't properly initialize the objects if the constructors were `Sensor(Drive & drive)` and `Drive(Sensor & sensor)`; can template composition solve that, or should I enter pointer hell? – Sanchises Oct 18 '15 at 21:50
  • Ah, yeah. I forgot about the references. I'd use (smart) pointers that aptly express the ownership semantics. Optionally, create a factory function that creates (sets of) objects. But really, if a Drive "has-a" Sensors "has-a" Drive, that is a code smell. Use responsibility-based separation to eliminate the cycle. – sehe Oct 18 '15 at 21:53
  • Basically at this point, you could post (a reduced version of) your code on [codereview.se] – sehe Oct 18 '15 at 21:56