schultz@grebyn.com (Ronald Schultz) (11/29/90)
WHAT IS A "GOOD" OBJECT ?
The following are some brief notes on how we evaluate objects in
our environment. Any additional comments or suggestions would be
appreciated.
Designing objects is not easy. Design is a complex task. But
determining whether or not a design is good or appropriate is
crucial to being successful. Our review review process for
objects consists of the following checks:
o Model Correspondence
o Mapping
o Constraints
o Visibility
o Feedback
(Please note that while our development is conducted in
Smalltalk/V 286 and any specific examples are provided in this
enviroment these notes should be appropriate to an OO development
effort.)
Model Correspondence:
It should go without saying that the naming of an object is
extremely important, particularly if issues of reuse are
involved. Associated with any name is a user's conceptual model
of what the object is. When the object, as designed by the
designer, corresponds closely to the user's mental model of the
object, we have defined a "good" object. When the user's mental
model of the object conflicts with the object as designed is, we
will have problems. Designers of objects, and the users of the
objects, are often not the same people. Object designers must
pay particular attention to their customers (object users) and
listen and respond to the needs of their customer. Object users
must be willing to specify and present to the designers
situations where differences exist between their mental model and
the actual object. Communication between designer and user is
the key to ensuring that model correspondence is accurate and
complete.
Mapping:
This evaluation involves determining if the mapping between
operations, and what they operate on, is clear and unambiguous.
The assignment of operations (methods if you wish) to objects is
a critical analysis and design consideration.
In particular we look for the following. If the number of
possible behaviors/states exceeds the number of operations, there
is apt to be difficulty in the use of the object. In general,
operations that perform more than one action are apt to be
difficult to remember. When the number of operations equals the
number of actions, each operation can be specialized and labeled
specific to its task. In this case the operations are visible
through the name of the operation.
In addition we look for instances of object blindness during the
mapping evaluation. AN EXAMPLE OF OBJECT BLINDNESS. A power
drill and a block of wood are the objects of interest. One
wishes to drill a hole in the wood block. Where does one assign
the operation "drill hole"; to the wood block or to the drill?
What is missing here is a "person" object. A "person" drills the
hole, not the drill or the block of wood. The mapping of "drill
hole" to wood block or power drill does not make "real world"
sense, and hence serves as a clue to a mapping problem.
Visibility:
Necessary operations should be visible. This ensures that the
interface is understandable and usable. Visibility of operations
not essential to the use of the object must be restricted from
view. Public operations should be visible to the object user,
private ones should not.
Visibility acts as a good reminder of what can be done with the
object. A good relationship between the name of an operation and
what it does makes it easy to find the appropriate operation for
a task. As a result, there is little to remember.
For an example of a "bad" operation one can look in the Smalltalk
ClassHierarchyBrowser Class for an operation named "hierarchy".
As an operation name this particular operation suffers because
(a) the name of the operation is not a verb and (b) it is
difficult to discern the action that this operation performs.
Constraints:
The appropriate use of constraints in object design can help in
ensuring the proper use of objects. First there are physical
constraints. The development environment itself can be used to
physically restrict what a user of an object can see about the
object itself. Restricting source code viewing to the object
interface and public methods is one way. Typing provides another
mechanism.
Next there are semantic constraints. The names of objects and
operations provide a great deal of semantic information to the
user of the object. Appropriately naming objects and operations
provides in many cases sufficient information to the user to
ensure that his use of objects is appropriate. If your
development environment supports associating graphics with your
objects, the appropriate selection of icons, and even colors, can
be used to provide cues to the user as to how to use the object.
In these cases semantic information provides a constraint to the
user that assists the user is rationally using the object.
Another constraint is the logical constraint. In this case logic
dictates how the use of an object should occur. Adding an
integer object to an icon object does not make sense logically.
Providing cues to the user as to what the logical constraints are
in the object's use helps ensure the appropriate use of the
object.
Feedback:
Objects should "feedback" to the user appropriate information
about their state, and their response to a particular operation.
Error handling and exceptional processing should be well
understood and detailed. If an exception condition occurs, the
object should provide details back to the user in manner that
allows the user to respond quickly, with a minimum of research.
Unformatted dumps of an object's state is not an appropriate
feedback mechanism.
Any comments or suggestions as to additional evaluation criteria?
Ron Schultz
schultz@grebyn.com
614-759-3127Piersol@Apple.com (Kurt Piersol) (11/30/90)
I'd suggest some other evaluation criteria, based on experience building scalable systems with Smalltalk-80. Thus, I'll use Smalltalk-80 terms to describe them. These are: 1. Abstraction - Where possible, the object should never refer explicitly to any other class. Any dependencies on other objects should be strictly protocol based, to assure as strong an encapsulation as possible. If you do this, as time passes, you'll find that you spend less time on algorithmic details and more on understanding the transformations and movement of your data. 2. Parsimony - Not only is it important to have necessary operations visible, but it is important to have only operations appropos to the particular object located there. Although it may be tempting, for instance, to take a specialized transformation and locate its code in the originator of the transformation, it's better to locate it in the recipient. If you need to build a ROT13 transform for your mail reader, put the operation on class String, rather than class MailReader. That way, other programmers can find the capability and make use of it in serendipitous ways. 3. Expressiveness - Designing the methods of a class is like defining a language for manipulating objects of that class. Make sure your message set is expressive, and you have a big win. There is no good way of measuring expressiveness that I've ever seen, but some thought about ways you might wish to refer to the object as a generic construct will often give you insights into ways to make its message set expressive. Approach each class design as an exercise in language design. 4. Consistency - Does your class have messages which resemble messages used for similar operations in your class library. Obviously, a more consistent system is more comprehensible. This postpones the inevitable point where your system becomes too large for anyone to master, and you must implement cumbersome bureaucratic controls to attempt to maintain system integrity. 5. Relevance - How much does your class take advantage of existing classes in its operation. If you are not making extensive use of the class library, you are giving yourself unneeded headaches with maintenance and integrity. Examine classes for use of common algorithms and structures, and decide if another object class can be either used or generated to centralize the needed functionality. Remember the Abstraction criterion, though, and rely on protocol instead of explicit class reference! Hope these are of some use to you! Kurt Kurt Piersol Apple Computer ,Inc. arpa: Piersol@Apple.com 20525 Mariani Ave. uucp: ...!apple!piersol Cupertino, CA 95014 ALink: PIERSOL.K 408/974-1201 Disclaimer: My opinions are my own, NOT Apple's. So There.
bryan@sierra.STANFORD.EDU (bryan) (11/30/90)
Ron's notes, IMHO, are far too informal. Too many goods, shoulds, and
nices. Notes like this often add to the confusion of ``what is an
object'' rather than explain it. I'd recommend reading:
``Assessing the Quality of Abstract Data Types Written in Ada'',
D.W. Embley and S.N. Woodfield (Brigham Young University),
Proceedings of the 10th International Conference on Software
Engineering, April 1988, Singapore, pp. 144-153, IEE Computer
Society Order Nuber 849, ISBN 0270-5257, ACM Order Number 592880,
ISBN 0-89791-258-6.
Embley and Woodfield present a straightforward, machine-checkable
method of measuring the coupling and cohesion of a module (in this
case, Ada package).
Now I know that Ada is not OO, and that data abstraction is but one
aspect of OO, but it's a good start. If anybody knows of other
published measuring techniques (i.e., metrics) that do take into
account other aspects of OO, please post references.
dougjohnson@m.cs.uiuc.edu (11/30/90)
I think that any discussion of what is a "good" object should recoginize that objects should not be designed in isolation. Instead, a system is made up of many objects, and they must be designed to work together. Thus, it is important to have a few very flexible interfaces, and most of your objects should fit into one or more of the interfaces. The suggestion that a method should do one thing is important, and is a special case of the general system design rule that each component should do one thing. Of course, since an object has many methods, it might seem impossible for an object to do one thing and for its methods to also do one thing, but "one" is relative. The phrase "do one thing" really means that it should be possible to describe what the component does in a short sentence and that you should never have to use just half of the component. A Button might be able to display itself and to react to a mouse click, but it still embodies one main idea. Another thing to remember is that some of the objects are part of the problem domain, while others are part of the solution domain. You should not expect users to understand (or even care) about the objects that are in the solution domain. In fact, it is best to hide their existence from the users as much as possible. Ralph Johnson -- University of Illinois at Urbana-Champaign
grp@Unify.com (Greg Pasquariello) (11/30/90)
I just have two comments that may be meaningful. Then again, they may not :-) In article <11379@goofy.Apple.COM>, Piersol@Apple.com (Kurt Piersol) writes: > > I'd suggest some other evaluation criteria, based on experience building > scalable systems with Smalltalk-80. Thus, I'll use Smalltalk-80 terms to > describe them. These are: > > 1. Abstraction - Where possible, the object should never refer explicitly > to any other class. Any dependencies on other objects should be strictly > protocol based, to assure as strong an encapsulation as possible. If you > do this, as time passes, you'll find that you spend less time on > algorithmic details and more on understanding the transformations and > movement of your data. > One comment I have about your abstraction criteria, is that it may not be quite broad enough. I believe there are two kinds of objects, "dumb" objects, and "sentient" objects. Dumb objects do not know about other objects, and provide the strong encapsulation. Sentient objects however, are aware of other objects (most notably, dumb objects), and know how to make use of and interact with them. Using the example from Ronald Schultz, the block of wood and the drill are the dumb objects; they do not know about each other, and provide highly encapsulated classes. The person, on the other hand, is aware of both the drill and the block of wood, and knows exactly how the two can interact, but provides only a weak encapsulation. As in life, if a new kind of drill comes along (i.e. drillPress), the person needs to get new instruction; he needs to be subclassed to drillPressOperator, maintaining the knowledge of how to use a drill on a block of wood, but with the new knowledge of how to use the drill press to manipulate the drill. > 2. Parsimony - Not only is it important to have necessary operations > visible, but it is important to have only operations appropos to the > particular object located there. Although it may be tempting, for > instance, to take a specialized transformation and locate its code in the > originator of the transformation, it's better to locate it in the > recipient. If you need to build a ROT13 transform for your mail reader, > put the operation on class String, rather than class MailReader. That way, > other programmers can find the capability and make use of it in > serendipitous ways. My only comment here, is based on the last sentence. Object designers must be careful to include only operations on the class (as you mention), and not include gratuitious operations that someone may find serendipitous later. > Kurt > Kurt Piersol Apple Computer ,Inc. > arpa: Piersol@Apple.com 20525 Mariani Ave. > uucp: ...!apple!piersol Cupertino, CA 95014 > ALink: PIERSOL.K 408/974-1201 -- --- Greg Pasquariello Unify Corporation grp@Unify.Com