2

I am working on a project where I have to monitor the status of an elevator. So far, my design looks like this: Class diagram

We are using a third party library to read and monitor real-time values from the elevator's logic controller. A tag is an integer value in a memory register of the logic controller. For example, DM140 contains the elevator direction.

I use an adapters layer in order to keep my application domain isolated from the third party library.

In the domain, the ElevatorDirectionMonitor takes the ElevatorTag integer value, extrapolates the elevator's current direction, and then notifies its clients that the elevator's direction has changed. So far, so good.

Here's my problem: There will be a lot (a hundred or more) of types of information to monitor from the elevator, such as the current floor, whether the doors are opened/closed, etc. If I continue this way, I will have to create one monitor class for each type of information I want to monitor (e.g. an ElevatorFrontDoorStatusMonitor, ElevatorRearDoorStatusMonitor) and each monitor associated with its own Observer interface. This means a lot of work. I've been trying to find another way to structure my application, but haven't found anything more "elegant".

The basic logic in each monitor class would be pretty much the same across the board: listen for value change on an ElevatorTag instance, extrapolate the information from the new tag value, and then notify the observers. The problem seems to come from the fact that each observer class must have a different interface signature (e.g. ElevatorDirectionObserver's has NotifyDirectionChanged(ElevatorDirection newDirection) when a DoorStatusMonitor would rather have something like NotifyDoorStatusChanged(DoorPosition frontOrRear, DoorStatus openOrClosed)).

Do you think that the solution I am working on is acceptable. If not,could someone suggest which patterns I should use or investigate, or anything that could help me to simplify my design?

  • I don't see anything about a controller in the question. I was assuming this design was just for monitoring, like to make a nice GUI of the elevator state for the security desk. – Karl Bielefeldt Aug 31 '15 at 15:13
  • @KarlBielefeldt is right, this software is inteded for monitoring purpose only. All the control logic of the elevator is done by a logical controller that is much more reliable than a computer. – Michaël Blanchet Aug 31 '15 at 16:02
-2

It's a pretty good instinct to balk at creating one hundred very similar classes. That's generally a sign those should be instances instead. You might try something like:

monitors.createIntMonitor("Floor", "DM141")
monitors.createBoolMonitor("Front door open", "DM142")
monitors.createEnumMonitor("Direction", "DM140", ["Up", "Down", "None"])
// or alternately
monitors.createEnumMonitor<ElevatorDirection>("Direction", "DM140")

monitors.getMonitor("Front door open").addObserver(observer)

This design can easily be initialized dynamically, such as from a configuration file or database, including adding new fields, without requiring your app to be recompiled. These kinds of changes often end up being the biggest maintenance cost if you do it statically. It also requires a lot less copy/paste/minor changes, which is a big source of errors.

The disadvantage is you no longer get compile errors when you put a RearDoorMonitor into a variable expecting a FrontDoorMonitor. It's also somewhat more problematic to cram multiple observers into one class, but that usually isn't the best idea anyway.

Your subscribers for a templatized enum monitor could look like this:

// Common interface
template <typename T>
class Subscriber
{
    public:
      virtual void receiveEvent(T t) = 0;
};

// Individual subscriber instance
class ElevatorSubscriber : public Subscriber<ElevatorDirection>
{
    public:
        void receiveEvent (ElevatorDirection direction);
};
  • I think the string approach for EnumMonitors can easily leads to errors from unexperienced programmers of programmers that don't have a deep knowledge of the system. – Michaël Blanchet Sep 4 '15 at 14:42
  • See my edit, @MichaëlBlanchet. We use this pattern all over the place in our code. Works with our green hills compilers and gcc. – Karl Bielefeldt Sep 4 '15 at 16:05
-1

I would try to find a middle-ground. If there are, as you wrote, a hundred or more of types of information, the chance is high there will be a lot of similar types, with similar interface signatures. For example, instead of having an ElevatorFrontDoorStatusMonitor and an ElevatorRearDoorStatusMonitor, create ElevatorDoorStatusMonitor, where "Front" or "Rear" is just a parameter. If there are sensors for each floor, make the floor number a parameter.

You might end up with a dozen different types of observers, maybe more, maybe less, each one supporting a different list of "pic tags". Which of the pic tags you can group together depends, of course, how much the operations in your program differ for the different types.

0

You're looking for Boost.Signals2. In this case, every "monitor" is just one instance of boost::signals2::signal<void(ElevatorDirection)>, say. And every "observer" is just some random function of the appropriate signature- like a lambda. You can just call the signal whenever you need and add/remove methods provided. No bullshit required.

The Observer pattern is not a pattern at all. It's just a class. A class you don't even have to implement.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.