Re: Newbie Modelling Interface Question

From: H. S. Lahman (h.lahman_at_verizon.net)
Date: 10/27/03


Date: Mon, 27 Oct 2003 19:04:23 GMT

Responding to AdamJoe...

> I have an architecture made up of a number of subsystems, one of which is a
> Hardware Interface subsystem. A client's access to this subsystem is
> restricted to the interfaces it realises. The client will hold a pointer(s)
> to the abstract type of the interface(s) and use it to invoke the
> appropriate methods on the derived class(es).Inside this subsystem I have
> two classes, amongst others, Fram and Rom, which implement the following
> methods.

A subsystem is essentially a class on steroids, so all the same OO
policies for cohesion, encapsulation, implementation hiding, and whatnot
apply to subsystems pretty much the same way they apply to classes.

The classes that implement a subsystem are its implementation.
Therefore they should be hidden just like a class implementation and
that includes any sort of inheritance structure spanning subsystems.
The usual paradigm for subsystem interfaces is to use a Facade pattern
where the outside world talks to the Facade wrapper and only the
Facade's methods understand the subsystem implementation. That is, the
Facade essentially re-dispatches incoming messages to the objects that
actually implement the subsystem.

The corollary to this is that subsystem interfaces should be pure data
transfer interfaces, typically event-based. A pure data transfer
interface has a message identifier and a by-value data packet. The
sender and receiver subsystem each map the message identifier and data
packet to their own contexts.

>
> Fram
> Read
> Write
> CrcCheck
>
> Rom
> CrcCheck
>
> It is anticipated in the future that there will be other memory types. I
> would like to define an interface or interfaces for this subsystem that
> these classes can derive from and clients can depend on.
>
> One option I've considered is to have two interfaces. The first is IMemory,
> which Rom derives from and provides one pure virtual function, CrcCheck. The
> second is IRWMemory which Fram derives from and provides pv functions Read,
> Write, and CrcCheck. The client then has a pointer to each interface.
>
> Another option is to have two interfaces again but one derives from the
> other. IRWMemory would derive from IMemory and inherit its CrcCheck
> operation. Rom would derive from IMemory and Fram from IRWMemory. Again the
> client would have a pointer to each interface.

Both these approaches expose the subsystem implementation, at least
indirectly, to the clients. That should be avoided. If you need
different interfaces for different client contexts, then subclass the
Facade wrapper, not the individual class interfaces.

The subsystem interface should also be as generic as possible. It
should also be designed around the subject matter of the subsystem in
terms likely to be favored by clients rather than in terms reflecting
the implementation. That is, the interface should support the services
that the clients think they need rather than the implementation of those
services.

For example, in a hardware interface, clients usually want to manipulate
the hardware at a much higher level than register and bitmap. Nor are
they likely to care what sort of memory is actually in the hardware.
More specifically, when the client wants to setup the hardware, that may
involve writing to memory but the client couldn't care less which memory
is written.

However, whatever hardware feature the client is trying to setup is
going to have to be writable. So when the client requests some sort of
valid setup, the Facade interface should know better than to dispatch
that request to ROM. In fact, the Facade will map the generic setup
request into _whatever hardware facilities are provided for that
particular setup_.

As a more concrete example from the world I used to live in, consider
initializing a test pin in a digital circuit tester. From the viewpoint
of the specific test program one needs to set voltage levels and timing
information (e.g., assert/return stimuli phases and read windows). So
the test program talks to a Facade interface that looks like:

setLevels (Voltage vol, Voltage voh, Voltage vil, Voltage vih);
setPhase (PatternTime assert, PatternTime return);
setWindow (PatternTime open, PatternTime close);

Now a single test pin channel will have ~200 control registers with all
sorts of scaling, embedded fields, and other stuff the Hardware Guys use
to annoy the Software Guys. Those registers will be organized into
groups based on hardware features (e.g., Levels Set, Timing Set). The
hardware interface Facade understands that the setPhase message should
be directed at a TimingSet object.

The TimingSet object, in turn, understands which register fields the
assert and return values need to be written into and it performs the low
level hardware writes to the backplane. [Our hardware changed so
rapidly that we provided a separate subsystem to handle the bit***
level stuff like split registers. That subsystem was configured
(register addresses and bit fields defined) at startup for the
configuration de jour from an Excel spread*** with the bit*** data
so that the Hardware Guys could muck with the bitsheets all they wanted
without our having to touch the code.]

While this isn't exactly the same situation as yours, hopefully it is
close enough to the analogous. That is, Timing Set vs. Levels Set are
similar to FRAM vs. ROM. The basic point is that the clients shouldn't
care about which particular hardware artifacts are actually used to
implement the services they want. That's because the clients are
<hopefully> at a different level of abstraction in viewing the problem
than specific hardware elements.

*************
There is nothing wrong with me that could
not be cured by a capful of Drano.

H. S. Lahman
hsl@pathfindermda.com
Pathfinder Solutions -- Put MDA to Work
http://www.pathfindersol.com
(888)-OOA-PATH