Newsgroups: comp.lang.smalltalk,comp.lang.c++,comp.lang.java,comp.object
Path: cantaloupe.srv.cs.cmu.edu!bb3.andrew.cmu.edu!newsfeed.pitt.edu!gatech!news.mathworks.com!zombie.ncsc.mil!newsgate.duke.edu!news.eff.org!sed.psrw.com!psinntp	!psinntp!psinntp!psinntp!merlin.hgc.edu!jcm
From: jcm@hgc.edu (James McKim)
Subject: Re: Dynamic vs. static type checking
Message-ID: <1996Jul19.162823.19190@merlin.hgc.edu>
Sender: usenet@merlin.hgc.edu (Action News Central)
Organization: The Hartford Graduate Center
References: <4saq9u$k3s@epx.cis.umn.edu> <31EA0843.E4@iitb. <rmartin-1607961107120001@vh1-058.wwa.com>
Date: Fri, 19 Jul 1996 16:28:23 GMT
Lines: 132
Xref: glinda.oz.cs.cmu.edu comp.lang.smalltalk:41036 comp.lang.c++:201105 comp.lang.java:69760 comp.object:52348

In article <rmartin-1607961107120001@vh1-058.wwa.com> rmartin@oma.com (Robert C. Martin) writes:
>In article <31EA78D6.6A07@iitb.fhg.de>, hutzel@iitb.fhg.de wrote:
>
>> Juergen Schlegelmilch wrote:
>
>> > The standard example is that of
>> > Rectangles and their subset Squares: Suppose you have an object of class
>> > Square (BTW, not PCG-object) attached to variables A and B, where A is
>> > constrained to hold (references to) Squares and B to refer to
>Rectangles. Now,
>> > if Square is a proper subclass of Rectangle, it provides all its methods,
>> > including set_lower_left(int x,int y). If you now do
>> >   B->set_lower_left(0,0)
>> > with B->get_upper_right_x =1 and B->get_upper_right_y =2, then afterwards
>> > (a) the Square is no longer a Square, and (b) the type constraint for
>> > variable A is violated. 
>> 
>> If you really had designed class Square as a proper subclass of class
>Rectangle, 
>> you should have implemented the characteristics of squares in an appropriate 
>> manner: Either you should have prohibited use of methods which could
>result in 
>> malformed squares (for example, in C++ override it as private in the
>subclass) 
>
>This is a useless strategy because the offending functions are still
>public in the base class Rectangle.  Any user of Rectangle who happens
>to receive a Square without knowing it, will be able to manipulate
>that Square using the dangerous functions.
>
>> or 
>> you should check the parameters inside an overridden method. Fact is:
>nobody may 
>> ask a square to be something different (Mutating objects shouldn't be used in 
>> everyday software design). If somebody does it either, he or she made an
>error in 
>> his or hers code. 
>
>Indeed not!  The user rightly assumed he was manipulating a Rectangle.
>This was all the guarantee he was given.  How was the user to know
>that somebody slipped him a Square.  Is the user now to be responsible
>for knowing every different possible kind of object that can be
>sent to him?  It cannot be.
>
>The error was made by the programmer who subclassed Square from Rectangle.
>That programmer knew that there were methods of Rectangle that were
>dangerous to Squares, and knew that users of Rectangle would 
>inadvertently call them on Squares.  That programmer should have
>prevented this situation by *not* deriving Square from Rectangle.

The real issue here is an old one in this group. Should inheritance
be used for subtyping (inheriting interface, by which I mean
both the syntax of a routine _and_ its specification) or for
subclassing (sharing implementation) as well? Robert is arguing
for subtyping only, and points out that while C++ (and Eiffel and a host
of other languages) allow descendants to hide features that their
ancestors made public, this is not a good idea in practice. Juergen
is basically arguing that it seems silly to rewrite the code for Square
from scratch when most of what you need is already there in Rectangle
and the most direct way for Square to get at it is to inherit it.
It is then up to the users of these classes not to treat an instance of Square
as a kind of Rectangle. This means no assignments of the form rect := squ
and no sending Square instances to a routine that is expecting a
Rectangle. 

Either view can be supported as either way the resolution is dependent
on programmer discipline, so I won't take sides there. I just want to
point out that assertions can help make it clear when inheritance
is being used for subtyping vs subclassing.

If the specification of set_width in Rectangle goes as follows:

  set_width( w : INTEGER )
  require w > 0
  ensure
    width = w
    height = old height

Then it is clear that SQUARE cannot satisfy the semantics of Rectangle
and so cannot be a subtype. The designers of class Square can then
either 

  1. Decide that Square will not inherit from Rectangle (Robert's
   approach).

  2. Decide that Square will inherit from Rectangle but hide the
   offending features (Juergen's approach). In this case of course
   the documentation of Square should clearly state that it is not
   to be used where a Rectangle is expected.

There are, of course, languages like Sather which separate the two
kinds of inheritance into two separate mechanisms.

OTOH, if the designers of Rectangle are a bit more flexible/prescient
they might define set_width as follows:

  set_width( w : INTEGER )
  require w > 0
  ensure
    width = w
    -- default is height = old height but descendants may redefine


Now the designers of SQUARE _would_ be able to treat it as a subtype
of RECTANGLE as they could define set_width as:

  set_width( w : INTEGER )
  require w > 0
  ensure
    width = w
    height = w



>
>-- 
>Robert C. Martin    | Design Consulting   | Training courses offered:
>Object Mentor       | rmartin@oma.com     |   Object Oriented Design
>14619 N Somerset Cr | Tel: (847) 918-1004 |   C++
>Green Oaks IL 60048 | Fax: (847) 918-1023 | http://www.oma.com  
>
>"One of the great commandments of science is:
>    'Mistrust arguments from authority.'" -- Carl Sagan


Hope this helps,
-- Jim
-- 

*------------------------------------------------------------------------------*
Jim McKim  (860)-548-2458     Teachers affect eternity. They can never tell 
Internet:  jcm@hgc.edu        where their influence stops.
