Which brings us to our first class diagram outline:
The focus of this discussion is on the engine, which obviously will be an aggregate of the EngineRoom. But just to make sure our example here is a little more descriptive, we'll include other aggregates of the EngineRoom class that would be consistent across both destroyers and submarines.
Note that all of these classes are true objects in the sense that they will all have properties and methods, and they will all communicate with the EngineRoom in one manner or another (i.e. single or bi-directional). Generically, objects such as this are referred to as JavaBeans - or just "beans".
These classes are called service classes because they provide services to the system's beans - they give the beans something to do! This particular graph of objects describes an extendable combination of command and fašade patterns for performing various engine room operations, i.e. at this point, controlling things and monitoring things.
The Engine class knows how to do those types of things an engine does: run, turn gears, take in fuel, burn that fuel, etc. But it might not necessarily know when it is running too hot; it just knows how to run. The purpose of an EngineMonitorSubmarine class will be to know the safe operating conditions for this engine and report to the EngineRoomOperationsMgr from where it was instantiated. Based on this information, the EngineRoomOperationsMgr may then need to request the services of the EngineControllerSubmarine object from its Controllers object aggregate.
I don't want to clutter up the object diagram, but note that both Controllers and Monitors classes will need to implement some type of generic EngineRoomOperationsInterface, and in turn there will be separate ControllersInterface and MonitorsInterface that will describe special contracts that each of their extended classes will need to follow.
Note that the EngineRoomOperationsMgr is not an aggregate of EngineRoom and we may ask ourselves why not: why wouldn't that be just as viable of a candidate for an aggregation relationship along with FuelTank, Boiler, and Engine?
Aggregate objects - basically properties of the containing object - characteristically only know how to deal with themselves and have no knowledge of the container or the container's content. Just looking at the name "EngineRoomOperationsMgr" is a clue that this object is going to provide services for lots of things in the EngineRoom. That is why it is a service class and has an association relationship with the EngineRoom. And if the EngineRoomOperationsMgr has no properties of its own or "state" - which it actually will have in our example - then it would be an "agent" service class (i.e. a class that has no state and provides services for other classes but not itself).
We might thus expect to pass as parameters to the abstract EngineRoomOperationsMgr an object of type Submarine and perhaps an int indicating EngineMonitor functionality, though as we will see: by not passing the int we more effectively decouple objects from each other.
We might also at first think that the EngineRoom object will receive the EngineMonitorSubmarine object as a return and will then cast the returned object appropriately and then call methods on that monitor. But this is not the case.
Doing this would couple the EngineRoom and EngineMonitor objects together. What is an EngineRoom supposed to know about monitoring the things that are in it? It might know that it needs to monitor its content, but it does not necessarily follow that it knows "how" to do this.
The object that knows how to monitor a submarine's engine is the EngineMonitorSubmarine object. Thus, the passed parameter of type Submarine not only informs the EngineRoomOperationsMgr as to what type monitor to instantiate, but also provides that monitor the submarine object it is to monitor!
The initialConditionsObject simply contains various types of startup and initialization information that a Ship object needs to get its systems up and running.
The constructor brings the Ship's systems on line by setting a protected class property for the InitialConditionsObject so that all extended and aggregate systems of the Ship object have access to it.
This is a process of initializing the state of an object via its constructor. This is better object construction then using a constructor to initialize individual class properties as that just couples objects together.
Note that we pass these objects an instance of Destroyer or Submarine.
And in turn the engineRoom will know that it needs to bring its systems up, so it might go through the following:
Next, one of the things that the engineRoom must do is turn on all its controllers and monitors for its systems.
Note that this simple call gives the engineRoomOperationsMgr object and all of its objects access to all of the aShip objects. Thus, adding a CommunicationsRoom to the Ship or an ElectricalPanel to the engineRoom will automatically flow down to all objects that receive the aShip as a parameter.
The EngineRoomOperationsMgr knows that it has a bunch of controllers and monitors to activate. The specific type of monitor, for example, it needs to use is found by the Monitors class testing the cast of the aShip object parameter, finding out that it is a Submarine, and then instantiating all of its Submarine oriented aggregate monitor objects.
In our particular case, we need the EngineRoomOperationsMgr to activate the EngineMonitorSubmarine object. Note that the Monitors service class has abstract aggregate classes of FuelTankMonitor, BoilerMonitor, and EngineMonitor. Each one of these abstract classes knows how to delegate the request to the correct object.
The EngineRoomOperationsMgr instantiates a Monitors class passing it the aShip parameter.
We need an engine monitor for a Submarine. The Monitors class could do this by calling a static factory create method on the abstract EngineMonitor class which would then return an instance of EngineMonitorSubmarine.
Note that this returned engineMonitor object contains a private property of type Ship - which in this case is a Submarine.
The engineMonitor object will most likely be a thread so that it can continue to call methods on the aShip Submarine object's engine object to monitor it. Recall that as the engine systems change, they will be recognized via reference in the thread object's aShip object.