wilk@svax.cs.cornell.edu (Michael Wilk) (10/09/89)
Several times at OOPSLA I heard references to the issue of "subtyping" versus "subclassing." This issue ties in with the concept of inheritance and the concept of classes as sets. Below is my understanding of this issue based on what I heard at the conference. I do not claim to be an authority, so please correct me where I am wrong. 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. 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. This seems to me a programming construct that has no semantic correlation to anything outside of programming. 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*. 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. 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. 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? 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. 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. Michael Wilk (wilk@cs.cornell.edu)
grover%brahmand@Sun.COM (Vinod Grover) (10/10/89)
In article <33009@cornell.UUCP> wilk@cs.cornell.edu (Michael Wilk) writes: >This seems to me a programming construct that has no semantic >correlation to anything outside of programming. 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." This is a classic fallacy. You are modelling VENITIAN-BLIND as a kind of RECTANGLE. This model would only provide you with answers that are consistent with that view, no matter what the reality is, or what high-school teachers think. How is it different from the following example ? type THOUGHT is record ... end record; function THINK(T : THOUGHT) return THOUGHT is ... begin ... end; Just because one uses suggestive names, does not mean that our model reflects reality! > 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*. > >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. > Another way to resolve this might be to not use inheritence and adopt the view that a VENITIAN-BLIND is an object consisting of a bunch of rectangles satisfying a certain obvious constraint. >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. This is the same as the concept of "mixins" in FLAVORS. That is classes that are not to be used on their own but in combination with other classes. For example a "window border" cannot be instantiated without combination with window class. > 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. I agree, but I believe that imposing strict object-orientation on everything may yield unreal objects. One example I saw was in Air Traffic Control System, where an object called ETHER was introduced to detect collision of Airplanes. All airplanes would send messages to the ETHER to indicate their current positions and other attributes. This implied ACTION-AT-A-DISTANCE. Now any physicist will tell you that this is all nonsense. Since the model was not used to answer "wrong" questions it worked. > >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? > >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. > I dont think so. I think that subclassing (aka inheritence) is useful in many situaions for modelling real-world objects. Where it leads to awkwardness one must either live with it or use other methods in combination. Vinod Grover grover@sun.com
ttwang@polyslo.CalPoly.EDU (Thomas Wang) (10/10/89)
wilk@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. >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. >This seems to me a programming construct that has no semantic >correlation to anything outside of programming. 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. This is a very interesting point. There are two kinds of inheritance. The first kind is real ISA relationship, such as student-person. You can say with confidence that a student is a person. The second kind is what I call marriage of convenience, such as your example of venetian blind - rectangle. Venetian blind is a shade, but is it a rectangle? It is not so clear that a venetian blind ISA a rectangle. In this situation, the best thing to do is to allow venetian blind to steal some functionalities from rectangle, but say that a venetian blind is not ISA a rectangle. I remembered someone on the net call them 'real inheritance', and 'user inheritance'. I don't know if they are common terminalogies. This is yet another example of an OO concept with multiple names. We need standard terminalogies fast! >Michael Wilk (wilk@cs.cornell.edu) -Thomas Wang (There is a maniac in the bathtub!! -Akane in "Ranma 1/2") ttwang@polyslo.calpoly.edu
ahmad@icsib6.berkeley.edu (Subutai Ahmad) (10/10/89)
In article <1989Oct9.184205.20790@polyslo.CalPoly.EDU> ttwang@polyslo.CalPoly.EDU (Thomas Wang) writes: >.... There are two kinds of inheritance. >The first kind is real ISA relationship, such as student-person. You can say >with confidence that a student is a person. The second kind is what I call >marriage of convenience, such as your example of venetian blind - rectangle. >Venetian blind is a shade, but is it a rectangle? It is not so clear that >a venetian blind ISA a rectangle. > >In this situation, the best thing to do is to allow venetian blind to >steal some functionalities from rectangle, but say that a venetian blind >is not ISA a rectangle. What if you also had a class called "shade"? This is exactly the type of situation (no pun intended) where you need multiple inheritance. Just inherit the properties you want from shade AND rectangle. Another way of looking at it is that the ISA relationship doesn't have to be an exclusive property. A Venetial blind ISA rectangle but it also ISA shade. --Subutai Ahmad
hws@icsib1.Berkeley.EDU (Heinz Schmidt) (10/10/89)
In article <33009@cornell.UUCP>, wilk@svax.cs.cornell.edu (Michael Wilk) writes: > Several times at OOPSLA I heard references to the issue of > "subtyping" versus "subclassing." This issue ties in with the > concept of inheritance and the concept of classes as sets. Below > is my understanding of this issue based on what I heard at the > conference. I do not claim to be an authority, so please correct > me where I am wrong. ... > 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. This is the point taken in "subtype = subset" approaches, like Cardelli: A semantics for multiple inheritance, inf. & comp. 76, 1988 Goguen,Meseguer: Order-sorted algebra slolves the constructor-selector, multiple representation and coercion problem, 1987 IEEE proceedings of ??? Smolka, Nutt: order-sorted equational computation, SR 87-14, Univ. Kaiserslautern, 1987 These semantics do not only explain coercion RECTANGLE -------- HEIGHT ---> INT A A * | | | | VENETIAN_BLIND --- HEIGHT ------+ which allows to "implement" VENETIAN_BLIND height in terms of RECTANGLE height, but also extension like the follwowing (I assume two more RECTANGLE instance variables, X, and Y, defining the location): RECTANGLE -------- MOVE ---> RECTANGLE A A | * | * | | ** | | V VENETIAN_BLIND --- MOVE ---> VENETIAN_BLIND The obvious point in the above set interpretation is: a VENETIAN_BLIND is a RECTANGLE, hence we can apply MOVE. (**) additionaly explains the intuitively clear point that a MOVE of a VENETIAN_BLIND results in a VENETIAN_BLIND at a different location, and the diagram also sheds some light on the relation between the different objects and MOVE methods involved. The subset interpretation implies: (1) a VENETIAN_BLIND *IS* a RECTANGLE -- without forgetting any of its instance variable values in (*), (2) the RECTANGLE move operation -- presumable just changing the values of X and Y, applies to all RECTANGLES, including VENETIAN_BLINDs, (3) hence the VENETIAN_BLIND move is the restriction of the RECTANGLE move to VENETIAN_BLINDs, or (4) in other words, MOVE on RECTANGLE was MOVE for *ALL* rectangles including VBs in the first place (because it was expressed in terms of methods supported by all rectangles). Obviously, inheritance is more than coercion: it does something to the inherited methods involved: VENETIAN_BLIND MOVE(X) == **(RECTANGLE MOVE (* (X))). It transforms this method to one that can be consistently applied to VENETIAN_BLINDs. On the implementation level (code sharing), this transformation is void, since the involved mappings (*,**) are identies -- provided the involved objects are of the proper type. > 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. In the above setting, the constructors of RECTANGLEs (like NEW_RECTANGLE or what has your favorite OO language) are all constructors of the set of RECTANGLE subtypes including RECTANGLE itself. In fact each class with a constructor has both aspects: the set of all instances created by the respective constructor (I'd prefer the term "essential rectangles") and the unions of all the subtypes (here, say, "abstract rectangles"). > ... > 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. The choice of instance variables partly is a matter of taste. I believe at the heart of OO programming is the understanding that instance variables, in general, are *not* independent. In general a method will operate on a couple of variables. So I disagree. Are you saying a square doesn't have a width? Or that it doesn't have a height? It has both of them, whatever you choose to represent them. And when I ask my little son whether he can widen and heighten a square, the answer is a clear "yes". In the above setting you might say, a SQUARE is a rectangle with the constraint HEIGHT=WIDTH (some OO languages allow to express such constraints). Again the "subtype = subset" diagram above helps me understand the relation between the involved WIDEN methods. SQUARE WIDEN must preserve the constraint, because it must return a SQUARE. You might say, there is no obvious way to extend the RECTANGLE WIDEN method such that it satisfies this requirement. Well, there's redefinition beside inheritance. In some OO languages you could even reuse the RECTANGLE WIDEN method and hook onto it a "after WIDEN" method that adjusts the Y instance variable upon a call to WIDEN. In other words you tell the compiler more details about (**) and the compiler produces an efficient "combined method" for SQUARE without forcing you to obscure the semantic relation between the different WIDENs involved. > 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. I disagree. The subset interpretation is intuitive. It can also be used to explain more complex situations of subtyping (like the above SQUARE constraint) in a satisfactory manner. But one must not confuse it with the independent constructor aspect involved. -- Heinz Schmidt hws@icsi.berkeley.edu
eberard@ajpo.sei.cmu.edu (Edward Berard) (10/10/89)
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
hallett@pet3.uucp (Jeff Hallett x5163 ) (10/11/89)
In article <33009@cornell.UUCP> wilk@cs.cornell.edu (Michael Wilk) writes: >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. Basically, subclasses perform two logical functions: 1. The allow for specification of task 2. They provide semantic differentiation Either may (or may not) require more instance variables. Either may (or may not) require more messages. It is possible to have an abstract class. Such a class has instance variables and defines active messages, but has no methods and cannot generate objects. Its sole purpose is to serve as a point of specification or differentiation in the inheritance tree and act as a starting point for the classes that will actually do the work. >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? This is true, but consider that a square is a rectangle with an additional constraint upon it. It has every property that a rectangle has but has an EXTRA one - it is a semantic specification. If you send area! to a square or a rectangle, you expect the area - it doesn't matter what it is. When you create a square, you also create a rectangle - it just happens that this rectangle has all of its sides equal. Note also that a rectangle can be scaled, so scale! should be a message to rectangle as well as square. If I ask a square for its height, I expect to get its height, just like any other rectangle. Subclasses are an intuitive device that allow you to modify the behavior of the original class to create a more specific form or function. My general guideline for creating subclasses is really wrapped up in a few questions: 1. Do I need something that is similar, has the same data, but is just a little different? Y == subclass. 2. If I write a method and I have to ask the question, "Am I a..." or "Do I have the property of...", I write a subclass (usually). 3. Do I have a logical counterpart in the real world whose behavior is convenient to model that lives in an ISA relationship with some other class? Y == subclass. -- Jeffrey A. Hallett, PET Software Engineering GE Medical Systems, W641, PO Box 414, Milwaukee, WI 53201 (414) 548-5163 : EMAIL - hallett@gemed.ge.com "Your logic was impeccable Captain. We are in grave danger."