Notes on Object-Oriented Design

Given an application to be constructed or a system to be modelled: The part-whole and inheritance relations are often confused by new object-oriented programmers. Examples of each: we might have an abstract class Vehicle and a subclass Bus (inheritance relation). A Bus has parts such as an engine (which might be an instance of class Engine), a body, an electrical system, and so forth. Parts can in turn have subparts. Note that Engine isn't a subclass of Bus however!

Back to the heuristic: a bus is a vehicle, but an engine is not a bus.

First sketch out a general system design. After that many of these steps are best done interactively -- construct classes and some test instances, and begin defining and testing methods online. Include method stubs for methods not yet defined. I often define a method stub using this pattern:

frobnicate: s "frobnicate this object using s as a milepost" self notYetImplemented To do this, add the following definition in class Object: notYetImplemented self error: 'this method hasn''t been implemented yet' The advantage of doing this is that we have placeholders and error checking (in case we try using one of these methods prematurely. Also we can browse all senders of notYetImplemented to see what still needs to be filled in.

Smalltalk is well-suited to exploratory programming -- programming systems where there isn't a complete specification yet. You'll need to rearrange the class hierarchy, change object representation, etc. as you go.

Message protocol; abstract types in object-oriented languages

In Smalltalk we say that a class obeys a certain message protocol. This is informal and descriptive in Smalltalk. In some other object oriented languages such as Cecil, this is formalized as an abstract type. Abstract type need not be the same as class!

For example, all collections understand the Collection protocol (do:, collect:, etc). All objects understand printing protocol (printOn: aStream). Very loosely, think of a protocol as consisting of a set of message names along with the types (protocols) of the arguments that are expected.

The object-oriented paradigm encourages programmers to concentrate on the objects being manipulated rather than the code that does the manipulation. Example: in Scheme there is a single "write" procedure that must work on all kinds of data. In Smalltalk each kind of object understands the "printOn:" message.

What should be represented as an object?

It is sometimes appropriate to make objects that represent processes, as well as program objects representing objects in the system being modelled. Example: should we make airplane objects that understand a "fly" message? Or should we make flights into objects?

Or in simulating a billiard game, we might make billiard ball objects that understand collide messages. But (less obviously) it might be appropriate to make Collision objects.

Aphorism: use objects to model entities in the application domain -- however, generally we will also need to introduce other objects that help us code the application.

Where should behavior go?

Examples: factorial function; lexical scanner. (A lexical scanner is the part of a compiler that takes an input string and divides it up into tokens. For example the Smalltalk input string '(x+5.3) printOn: s' would be divided into the tokens x, +, 5.3, etc.)

Should we implement the factorial function as a message to integers, or as a separate FactorialComputer object? Should we implement the scanner as an operation on Strings, or make a separate Scanner object?

Probably factorial should be a message to integers, but there should be separate scanner object. We could still allow the scanner to be accessed by a scan message -- but the code would be something like 'aString scan' -- but the code would just be

scan | scanner | scanner := LexicalScanner new. scanner target: self. ^scanner tokens

Inspector: all objects understand the inspect message -- but the details of the inspector window etc are all in an inspector object.

Uses of subtyping

Nonstandard uses of subtyping:

Object-Oriented Design Methodologies

Many of these: Booch, Rumbaugh, etc etc.

Checklists of things to do; diagramming methods, etc. See e.g.

Author:       Booch, Grady 
Title:        Object-Oriented Design with Applications
Edition:      2nd ed.
Publisher:    Benjamin-Cummings Publishing Company
Year:         1994
Pages:        589p.
ISBN/Price:   0-8053-5340-2 Cloth Text $52.75 (Ingram Price), $51.75 (Net),
              $51.95

Frameworks

Reusable collections of classes -- subclass from these and adapt them to construct an application. Examples: the Model-View-Controller framework; the file system.

These need to be built with care: what are the right superclasses? what are the right method stubs that let you specialize behavior easily? Generally, you have to build a couple of applications first to see what the right generalizations are.

Patterns

Patterns in object-oriented programming are idiomatic and recurring structures of objects. A recent book is a catalog of 23 design patterns:

Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides, "Design Patterns: Elements of Reusable Object-Oriented Software," Addison-Wesley, 1994.

(An intellectual antecedent is the work in architecture of Christopher Alexander.)

Examples:

See also the patterns home page.

Object-oriented design and the pinball assignment

What are the objects? pinballgame, ball, flippers, scoring device. What are the inheritance relationships? Hint in assignment: make a Bumper class, with subclasses Flipper, Wall, ScoringThing, BlackHole, etc. Don't confuse inheritance and part/whole relationships -- for example, Ball is not a subclass of PinballGame!

Are there frameworks to use? Yes - AnimatedObject, AnimationFrame, window system.