Tuesday, September 06, 2011

It's almost like I'm getting the hang of this

Weird as it may be, I feel highly inclined to make another post today.

I've been looking at the Lesson hierarchy, and I am quite unsure as to whether I'm happy with the current design.  Currently, inheritance is used in order to create new lessons, and LessonInterface accepts a Lesson* which it then operates on.  On the bright side, this means that it is easy to partially implement a lesson, and then complete the implementation somewhere else (for instance, implement the exercise storage logic, but not create the exercises).

However, there's also a very significant drawback:  being able to override any function means that someone could override some of the functions that implement exercise storage logic, but not the rest, breaking the mechanism entirely in strange ways.  Also, while one class may implement lesson storage logic, and another may implement output, the two are hard to put together (they both share a common base, pieces of which they do not implement).  They could inherit from Lesson virtually, but I am not sure about the consequences of that.

All in all, it seems like each lesson can be put together as a number of things that are guaranteed to provide several functions.  The primary two areas where helper classes are useful are storage and user interaction:  an intermediate class that provides one is unlikely to want to provide the other.  A third point is the solution:  as things are, the Solution class is dangling to the side, not providing much of an advantage, and costing too much effort to use most of the time.

Current solution
Above is a diagram of how the system currently works.  Bold lines represent inheritance, with the arrow pointing to the child (looking for a way to reverse that...), and the dotted lines represent pointers.

One of the obvious issues is that this contains the so-called `dreaded diamond', which, while it could certainly work, does cost somewhat more trouble.  In addition to that, the initialisation order of LessonInterface, Lesson and Solution is somewhat more complicated than it should be: a  Lesson must be created first, passed to the LessonInterface (which now has ownership of it), and then passed again to Solution, which must in turn be passed to the Lesson.

In order to repair these issues, I am considering restructuring the system to look like this:
First suggestion

However, while this does seem like an improvement, it feels like I should be able to take it a step further:  with this, I'm using inheritance for code reuse, which seems questionable:  it would make more sense for FinalLesson to have LinearStorage and VerboseOutput members.  There would need to be several functions to forward them, which seems somewhat needless...

Alternatively, these members could be public.  While public data is generally frowned upon, I think it may be justified in this case -- after all, FinalLesson would simply be carrying around two interfaces that those who interact with it have just as much right to see fully as it does.  It could also be done with a get_storage and a get_output function, but this offers nothing above simple members, and forwarding functions would simply take time to write (and would have to be written for each lesson), while providing no benefit.

The revised model is thus:

I'm not sure whether this is any good.  Thinks seem less tightly coupled (no inheritance, no awareness of LinearStorage as to what is stored, no connection between lesson and solution), but I'm not quite sure.  This does mean that I can't use different kind of lessons with the same LessonInterface (would I ever want to?), and I end up with public members...  Which I'm even more unsure about, but it seems like it could be acceptable.

Still need to think this over -- a redesign would take time -- but it looks, all in all, like a good idea.

No comments:

Post a Comment