Path: utzoo!utgpu!jarvis.csri.toronto.edu!rutgers!apple!gem.mps.ohio-state.edu!tut.cis.ohio-state.edu!pt.cs.cmu.edu!sei!ajpo!eberard From: eberard@ajpo.sei.cmu.edu (Edward Berard) Newsgroups: comp.object Subject: Re: Subclassing vs. subtyping Summary: object-oriented concepts are supposed to reflect the "real world" Message-ID: <595@ajpo.sei.cmu.edu> Date: 10 Oct 89 01:28:06 GMT References: <33009@cornell.UUCP> Distribution: comp Lines: 308 wilk@svax.cs.cornell.edu (Michael Wilk) writes: > > A "class" serves at least two purposes: the (usually run-time) > generation of "instances" and the (usually compile-time) genera- > tion of "subclasses." If we think of classes as sets, then > instances of that class refer to members of that set. I think you have missed the original intention of classes. A class is a template, pattern, or description which represents an abstraction of a category of items, characteristics, or metrics. A class is a mechanism for gathering together the common attributes of a number of very similar items. You might say that a class is a means of describing and encapsulating the essence of a collection of very closely-related items, characteristics, or metrics. In "real life" we use classes to help us manage complexity. For example, if we understand what an airplane is, we may say things like "Boeing 747s, McDonnell Douglas DC-80s, and Piper Cubs are all airplanes." We may even say that "they are all the same class of entity." We may go even further, and point out that there is more than one Boeing 747, and that "Boeing 747" really represents a class of very similar airplanes, which have all the characteristics of airplanes, and some additional characteristics which serve to both identify them as Boeing 747s and to differentiate them from, say, Piper Cubs. Depending on the programming language we are using, we have various ways of capturing the essence of a class. Some programming languages provide an encapsulation mechanism, e.g., Smalltalk's classes, which allows us to syntactically and semantically bind together all of the sub-concepts which represent our abstraction of the real world class. These languages also provide a few, very simple, mechanisms for representing different aspects of the real world category of items. For example, depending on the language, we may have available to us such things as operations, concurrency, exceptions, and a number of mechanisms for representing knowledge of state. In a nutshell, classes are a mechanism for capturing concepts, and objects (i.e., items created using classes as the patterns) are realizations of the concepts. > > The meaning of a subclass is more subtle. In Smalltalk and CLOS > (and probably others) a subclass represents a set of objects that > are extensions of objects in the original class: they have the > same instance variables and methods, and additional instance > variables and methods can be added. Once again, in real life, subclasses represent refinements in our categorization process. When we say that a Ford is a kind of automobile, we are defining a subclass. Specifically, we mean that Fords possess all of the characteristics of automobiles, and that Fords possess additional characteristics which serve to uniquely identify them as Fords. Without much effort, we can make some additional observations: - Subclasses are refinements of superclasses. Specifically, superclasses represent broader, more general concepts, and subclasses represent specializations on these concepts. Said another way, superclasses represent higher levels of abstraction than do subclasses. - What makes a given class a subclass or a superclass is its context. For example, automobile is a superclass with respect to Ford, and a subclass with respect to "modes of transportation." [Depending on our choice of programming language, we are often forced to specify the immediate superclass when we are defining a new class, but we are seldom, if ever, required to define all the possible subclasses.] - In real life, we allow that a subclass (or subcategory) may modify, as well as extend, the characteristics of the superclass. For example, we might say that all automobiles have 4 wheels, but then allow for special cases where some automobiles have 3 wheels, and others have 6 wheels. We speak of subclasses "inheriting" characteristics from their respective superclasses. In many cases the inheritance is "wholesale" inheritance, i.e., _all_ of the inheritable characteristics of the superclass are inherited. In some special cases, we allow for "selective" inheritance, i.e., there is some mechanism which allows us to chose which characteristics we wish a subclass to inherit. - In real life we also allow for subclasses to acquire characteristics from more than one superclass. The most common example is biological, i.e., we inherit characteristics from both of our parents. This is most commonly referred to as multiple inheritance. > This seems to me a programming construct that has no semantic > correlation to anything outside of programming. Subclasses truly reflect real life. > Let us consider > an example below. A rectangle is what you think it is; a > venetian blind is a rectangular geometric figure with additional > horizontal lines. > > Class RECTANGLE > Instance variables HEIGHT, WIDTH > Methods HEIGHTEN, WIDEN, AREA > > Class VENETIAN-BLIND > Inherit from RECTANGLE > Instance variables NUMBER-OF-SLATS > (with HEIGHT and WIDTH inherited) > Methods CHANGE-NUMBER-OF-SLATS > (with HEIGHTEN, WIDEN, and AREA inherited) > > In standard object-oriented languages, VENETIAN-BLIND is a > subclass of RECTANGLE, so that all VENETIAN-BLINDS are > RECTANGLES. But ask any high school mathematics teacher if all > such shapes are rectangles and you'll hear a clear "No." In > fact, it is quite the opposite: VENETIAN-BLIND is the more > general class, as we have a rectangle whenever NUMBER-OF-SLATS > equals one! The point to remember is that a rectangle consists > of four sides *and nothing more*. There are a number of comments I can make on the above: 1. All objects have state. [I recognize that some programming approaches attempt to recognize "functional objects," i.e., "stateless objects." However, for purposes of the immediate discussion, I will chose to ignore them.] How this state is maintained is not relevant to those interacting with the object. What is important is that an object posses knowledge of its state. Some programming languages, e.g., Smalltalk, provide special mechanisms for storing state information, e.g., instance variables. However, an object's state may also be determined (calculated) on an as-needed basis, e.g., what is the current velocity of a vehicle object. An object's state may also be maintained outside of an object, e.g., some of the state information for a bank account object may be maintained in a database. 2. As others have pointed out, class Venetian_Blind is more properly represented as a collection of rectangles. In the context of the Venetian_Blind class, these rectangles are referred to as "slats." In object-oriented terminology, the Venetian_Blind class is a homogeneous composite class, i.e., it is a composite class (a class defined as an aggregation of other (component) classes) whose components are _conceptually_ all of the same class. > > One way to resolve this is to say that RECTANGLEs are not the > quadrilaterals we normally think of, but include any extensions > so long as its methods are supported in the subclasses. Thus, we > should replace the name RECTANGLE with something like > RECTANGULAR-THING and have two subclasses: VENETIAN-BLIND and > RECTANGLE (or perhaps STRICT-RECTANGLE) where the latter is > indeed the simple geometric figure without extensions. Given my previous comments, I would not recommend this approach. > But there's nothing to insist that a programmer adopt this > regimen; subclassing simply doesn't enforce it. What is more, > RECTANGULAR-THING cannot generate instances of every object in > the set of objects it represents; it can generate a STRICT- > RECTANGLE but not a VENETIAN-BLIND. I believe C++ deals with > this by calling RECTANGULAR-THING an "abstract" class and > forbidding it from generating any instances at all. This is an > understandable but unsatisfactory solution. What you are talking about here is sometimes referred to as "partial types." Partial types represent important, but incomplete concepts. For example, in everyday life, we speak of dogs. However, there is no single dog for which the concept of a dog is a _complete_ description. We usually create subclasses of dog, e.g., German_Shepard, Beagle, and Poodle, to more completely describe a given dog. (Even this amount of subclassing may be insufficient, e.g., there are many different kinds of Poodles.) Partial types are extremely useful. They allow us to gather, in one place, a set of common characteristics, i.e., characteristics which are common to all of the subclasses of the partial types. For example, in developing a user interface capability, we may create a "window class," but never create an instance of this class. We may, however, create instances of the subclasses of the window class, e.g., text windows and graphics windows. Partial types serve a number of uses: - They allow us to localize and encapsulate a set of highly logically related (cohesive) characteristics which support a single, object-oriented concept. We use this idea in everyday life, e.g., all dogs have the following characteristics ... . Think about how difficult life would be if you could never refer to "airplanes in general," "the concept of shapes," or "a category of occupations." - From the standpoint of organizing an application in an object-oriented manner, partial types spare us the drudgery of having to constantly repeat a set of common characteristics. (Think of how many keystrokes you'll save. ;-)) > > Let us now try to use subclassing to create a class that repre- > sents a restricted form of an existing class. > > Class RECTANGLE > Instance variables HEIGHT, WIDTH > Methods HEIGHTEN, WIDEN, AREA > > Class SQUARE > Inherit from RECTANGLE ? > Instance variables SIDE-LENGTH > Methods SCALE, AREA > > What happened to inheritance? It was useless! Since SQUARE is > more restricted than RECTANGLE we need *fewer* instance > variables, not *more* as subclassing allows. And the HEIGHTEN > and WIDEN methods that applied to RECTANGLEs make sense only if > we allow squares to be automatically transformed into rectangles. > It seems best to compose the class SQUARE without any references > to the class RECTANGLE; thus, any relationship between squares > and rectangles is only in the mind of the programmer. Perhaps > that is enough? Mathematicians tell us that "squares are simply special cases of rectangles," and that "circles are simply special cases of ovals." What makes squares different from rectangles, and circles different form ovals, is that restrictions are placed on the states squares and ovals may assume. This is yet another way in which a subclass may differ from its superclass, i.e., subclasses may place restrictions on the states that their instances (objects created using the subclass) may assume. Using the examples of the square and rectangle classes, we could say the following: - Squares still have "height" and "width." However, there is a restriction that these two pieces of state information have equal values. - The operations of "heighten" and "widen" are still present in squares. However, the methods (i.e., the internal implementations of the operations) have been altered. For example, heighten now changes the state of "width" as well as the state of "height." - The method for the operation of "area" for a square is identical to the method for the operation of "area" in the class rectangle. - Although we recognize that the height and width of a square are identical, we may not wish to replace them with a single piece of state information, e.g., side_length. Consider the situation where we are examining a number or rectangles to determine which of them are "true squares." This touches on the essence of many discussions concerning the differences between classes and types, e.g., should one be allowed to compare squares and rectangles in the same expression? - Why did you supply a "scale" operation for class square, but not for class rectangle? > My conclusion is that subclassing is a tool for code re-use and > does not conform to our intuitive understanding of types as sets > and subtypes as subsets, making it difficult to design classes at > a conceptual level before actual programming. My observation is almost exactly the opposite. Subclassing does provide for some low-level reuse. (There is much more to software reuse than merely "robbing code" from superclasses.) All too often I run into people who cannot separate concepts from implementation, e.g., they cannot explain the concept of inheritance outside fo the context of their favorite object-oriented programming language. In the classes I teach, more than one person has observed that I am attempting to convey a method of thinking about a problem (i.e., an object-oriented approach) which can be applied equally-well to hardware, human organizations, and source code implementations. If people do not understand object-oriented thinking before they write their first program, it will be even more difficult to separate concepts from implementations at a later date. > If I am incorrect then I would appreciate a re-education. Please > include references to published books or articles as I am putting > together a paper and will need to make the appropriate citations. > For a more detailed discussion of partial types, see: [Halbert and O'Brien, 1987]. D.C. Halbert and P.D. O'Brien, "Using Types and Inheritance in Object-Oriented Programming," IEEE Software, Vol. 4, No. 5, September 1987, pp. 71 - 79. For a discussion of "types vs. classes," see, for example, the recent discussions taking place on comp.lang.smalltalk involving Alan Wills (Manchester University, UK), Paul R. Wilson (Software Systems Laboratory, University of Illinois), Ralph Johnson (University of Illinois), and Robert Dew (Australia). I would recommend that you contact Ralph Johnson (johnson@cs.uiuc.edu) as a starting point. (Be sure to ask about Typed Smalltalk.) -- Ed Berard Phone: (301) 353-9652 FAX: (301) 353-9272