views:

75

answers:

4

I'm building a control system for a hardware installation (water circulation system). I've designed it in two layers: the hardware description layer and the control layer.

+----------------------------------------------+
|    Control (enables manipulation of devices) |
+----------------------------------------------+
        | uses (decorates)
        v
+---------------------------------------+  (de)serialize  +------------+
|            HW Description             |<--------------->| Files / DB |
| (stores physical layout, cabling etc) |                 +------------+
+---------------------------------------+

The hardware description layer contains a map of the hardware, describing how pipes are connected to heat exchangers and other equipment. The data for this layer is configurable per installation, and will be read in at runtime. Therefore all the classes in the hardware description layer should be serializable in one way or another (via Hibernate / serialization to XML or so).

The control layer will decorate the hardware description layer classes with intelligence, so the heat exchanger will for instance get a HeatExchangerController associated with it.

In this system, the entities in the control layer might need to seek out their neirest neighbours via the hardware description, so the heat exchange controller might do something like

HeatExchangerController neighbour = this.getHeatExchanger().getInputPipe().getOtherSide().getHeatExchanger().getController();
neighbour.notify(highLoadAlert);

The problem is that this makes the lower layer (the hardware layer) aware of the intelligence above it (its controller(s)). Not only is this breaking the one-way dependency rule between the layers, but since all hardware description classes should be serializable this complicates the code, since all references to the higher layers need to be optional, declared transient etc.

Now I'm a bit stuck trying to decide if it is a good or bad idea to make the control available through the HW description layer like this, or if there are any alternative approaches to take. I can only think of one method which would involve mirroring and delegating almost all of the HW description layer, which seems way to intrusive to be good.

So my question is if any of you recognize this situation and have any tips / experiences. Does it have a name? Are there are any good patterns /best practices for it, and are there are any other well-known libraries / frameworks which also have had and worked around this situation?

Thank you.

A: 

Take a domain driven approach.

If you break the system vertically (domains) as well. Then these vertical components can talk to each other via the controllers and nothing else. Keep the horizontal layers within each domain and the layering is vertical within a domain and each domain is isolated from each other inasmuch that they can can only communicate via the controllers.

Preet Sangha
Can you be a bit more specific? If I look at my particular scenario I cannot think of any more granular domains than maybe 'regulating heat'. What would you name the domains and the controllers?
disown
I would say each device communicates to another via controller.
Preet Sangha
I'm sorry but I don't understand how this avoids going up again from the HW description, could you elaborate?
disown
My suggestion is that horizontal layering is within a defined component and that vertical layering allows communication between similarly layered components.
Preet Sangha
So the pipe controllers can only talk to the pipe hw, and the heat exchanger controllers only with the heat exchanger hw? Doesn't seem to solve my problem.
disown
no one pice of hw van only talk to another via a controller, unless it has the same controller. Sorry i mixed up the horiz and vert in my comment.
Preet Sangha
A: 

not too sure what your problem is. if you don't want a getController() method in your hardware class, you can store hardware->controller mapping in a weak hash map

public class Controller

    static WeakHashMap<Hardware,Controller> map = ...

    static public Controller of(Hardware hw){ return map.get(hw); }

    Controller(Hardware hw)
    {
        map.put(hw, this);
    }

very often, people love to add "convenient" methods, having no fear of complexity in dependencies

user.getThis()
user.getThat()
user.getTheOthers()

while User class is a core class, it shouldn't have knowlege of This, That and The Others. Now a lof ot code depend on User class, and the User class has dependency on a lot of other classes. The end result is every class in the application depends on every other class, a big ball of mess.

irreputable
Point taken. But if I have this relation anyway, but indirectly, as you show, is it better? Maybe I should have this relationship.
disown
A: 

You haven't shown us what benefit you get from seperating the controller logic from the hardware description. Are you sure this separation is worth the effort?

It sounds like you are building an Anemic Domain Model?

meriton
Good stuff. This is what I have been thinking, that it's somehow incorrect. The biggest problem is that it's really hard to NOT separate data from logic. It might be possible in simple examples, but there is really a big difference between the HW description, which needs to be in a format which is easy to serialize, and the logic. So one could also argue (according to DDD) that I have two sub-domains which should have two different models.
disown
Why is it hard? Why can't you simply move the controller's methods into your serializable (or otherwise persisted classes)? No object persistence framework I know is bothered if the classes have methods beyond setters and getters ...
meriton
It's usually more complex than you describe it - first you have the other model argument which I made, but object serialization technologies often imposes quite high requirements on the objects, all properties must be modifiable (JAXB), the order of initialization is not defined, constructors do not get called etc etc. It might work in a classic JPA environment, for a general object graph with more than trivial inter-dependencies and startup sequences quickly tends to get out of hand IMO.
disown
In the end I linked the controllers to the hardware description with transient fields, and used decorators (visitors) to add the controllers after deserialization. The other solutions got way to elaborate and I ran into all sorts of other problems. Thanks.
disown
A: 

This is the complicated solution I had in mind. It adds a decoration layer on top of the HW layer, which adds the links up to the control layer to each HW object:

                                                                 -+
+--------------------------+               +-----------------+    | 
| HeatExchangerController  |               | PipeController  |     > Controller Layer
+--------------------------+               +-----------------+    |
            ^ 1 controller                        ^ 1 controller -+
            |                                     |
            v 1 heatExchanger                     v 1 pipe       -+
+---------------------------+  inputPipe 1 +------------------+   |
| ControlAwareHeatExchanger |--------------| ControlAwarePipe |    > Decorated HW Layer
+---------------------------+ 1 otherSide  +------------------+   |
            |                                     |              -+
            v 1 realExchanger                     v 1 realPipe   -+
+---------------------------+  inputPipe 1 +------------+         |
|      HeatExchanger        |--------------| Pipe       |          > HW Layer
+---------------------------+ 1 otherSide  +------------+         |
                                                                 -+

The Decorated layer will contain code like:

class ControlAwareHeatExchanger {
  private final HeatExchanger realExchanger;
  private final ControlAwarepipe inputPipe;

  public ControlAwareHeatExchanger(HeatExchanger decoratee) {
    realExchanger = decoratee;
    inputPipe = new ControlAwarePipe(decoratee.getInputPipe());
  }

  public ControlAwarePipe getInputPipe() {
    return inputPipe;
  }

  ...
}      

This way you can do:

ControlAwareHeatExchanger controlHE = heatExchangerController.getHeatExchanger();
ControlAwarePipe controlInputPipe = controlHE.getInputPipe();
ControlAwareHeatExchanger otherHE = controlInputPipe.getOtherSide();
HeatExchangerController otherController = otherHE.getController();

So you can go down to the decorated hw layer, jump around, and then return to the controller layer again, all without the hw layer knowing about the control layer.

But as you can see, you need to mirror all connections and decorate the whole HW layer to acheive this. A lot of delegation will be involved. Thoughts?

disown