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-3127
Piersol@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. doug
johnson@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