Translating OO models and designs to C++
by Doug Lea.
UNDER CONSTRUCTION!
Implementing classes in C++ takes an idiomatic form when based on a
set of OO models/designs. As with modeling, the concerns can be
loosely categorized into four cells:
This document describes only the basic design decisions and techniques
for the most common aspects of each cell, and only as they apply to
sequential programming in C++. Other forms and strategies are
described in Design Patterns, OOSD, and elsewhere.
Scaffolding
Even though we'll concentrate on bottom-level implementation
matters, ideally, every Analysis/Design-level class should be
represented in C++ as an abstract class. The reasons include:
- Abstract classes enable progressive commitment during
design and implementaion -- you don't have to ``bottom out''
with every implementation decision at once. Only the
bottommost ``leaf classes'' need be fully implemented.
- Abstract classes allow you to defer commitment about implementation
details that may change across contexts and time.
- Classes that do not commit to representation details are
normally easier to subclass and compose.
- Abstract classes enable simpler reuse across applications
by allowing you to construct sets of interoperable implementations.
On the other hand, there are a few disadvantages, leading
to cases where they are almost never used:
- Abstract classes can lead to unacceptable performance problems.
- Some C++ mechanics only work well with concrete classes.
- When there is only one reasonable concrete implementation, abstract
classes just add needless busy-work.
- Some classes do not in any way represent higher-level abstractions,
but are needed only for miscellaneous implementation-dependent
chores.
Additionally, it is equally common (or perhaps even more common)
to first establish particular concrete classes, and then abstract
away details to arrive at more abstract classes.
Attributes
There are two sides of how to go about implementing values described
as attributes in models, access and update.
Accessing attributes
I'll illustrate with the example of a Thermostat with
currentTemperature and desiredTemperature
attributes. There is a continuum of possibilities for
implementation. Some of them don't make a lot of sense in this
example, but are listed for completeness.
- Direct storage via primitive types.
- For example, declaring a field
float curTemp.
- Direct storage via ADT classes.
- For example, defining an ADT class
Temperature
and a field in Thermostat listed as
Temperature curTemp.
- Indirect storage.
- For example, declaring a field
float* curTemp,
and in the Thermostat constructor, attaching
it to a new'd float.
- Static storage
- For example, if all instances should maintain the same
desiredTemperature, declaring a
static float desTemp_.
- Shared indirect storage
- For example, if all instances in the same house maintain the same
desiredTemperature, declaring a
float desTemp_ in a House class,
and linking all Thermostat values to it when
they are installed in the House
- Data base queries.
- For example, if a data base maintains desired temperatures,
implementing
desiredTemperature as a database
query via some kind of database interface.
- Subclassing a class defining storage.
- For example, declaring that
Thermostat : public Temperature.
- Constant computation.
- For example, if
desiredTemperature were always 68,
defining float desiredTemperature() { return 68; },
or nearly equivalently but more flexibly, defining a
static const float theDesTemp = 68 and returning its value.
- Local computation.
- For example, if
desiredTemperature were always 5
degrees greater than the current temperature,
defining float desiredTemperature() { return
currentTemperature + 5; }
- Delegated computation.
- For example, computing
currentTemperature by
asking a TemperatureSensor known via a
connection attribute. Note that delegated computation
can sometimes fail (for example if the link is null), so
exception mechanics may be needed.
- Caches.
- For example, using any computation-based scheme, but also
maintaining a stored
representation of the previous result,
along with some way of known whether it is still valid.
- Subclassing a class defining computation
- For example, overriding one of the above versions.
Updating attributes
The following strategies interact with access mechanics; only
a few combinations of them make sense.
- Direct updates.
- For example, defining
raiseDesTemp(float delta)
as { desTemp_ += delta; }.
- Indirect updates.
- For example, offloading the update mechanics to the object
maintaining the actual representation.
- History-preserving updates.
- For example, saving the history of
raiseDesTemp
in a list so previous states can be reverted to.
- Reconnections.
- For example, with an indirect scheme, rebinding to a
new float on update rather than modifying
the current one. Similarly, but with additional mechanics
for reference-counting schemes.
- Constrained updates.
- For example, if desired temperatures must always lie
in some range, ensuring that these constraints hold
after the update by clipping them to bounds.
- Exceptionable updates.
- For example, if desired temperatures must always lie
in some range, raising an exception if a client tried to
set to an impossible value. Normally, these should
operate transaction style: If the attempted update fails,
the state is guaranteed to be that holding before the
attempted update.
- Propagated updates.
- For example, notifying a
Heater if the
desiredTemperature is changed.
- Global restructuring on update.
- For example, if an attempt to set the desired temperature
in one room of a house requires recomputing a global
optimal collection of settings through the house, invoking
a general-purpose constraint solver that does this, perhaps
even resulting in the actual setting for the current thermostat
to be different than requested.
- Database updates.
- For example, issuing a database transaction every time that
desiredTemperature is changed.
- Indirect propagation.
- For example, defining a
PropagatableTemperature
class that accepts a list of objects to notify when it is
changed, and delegating all accesses and updates to it.
Relationships
Last update Wed May 10 06:05:25 1995 Doug Lea (dl at gee)