Path: utzoo!utgpu!jarvis.csri.toronto.edu!mailrus!cornell!rochester!pt.cs.cmu.edu!sei!ajpo!eberard From: eberard@ajpo.sei.cmu.edu (Edward Berard) Newsgroups: comp.lang.c++ Subject: Re: Objectifying incoming messages? Summary: Clarifications and More Information Message-ID: <535@ajpo.sei.cmu.edu> Date: 5 Aug 89 05:25:36 GMT References: <444@cimshop.UUCP> <452@cimshop.UUCP> <533@ajpo.sei.cmu.edu> <455@cimshop.UUCP> Lines: 316 In article <455@cimshop.UUCP>, davidm@cimshop.UUCP (David Masterson) writes: > In message <533@ajpo.sei.cmu.edu>, eberard@ajpo.sei.cmu.edu writes: > >There are quite a number of other issues and solutions I would like to > >discuss, but I have already taken enough space. > > > Discuss more... Discuss more... I agree with you that there are quite a > number of interesting issues here that need to be discussed in order for the > object-oriented paradigm that C++ uses to move out of the single process type > of development into a more multi-tasking environment (yes, you can write > multiple cooperating tasks in C++, but does C++ effectively define how they > will interface?). First, I would like to give you some background. Since 1981, I have been researching, applying, and writing about the object-oriented paradigm. In addition to applying object-oriented technology on approximately one million lines worth of source code in house, I have consulted on object-oriented techniques with a substantial number of clients. I also developed (and still give) a number of courses on object-oriented technology. My clients and students use many different programming languages, and develop a wide range of applications (everything from embedded, real-time to classic business information systems). Since I never had the "luxury" of a single programming language or application domain, I had to extract "the essence" of object-oriented technology, i.e., the _concepts_ as opposed to the _implementations_. My clients have always wanted me to take ideas and words and make them real. My job is to make the technology systematic, to clearly define terms and processes, and to do this on many different levels of abstraction. What you see discussed here is the result of a great deal of research and "hard knocks." I'm still learning. > >[stuff deleted] There > >are two types of object coupling: white-box object coupling and > >black-box object coupling. > > > Your definitions and examples here threw me some. I think I understand what > you're talking about, but never before put names to it, so I'm having trouble > matching up what you're saying with what I think I know. I'm interested in > how coupling might go too far and where the cross-over point from black to > white is (is it really that black and white?). > One way to decrease both the resuability and reliability of an object or class is through "object coupling." When Larry Constantine was researching structured design he noticed that (functional) modules could be "coupled," but that all coupling was not the same. I (and others) noticed the same thing about objects. In attempting to quantify object coupling (really "class coupling") I observed: - There was a type of object coupling which had a strongly negative impact on software reusability, but a fairly small impact on software reliability. This was because one class might know about another specific class by name, but would know little else about the other class, i.e., it treated this other specific class as a "black-box." - There was another type of object coupling which not only had a strongly negative impact on software reusability, but also had a very detrimental effect on software reliability. This other type of object coupling made maintenance and modification of object-oriented systems difficult and error prone. The main reason for this was because one class made use of some very specific characteristics of one, or more, different classes. Unlike classes exhibiting the first type of object coupling, classes exhibiting this type of object coupling "knew" something about another class, and took explicit advantage of what it "knew." In effect, the other class was a "white-box." [System Axiom: The more any part of a system "knows" about any other part of the same system, the more tightly coupled those two parts of the system become. Further, tight coupling both reduces the reusability of at least one of the components, and makes the overall system both harder to modify, and more vulnerable to negative side effects when the system is changed in any way. (See Herbert Simon's discussion of "nearly decomposable systems" in his book: "Sciences of the Artificial.")] The first type of coupling (what I now call "black box object coupling") was less serious than the second type of coupling (what I now call "white box object coupling"). I also noticed: - That black box object coupling was relatively easy to remove through a technique I refer to as "data abstraction decoupling" (although it is probably more accurately described as "object abstraction decoupling"). - That white box object coupling takes more work, and more skill to remove. The three techniques for removing black box object coupling are: "selector decoupling," "constructor decoupling," and "iterator decoupling." - That while there are varying degrees of both white box and black box object coupling there does not appear to be, as yet, any reason to identify any further refinement of either form of object coupling. > > Because the "display" operation is required to change the > >state of an object which is not an instance of the counter class > >(i.e., it must change the state of some "output object") we say that > >the counter object is white-box coupled with the "output object." > > > I don't understand "output object" or "display". Isn't it more appropriately > called asking the "counter" object for its current value? Is that black or > white coupling (one object requests information from another)? Two key points to remember when describing any object or class are: describe the object or class from an _external_ viewpoint only, and describe the characteristics of the object or class _in_ _isolation_, i.e., independent of any other object or class, and definitely independent of any specific application. This favorably increases the probability that the object or class will be constructed in such a way as to make it more reusable. Assume that "output object" is a simple object with one operation in its interface, i.e., "put." "Put" accepts strings of characters as its input, and its method directs these strings of characters to the "outside world." (In Smalltalk parlance: messages sent to the output object consist of a selector (i.e., "put") and a "string object.") If you like concrete examples, imagine that "output object" is a one-line LCD display, or a speech synthesizer, or even a very dumb ASCII terminal screen. "Display" is a composite operation whose function is to take the present value of the counter object, and to give it to "output object" for "'display' to the outside world." A "composite operation" is an operation which is composed of two, or more, primitive operations. A "primitive operation" is an operation which cannot be accomplished simply, reliably, and efficiently without knowledge of the underlying implementation of the object or class in which it is encapsulated. *** In the next three paragraphs, I know I will be defining some terms *** *** in a manner which is inconsistent with their usage in some *** *** programming languages. I am specifically thinking about Smalltalk *** *** and C++. Please be kind. If my definitions offend you, focus on *** *** the concepts, not the terminology. Please :-) *** A "selector" operation is an operation which returns state information about the object in which it is encapsulated, but cannot, by definition, change the state of that object. Examples of selector operations include: sine functions, end-of-file functions, and an operation which tells you how may items are contained in a list. A "constructor" operation is an operation which can (and often does) change the state of the object in which it is encapsulated. Examples of constructor operations include: an operation which adds an item to a list, an operation which increments a counter, and an operation which causes characters to appear on an output device. An "iterator" operation is only applicable to homogeneous composite objects. (A homogeneous composite object is a composite objects whose component objects are all (conceptually) instances of the same class.) An iterator capability is a simple concept, it provides the user with the capability to "iterate over" ("loop through") the composite object. Although a user of a homogeneous composite object may know conceptually that the object is a composite object, he or she is unaware of the underlying implementation of the object, e.g., it could be a linked list, an array, a record structure, or something entirely different. Further, the designer of the object or class cannot possibly imagine all the possible things users might want to do as they iterate over the object. Therefore, iterators are actually two-part operations. The designer of the object or class obviously knows about the underling implementation of the object or class, and thus supplies the capability to visit one, or more, nodes of the object in succession. The user of the iterator capability obviously knows what he or she wants done at each node, and hence they supply the appropriate operation(s). (There are several types of iterators, e.g., selective, constructive, active, and passive.) *** And now, back to our story ... *** A rule of class design is that classes contain only primitive operations in their interfaces. (Don't worry. There are well-defined ways for dealing with highly-reusable composite operations.) Another rule is that each operation in the interface of a class must be definable as a selector, constructor, or iterator _solely_ with respect to that class. Let us now re-examine the "counter class." We said that the counter class had several operations in its interface: - Zero (the value of the counter), which is a constructor operation with respect to the counter, and is also a primitive operation for the counter class. - Increment (the value of the counter by one), which is also a constructor operation with respect to the counter, and is also a primitive operation for the counter class. - Decrement (the value of the counter by one), which is yet another primitive constructor operation for the counter class. However, when we come to the display (the current value of the counter on an output device) operation, we run into trouble. We might be tempted to describe this operation as a selector with respect to the counter class since it does not change the state of a counter object, but that is not the whole story. Further examination reveals that the state of the output object (device) is being changed, e.g., characters appear on the device. We now realize that we have a composite operation on our hands, i.e., "Display." There are at least two primitive operations, each for a different class, i.e,: - a selector operation which returns the current value of a counter object, and - a constructor operation which alters the state of the output object (e.g., the aforementioned "put" operation) There may also be other classes and operations involved. Our course of action is simple. We first remove the composite operation (i.e., "Display") from the interface of the counter class and replace it with a selector operation which returns the current value of a counter object. We have now decoupled the counter class from the output object class using selector decoupling. At this point, we have quite a number of options available to us, but I choose not to discuss them, or the tradeoffs among them, here. However, I will say that a good course of action is to decompose the composite operation ("Display"), uncovering primitive operations as we go, and decoupling objects as you go as well. > >[By the way, there are specific techniques for removing undesirable > >object coupling. There are also specific guidelines for both avoiding > >unnecessary object coupling, and in the construction of reusable > >systems of objects.] > > > Where? I think I want to look this up. See the above discussion. More details are available now in the my course notes, and, later, in my book (if I ever get it finished). > >Objects encapsulate: > > > > - knowledge of state information > > - operations (and their corresponding methods) > > - other objects, i.e., there can be: > > - homogeneous composite objects > > - heterogeneous composite objects > > - exceptions > > - constants > > - concepts > > > Could you define "exceptions, constants, and concepts" more? Particularly as > to transferring this information from process to process? A similar question: > should processes be thought of as objects and what implications does that > have? Please allow me to provide you with some partial answers to these questions. (This message has got to be quite long already, and I'm bushed.) Exceptions are a mechanism which allows an object to alert the system of objects in which it is encapsulated that an exceptional condition has occurred. Unlike an "error flag," or the return value for a function, which the system can forget to check for, or ignore, exceptions demand that the system take some action -- even if that action is only to "turn off" (or "lower") the exception. Many programming languages provide exceptions (e.g., PL/1, Mesa, Ada), and the semantics of exceptions, and exception handling is slightly different in each one. (Exception semantics and exception handling in the presence of multiple threads of control is another fairly interesting topic area.) However, the designer of a class must determine what exceptional conditions might arise through use (or abuse) of the class, and provide well-named exceptions for these conditions. Examples of exceptional conditions for which one might want to provide exceptions are: attempting to add another item to a list which is already full, attempting to address a memory location in an EEPROM which is out of range, and a temperature sensor which detects a value outside of the limits set by a user. Notice that exceptions are one mechanism which allows an object to notify other objects of specific situation. Notice further that since exceptions do not require mention of other specific classes or objects, they should not contribute to object coupling. Well-designed objects and classes provide well-named exceptions in their interfaces. By "constants" I mean objects of constant state. The designer of an object or class may occasionally find it useful, or even necessary, to provide a constant in the interface to the object or class. These constants are usually used by one, or more, of the operations in the interface for the class, but may have other uses. Note that the constants I am referring to are not part of the state of an object. By "concepts" I mean the idea behind the abstraction, including such things as the intended, and proper, use of the object or class. For example, given a savings account object, one must put money in before one can expect to take money out. Objects and classes which lack coherent, and well-thought-out concepts are useless, dangerous, or both. There is so much more to tell, but Uncle Ed is really beat. (Its after 1:00 a.m.) See you next time. ;-) ;-) ;-) -- Ed Berard Berard Software Engineering, Inc. 18620 Mateney Road Germantown, Maryland 20874 Phone: (301) 353-9652 E-Mail: eberard@ajpo.sei.cmu.edu