|
The following is a short list of powerful design principles that came to mind while working on the OOP architecture for a small board game:
Class Design
- Fred Brooks
wrote that the hope for OOP lay in “Building with bigger pieces” (“No
Silver Bullet Refined”, 1995 Mythical Man-Month, p. 219). He quotes
James Coggins as observing that programmers are too often building low
level pieces, “aiming low in abstraction”, while “if we design
large-grained classes that address concepts our clients are already
working with”, there are many advantages. This is key – Build with big
pieces; classes should represent as high-level big concepts in the problem
domain as possible (i.e.: Maximize class conceptual size).
- On the
other hand, I feel that the class interface (C++ style header, or UML
class diagram) should fit on one single display screen. If it gets larger
than that, then it should be broken into separate classes. (i.e.: Upper
bound to class size is one screen for the header.)
- Feel
free to use functions as signaling messages (even in a 2-way cycle between
classes), instead of relying on return values. In particular, don’t create
new classes just for use as a return value from a function. Shelly/Cashman
Systems Analysis and Design was particular helpful here, in its Ch.
5 OOD examples which have no query (value-returning) methods at all.
Function Design
- A
function should do one job well. A function should be powerful, and putting
as much functionality into one as possible is reasonable. (i.e.: Maximize
function power.)
- A
function should definitely fit on one display screen. A programmer should
be able to take in a whole function at once on his or her display; if a
function larger than this, then it must be broken into other functions.
(i.e., Upper bound on function size is one screen.)
- It
then follows that breaks, continues, and multiple return statements are
all completely acceptable within the context of a sufficiently short
function. If the programmer can see all the control branches and exit
points at once, then these mechanics can make a function’s logic more
clear (and shorter), without the problems of open-ended GOTOs.
- If a certain
job is done in multiple places (say 3+) in a program, then that should
definitely be factored out into a new function, even if it is a fairly
short one.
- Don’t
use Polish notation for variable names and identifiers. My personal
biggest productivity boost came on the day when I decided to abort trying
to use Polish notation everywhere and just give identifiers reasonable,
clearly descriptive, short names. With short functions that fit on one
screen (see above), it should be immediately evident which variables come
locally, or as parameters, versus those from the class (or globals).
Summary
For classes:
- Maximize
class conceptual size – build with big pieces.
- Upper
bound to class size is one screen for the header.
- Feel
free to use functions as signals between classes, using fewer return
values.
For functions:
- Maximize
function power.
- Upper
bound to function size is one screen.
- Break,
continue, multiple returns are all acceptable in a one-screen function.
- Jobs
done in multiple places (3+) must be refactored into a new function.
- Don’t
use Polish notation for identifiers.
Written 1/8/05, DRC
|