[comp.object] testing object oriented programs

jimad@microsoft.UUCP (Jim ADCOCK) (05/26/90)

In article <1990May23.171152.29448@Neon.Stanford.EDU> pallas@Neon.Stanford.EDU (Joe Pallas) writes:

>Inheritance does NOT weaken encapsulation.  You cannot "break" a class
>by inheriting an implementation from it.  You can, however, create a
>broken class by inheriting an implementation from an unbroken class.
>The correctness of an implementation says nothing about the
>correctness of implementations which inherit from it.

I'd claim in the following trivial example, the base class *is* broken --
but you cannot tell that by testing the base class without deriving
from it.  Conversely, the derived class *is not* broken -- but the 
bug in the base class only shows up when testing the derived class.
So I claim that inheritence *does* weaken encapsulation, and you *can*
break a class by inheriting an implementation from it -- if the base
class is never derived from, then the present implementation of the
base class is just fine!  Only in the presence of derivation need the
base class be fixed.

class BASE 
{ 
public:
	virtual void PrintClassName() { printf("BASECLASS\n"); }
	virtual void PrintBaseClassName() { PrintClassName(); }
};

class DERIVED : public BASE
{
public:
	void PrintClassName() { printf("DERIVEDCLASS\n"); }
};

main()
{
	BASE* pbase;

	pbase = new BASE;
	pbase->PrintClassName();
	pbase->PrintBaseClassName();

	pbase = new DERIVED;
	pbase->PrintClassName();
	pbase->PrintBaseClassName();
}

pallas@Neon.Stanford.EDU (Joe Pallas) (05/26/90)

In article <54873@microsoft.UUCP> jimad@microsoft.UUCP (Jim ADCOCK) writes:

>I'd claim in the following trivial example, the base class *is* broken --
>but you cannot tell that by testing the base class without deriving
>from it.  Conversely, the derived class *is not* broken -- but the 
>bug in the base class only shows up when testing the derived class.
>So I claim that inheritence *does* weaken encapsulation, and you *can*
>break a class by inheriting an implementation from it -- if the base
>class is never derived from, then the present implementation of the
>base class is just fine!  Only in the presence of derivation need the
>base class be fixed.

Does a class change somehow when you inherit from it?  I think not.
So how can the same class be both correct and incorrect?  Only if we
change our correctness criteria.  If the specification of a class says
nothing about inheritance (and, as I mentioned before, no one seems to
have a good way of specifying such things yet), then what happens when
you try to reuse the class cannot determine whether it meets its
specification.

If you make a decision to reuse code THAT MAKES NO PROMISE in its
specification about its reusability, then YOU must take the
responsibility for ascertaining its reusability---you are making a
decision at least as significant as any other implementation decision,
and the correctness of that decision is your responsibility.  If some
otherwise handy class is not reusable, you can go complain to whoever
wrote it, but you cannot blame that person for your decision to reuse
the code inappropriately.

joe

cox@stpstn.UUCP (Brad Cox) (05/27/90)

In article <54783@microsoft.UUCP> jimad@microsoft.UUCP (Jim ADCOCK) writes:
>In article <1990May20.154035.15064@axion.bt.co.uk> krichard@axion.bt.co.uk writes:
>>Features of OOP such as encapsulation and well-defined interfaces
>>no doubt facilitate the verification, validation and testing of
>>object-oriented programs.  On the other hand, others such as
>>inheritance and dynamic binding would appear to make testing more
>>difficult.
>>
In deciding whether object-oriented programs are more or less difficult to
test, we must be careful to not confuse the chicken and the egg. The chicken
is the object-oriented tools, where by 'object-oriented', I'm referring to
open universe languages (Smalltalk, ObjectiveC) that do not insist that the
relationship between parts and the whole be known and declared in advance,
at compile-time, as the universe is being created by the compiler. In
closed-universe languages (Ada, C++, Object-Pascal, etc), i.e. strongly
type-checked languages, all such relationships must be known and declared
at compile time.

Open universe languages simply provide support for solving open-universe
*problems* (collections of unknown components, pluggable software components,
software components marketplaces, etc).

But open universe problems are intrinsically more difficult to test than
closed universe problems, independently of the tools used to build them.

In other words, the testing difficulties arise from the nature of the problem,
not from the nature of the object-oriented tools used to solve them.

-- 

Brad Cox; cox@stepstone.com; CI$ 71230,647; 203 426 1875
The Stepstone Corporation; 75 Glen Road; Sandy Hook CT 06482

jimad@microsoft.UUCP (Jim ADCOCK) (05/30/90)

In article <5121@stpstn.UUCP| cox@stpstn.UUCP (Brad Cox) writes:
|The chicken
|is the object-oriented tools, where by 'object-oriented', I'm referring to
|open universe languages (Smalltalk, ObjectiveC) that do not insist that the
|relationship between parts and the whole be known and declared in advance,
|at compile-time, as the universe is being created by the compiler. In
|closed-universe languages (Ada, C++, Object-Pascal, etc), i.e. strongly
|type-checked languages, all such relationships must be known and declared
|at compile time.
|
|Open universe languages simply provide support for solving open-universe
|*problems* (collections of unknown components, pluggable software components,
|software components marketplaces, etc).
|
|But open universe problems are intrinsically more difficult to test than
|closed universe problems, independently of the tools used to build them.
|
|In other words, the testing difficulties arise from the nature of the problem,
|not from the nature of the object-oriented tools used to solve them.

Well, clearly I disagree with both Brad's notion of "object-oriented" and
"Open-Closed."

Ideally, since in real-world libraries over half the classes are leaf classes,
I'd like to be able to specify a leaf class as being such, and declare it
"Closed."  A class declared closed can no longer have its methods overridden,
changing their meanings of its methods.  This would allow a compiler to 
significantly optimize the closed class, and would allow testing the closed
class as a module with fixed meaning.  An object of a closed class could 
still be embedded [has-a], or inherited from for purposes of pure,
orthogonal, extension -- you can add methods, but not change existing
methods.

Classes defined as being inheritable, and whose methods can be overridden
would be considered "Open."  I don't know how you really test these, but
they still would be useful components for making closed modules.  And at
least closed modules can be reasonably tested.

rick@tetrauk.UUCP (Rick Jones) (05/30/90)

In article <54917@microsoft.UUCP> jimad@microsoft.UUCP (Jim ADCOCK) writes:
>In article <5121@stpstn.UUCP| cox@stpstn.UUCP (Brad Cox) writes:
>|The chicken
>|is the object-oriented tools, where by 'object-oriented', I'm referring to
>|open universe languages (Smalltalk, ObjectiveC) that do not insist that the
>|relationship between parts and the whole be known and declared in advance,
>|at compile-time, as the universe is being created by the compiler. In
>|closed-universe languages (Ada, C++, Object-Pascal, etc), i.e. strongly
>|type-checked languages, all such relationships must be known and declared
>|at compile time.
>|
>|- etc -
>
>Well, clearly I disagree with both Brad's notion of "object-oriented" and
>"Open-Closed."
>
>[discussion of open v. closed classes, etc]

I agree with Jim that you cannot limit the world view of object-oriented
programming to untyped languages of the Smalltalk school.  Static typing with
dynamic binding is much more appropriate in many (the majority?) of
applications.

The problem of safe inheritance has been nicely addressed in Eiffel in the form
of assertions, the most important of which is the "class invariant".  For those
who don't know Eiffel, this is a list of boolean expressions in a class whose
truth can be tested at run time every time a routine in the class returns to
the caller.  A failure of course generates a run-time exception.

With inheritance, the invariants of all parent classes are also checked, i.e.
invariants are inherited, and can only be added to, not replaced.  This allows
an inheriting class to overide a routine, but it must still conform to the
invariant.

There is a performance overhead in all this, so invariant checking is a
compile-time option, and can be used to "prove" the software works, then
compiled-out for a production version (assuming you're confident!).

Even if you're not using Eiffel (biased opinion: you should be :-), the idea
can be incorporated in other languages.  Even C has a simple "assert"
mechanism, has anyone tried extending this with C++?

The hardest part is coding discipline, to ensure that you correctly express the
semantics of the class within the assertions, but then coding discipline (or
lack of it) is often the root cause of an awful lot of software errors!

-- 
Rick Jones					You gotta stand for something
Tetra Ltd.  Maidenhead, Berks			Or you'll fall for anything
rick@tetrauk.uucp (...!ukc!tetrauk.uucp!rick)	     - John Cougar Mellencamp

render@m.cs.uiuc.edu (Hal Render) (05/31/90)

In article <480@tetrauk.UUCP> rick@tetrauk.UUCP (Rick Jones) writes:
>The problem of safe inheritance has been nicely addressed in Eiffel in the form
>of assertions, the most important of which is the "class invariant".  For those
>who don't know Eiffel, this is a list of boolean expressions in a class whose
>truth can be tested at run time every time a routine in the class returns to
>the caller.  A failure of course generates a run-time exception.
>
>With inheritance, the invariants of all parent classes are also checked, i.e.
>invariants are inherited, and can only be added to, not replaced.  This allows
>an inheriting class to overide a routine, but it must still conform to the
>invariant.

Although class invariants are A Good Thing, I think that a more direct 
mechanism that prevents a method from being redefined in a subclass
would be better in this case. There are already with different kinds
of inheritance/visibility constraints for methods in some OOPLs, and 
this one sounds neither far-fetched nor difficult to implement. 
Further, it avoids the need for "coding discipline" that is the cause
of many program errors.

hal.

mckenney@sparkyfs.istc.sri.com (Paul Mckenney) (06/01/90)

In article <1990May30.204110.22011@ux1.cso.uiuc.edu> render@m.cs.uiuc.edu.UUCP (Hal Render) writes:
>In article <480@tetrauk.UUCP> rick@tetrauk.UUCP (Rick Jones) writes:
>>The problem of safe inheritance has been nicely addressed in Eiffel in the form
>>of assertions, the most important of which is the "class invariant".  For those
>>who don't know Eiffel, this is a list of boolean expressions in a class whose
>>truth can be tested at run time every time a routine in the class returns to
>>the caller.  A failure of course generates a run-time exception.
>>
>>With inheritance, the invariants of all parent classes are also checked, i.e.
>>invariants are inherited, and can only be added to, not replaced.  This allows
>>an inheriting class to overide a routine, but it must still conform to the
>>invariant.
>
>Although class invariants are A Good Thing, I think that a more direct 
>mechanism that prevents a method from being redefined in a subclass
>would be better in this case. There are already with different kinds
>of inheritance/visibility constraints for methods in some OOPLs, and 
>this one sounds neither far-fetched nor difficult to implement. 
>Further, it avoids the need for "coding discipline" that is the cause
>of many program errors.
>
>hal.

One major reason that testing inherited classes becomes complex is that
classes often have member functions and variables that cooperate; these
members must therefore be overridden in groups (or, if only a single
member of the group is overridden, the programmer must very carefully
ensure that the overriding function fits properly with the other members
of the group).

So, one approach would be to allow the programmer to specify groups of
member functions/variable.  The compiler could then give errors is
some, but not all, members of a group were overridden.  If the programmer
``knows what he is doing'', he can give the equivalent of a cast:

	class  foo : public parent_of_foo

		. . .

		inline int 	bar() { parent_of_foo::bar(); }

		. . .

		};

by explicitly inheriting the parent's behavior.

This approach of course requires additions to the languages in order to
specify the member groups.  However, it is a good fit to the major problem
and adds absolutely no runtime overhead (at least for languages that allow
inlining).
				Thanx, Paul

wdavis@x102c.harris-atd.com (davis william 26373) (06/01/90)

In article <1990May30.204110.22011@ux1.cso.uiuc.edu> render@m.cs.uiuc.edu.UUCP (Hal Render) writes:
>In article <480@tetrauk.UUCP> rick@tetrauk.UUCP (Rick Jones) writes:
>>The problem of safe inheritance has been nicely addressed in Eiffel in the form
>>of assertions, the most important of which is the "class invariant".

(rest of the Eiffel discussion deleted)

>Although class invariants are A Good Thing, I think that a more direct 
>mechanism that prevents a method from being redefined in a subclass
>would be better in this case.

Why do you think it is better?

To have a class be able to specify that a method cannot be redefined
assumes that there is not a good reason for the redefinition.  Even if
there is no good reason at the time the class is written, there may
be a good reason in the future.  Just trying to determine if there
could be a good reason at the time a class is designed can become an
unbounded "what-if" game.

The benefit of being able to extend a class via inheritance in a way
that the original class implementor did not consider is a powerful
method of software development.  The "safe" aspect is the question of:
"What did the original implementor intend as the constraints?"
I think this is very well addressed in the notion of "Programming
by contract" that is described by Dr. Meyer as the basis for
the invariants used in Eiffel.

Do you have some equally compelling reasons why the prevention
should be considered better?

> There are already with different kinds
>of inheritance/visibility constraints for methods in some OOPLs, and 

Each constraint in a language comes because it has benefits that,
in the opinion of the language designer(s), outweigh the cost
that comes from not having the feature.  Just because other languages
have have certain restrictions, this does not mean those restrictions
would be a good idea in a new language or even a new version of the
same language.

>this one sounds neither far-fetched nor difficult to implement. 

Just because it is easy to implement does not mean it is a good idea.

>Further, it avoids the need for "coding discipline" that is the cause
>of many program errors.

Taking away valuable functionality because some people are going to
have problems using it is not always the best way to design a language.
Should we remove all loop constructs because it is possible to make
a mistake and have the loop not terminate?  We cannot have the language
ensure that all loops terminate (unless someone has solved the general
case of the "halting problem").

render@m.cs.uiuc.edu (Hal Render) (06/01/90)

In article <3754@trantor.harris-atd.com> wdavis@x102c.ess.harris.com writes:
>In article <1990May30.204110.22011@ux1.cso.uiuc.edu> render@m.cs.uiuc.edu writes:
>>Although class invariants are A Good Thing, I think that a more direct 
>>mechanism that prevents a method from being redefined in a subclass
>>would be better in this case.
>
>Why do you think it is better?

I think it is generally better to provide a direct solution to a problem
rather than a round-about one.  I have frequently had to rely on "coding
discipline" to do something that I felt a language should offer me as
a feature.  In this case, the problem is someone redefining a method so
that it no longer meets the original intent.  My principal problem with
relying on class invariants to prevent such redefinition is the difficulty
one often has in formulating a useful invariant for a particular piece of
code.  I admit that part of my lack of confidence in this may be due to 
the lack of practice I have had in formulating invariants.

>[Description of the value of redefinition ommitted.]
>Do you have some equally compelling reasons why the prevention
>should be considered better?

Preventing errors is always a tricky thing.  Upon reflection I do concede 
that it could cause just as many problems for the users to forbid them 
to redefine certain methods as it would be for the designer to formulate
invariants that are strong enough to prevent undesirable redefinitions
yet weak enough to allow desirable ones.  The problems are subtle, and
I will need to examine them more before I can say anything useful about 
the good/bad points of class invariants.

>>this [feature] sounds neither far-fetched nor difficult to implement. 
>
>Just because it is easy to implement does not mean it is a good idea.

True, but my point was that difficulty of implementation would not be a 
point against it.

>>Further, it avoids the need for "coding discipline" that is the cause
>>of many program errors.
>
>Taking away valuable functionality because some people are going to
>have problems using it is not always the best way to design a language.
>Should we remove all loop constructs because it is possible to make
>a mistake and have the loop not terminate?  We cannot have the language
>ensure that all loops terminate (unless someone has solved the general
>case of the "halting problem").

I think you're reaching on this one.  I did not propose outlawing
redefinition, only the ability to prevent it for specific methods.  
I see the value in such language-supported constraints because I am 
now in the process of writing a fairly complex Smalltalk application.
Frequently I find that I would like to be able to constrain the visibility
of methods to both subclasses and container classes to prevent unintended
violations of the semantics of the class.  Unfortunately, Smalltalk does not 
support such constraints, so consequently I must rely on "coding discipline" 
to safeguard myself against errors.  Since I am a fallible human, coding 
discipline has occasionally failed me, so I am leery of people who suggest
it as an alternative for more direct language support.   As I said, I do 
see the potential problems with being able to blindly restrict redefinition,
but I have also faced the problems that such redefinition can cause in 
the conceptual integrity of a class hierarchy.

Constraints, by their definition, are intended to prevent some sort of
activity.  We include them in a language to prevent "undesirable"
activities.  Of course, what seems undesirable for one person may seem 
highly desirable to another, so we have disputes such as this.  

hal.

pkr@media01.UUCP (Peter Kriens) (06/01/90)

Class invariants

The discussion about class invariants and static type checking
is a very crucial one. About this problem I have one question,
do the static typing and class invariants not limit the future
use of the code? Does the original programmer contain the wisdom
to foresee in what way his code will be reused? I have found
many times that I could reuse code which I was pretty sure
the original programmer could never have thought of. This gave
me reliable and debugged code. And doesn't this prevent more
errors than relying on type checking and forbidding people
to do something.

Besidedes doesnt the dynamic binding ease the debugging
process so much that there are hardly ever those obscure errors
that I remember from pascal and other "save" languages?

pkr
pkriens@media01

dcr0@GTE.COM (David Robbins) (06/01/90)

From article <1990May31.224646.15066@ux1.cso.uiuc.edu>, by render@m.cs.uiuc.edu (Hal Render):
> In article <3754@trantor.harris-atd.com> wdavis@x102c.ess.harris.com writes:
>>In article <1990May30.204110.22011@ux1.cso.uiuc.edu> render@m.cs.uiuc.edu writes:
>>>Although class invariants are A Good Thing, I think that a more direct 
>>>mechanism that prevents a method from being redefined in a subclass
>>>would be better in this case.
>>
>>Why do you think it is better?
> 
> I think it is generally better to provide a direct solution to a problem
> rather than a round-about one.

This last statement is really a *key* point.  At least part of the problem
under discussion here is that a subclass may redefine methods in a way that
breaks non-redefined methods.  In other words, the redefined method violates
some assumption the broken method makes.

While it is true that one way to avoid such problems is to prevent
redefinition, that is not really a direct solution to the problem.  The most
direct solution to the problem is to provide a means by which *every* method
could *completely* specify the assumptions it makes about other methods (and
variables), such that the compiler (or some other tool) could verify that
no assumption was violated.  This solution (were it feasible) would permit
the maximum possible flexibility in redefinition, without leaving the
prevention of such errors entirely up to "coding discipline" (which, as we
all know, is not quite infallible :-)).

But of course we recognize that the current state of the art falls a wee bit
short of being able to completely and formally specify all the assumptions
a method makes about the world around it.  So we must look around for a more
practical solution that at least partially gives us what we need.  Eiffel's
assertions are a definite step in the right direction.  The nature of these
assertions (pre- and post-conditions, class invariants) is precisely that they
say something about the assumptions a method (or a class) makes.

Assertions, as provided by Eiffel, have some definite shortcomings.  There are
many significant assumptions that are difficult or impossible to code in the
form of assertions, and others that can be coded, but are non-trivial to do
and/or expensive to actually check.  On the other hand, the very practice of
thinking about assertions helps to provide and focus some "coding discipline"
in a way that can significantly mitigate the problem under discussion.

In my own (somewhat limited) experience with object-oriented programming,
mostly in Smalltalk and Eiffel, I have encountered the problem of redefinition
breaking things that work, and have experienced the benefits of using
assertions.  It is certainly true that Smalltalk gives you no help in this;
your only recourse is to understand the parent class(es) well enough to know
what you can and cannot do when redefining a method.  If you spend enough
time in Smalltalk, you may actually get fairly good at this.  But I have also
found that the use of assertions in Eiffel is a big win, in more ways than
one.  Relative to the problem at hand, the use of assertions makes it much
easier for me to understand what I can and cannot do when redefining a method.
Even an assertion that can only be stated in the form of a comment helps.

I am always in favor of a language providing, somehow, a "direct solution"
to programming problems.  But what is a "direct solution"?  If the problem
is that a given method should absolutely never be redefined, then the language
should make it possible to say so.  If the problem is that a given method
should only be redefined in a manner that is consistent with certain
assumptions, then the language should make it possible to say so.  I suspect
that the problem is much more often the latter than the former; but I won't
claim that there is *never* a good reason to absolutely forbid redefinition.

The thing is, the former problem is easy to solve with today's language
technology, while the latter is hard to solve.  So, naturally, we may tend to
perceive the problem as one we have an easy solution for.  Someone once said
that if the only tool you have is a hammer, every problem tends to look like
a nail.  We need to continue to search for the right tool for the right job.
For the moment, we don't have all the right tools for prevention of the
problems that can be introduced by redefining methods; but we do have some
tools (e.g., assertions) that can help.
-- 
Dave Robbins                    GTE Laboratories Incorporated
drobbins@bunny.gte.com          40 Sylvan Rd.
...!harvard!bunny!drobbins      Waltham, MA 02254
CYA:  I speak only for myself; GTE may disagree with what I say.

plogan@mentor.com (Patrick Logan) (06/02/90)

In article <31960@sparkyfs.istc.sri.com> mckenney@sparkyfs.istc.sri.com (Paul Mckenney) writes:
  > One major reason that testing inherited classes becomes complex is that
  > classes often have member functions and variables that cooperate; these
  > members must therefore be overridden in groups (or, if only a single
  > member of the group is overridden, the programmer must very carefully
  > ensure that the overriding function fits properly with the other members
  > of the group).
  > 
  > So, one approach would be to allow the programmer to specify groups of
  > member functions/variable.  The compiler could then give errors is
  > some, but not all, members of a group were overridden.
  > 
  > This approach of course requires additions to the languages in order to
  > specify the member groups.  However, it is a good fit to the major problem
  > and adds absolutely no runtime overhead (at least for languages that allow
  > inlining).
  > 			   Thanx, Paul

At last Fall's Pacific Northwest Software Quality Conference, Fredrick
Hart of the Georgia Institute of Technology presented an interesting
paper titled "Constructing Reusable Software Using Feature Contexts".
(Co-authored by John Shilling.)

The abstract:

  "Explicit documentation of software features, using feature
   contexts, provides an effective way to create easily customized
   software components thus promoting reuse and enhancing
   maintainability."

The paper describes "features" as "logical units of program
functionality". And a "feature context" is "simply the realization of
a feature in the environment".

In this case the feature is "mutually dependent member functions and
data".  A feature context could be used to capture (realize, in their
words) this feature for use in the process of specializing super class
functionality in a subclass.

Features and feature contexts are loosely related to database views
and hypertext. I hope this description is somewhat clear. My point is
that the functionality described by Paul does not have to be a
programming language extension. It could be a part of the development
environment and applied to more than one language and to languages
that are not explicitly object oriented.

Proceedings of the conference I mentioned can be ordered for $30 from:
    PNSQC
    P.O. Box 970
    Portland, OR 97075

(It's the 1989 Pacific Northwest Software Quality Conference)

It is full of useful and interesting papers. I can't make individual
copies of this paper, sorry.

Patrick
-- 
Patrick Logan  uunet!mntgfx!plogan         |
Mentor Graphics Corp. 8500 SW Creekside Pl |
Beaverton, Oregon 97005-7191               |

cox@stpstn.UUCP (Brad Cox) (06/02/90)

In article <54917@microsoft.UUCP> jimad@microsoft.UUCP (Jim ADCOCK) writes:
>In article <5121@stpstn.UUCP| cox@stpstn.UUCP (Brad Cox) writes:
||Open universe languages simply provide support for solving open-universe
||*problems* (collections of unknown components, pluggable software components,
||software components marketplaces, etc).
|
|Ideally, since in real-world libraries over half the classes are leaf classes,
|I'd like to be able to specify a leaf class as being such, and declare it
|"Closed." 
|Classes defined as being inheritable, and whose methods can be overridden
|would be considered "Open." 

By "open universe languages", I was referring to encapsulation/binding, not
to inheritance, which is a different matter altogether.
-- 

Brad Cox; cox@stepstone.com; CI$ 71230,647; 203 426 1875
The Stepstone Corporation; 75 Glen Road; Sandy Hook CT 06482

rick@tetrauk.UUCP (Rick Jones) (06/04/90)

In article <9189@bunny.GTE.COM> dcr0@GTE.COM (David Robbins) writes:
>From article <1990May31.224646.15066@ux1.cso.uiuc.edu>, by render@m.cs.uiuc.edu (Hal Render):
>> In article <3754@trantor.harris-atd.com> wdavis@x102c.ess.harris.com writes:
>>>In article <1990May30.204110.22011@ux1.cso.uiuc.edu> render@m.cs.uiuc.edu writes:
>>>>Although class invariants are A Good Thing, I think that a more direct 
>>>>mechanism that prevents a method from being redefined in a subclass
>>>>would be better in this case.
>>>
>>>Why do you think it is better?
>> 
>> I think it is generally better to provide a direct solution to a problem
>> rather than a round-about one.
>
>This last statement is really a *key* point.  At least part of the problem
>under discussion here is that a subclass may redefine methods in a way that
>breaks non-redefined methods.  In other words, the redefined method violates
>some assumption the broken method makes.
>
> -- etc

I think David has put his finger on the nerve of this thread.  While most of
the work in the field of OOPL's seems to be focussing on the mechanics of the
languages, the wider issue of how they can support a reliable software design
and engineering method is not really being addressed.  As he says, Eiffel has
made a significant step in the right direction with assertions, which while
being far from perfect are a lot better than nothing.  They are in fact one
of the major reasons why I'm using the language.

Eiffel's assertions support a semi-formal definition of the class interface
in relation to clients.  This discussion has been about the definition of the
interface in relation to descendants, for which it (let alone any other
language) does not provide any mechanism.  I would very much like to see
Eiffel take a new lead in addressing this problem.

I believe that for software to be flexible and reliable, and for the great
Nirvana of "bags of reusable software components" to become a serious reality,
classes must *always* be extensible and adaptable by inheritance provided
that they conform to whatever interface constraints are specified for the
parent.  Ducking the problem by saying "non-redefinable" is not the real
answer;  the constraints must be good enough to say what you mean.

Traditionally issues like this have been regarded as part of the "development
environment", rather than the province of the language itself, but one of the
benefits of a good OO development strategy is that it can (and must)
integrate everything.  This ultimately means that OOPL designers have got to
start incorporating serious constraint definition into the languages as well
as all the standard stuff for expressing executable routines.

IMO the supposed quantum leap in software productivity and reliability will
not materialise and OO will all look like a lot of hype in a few years time
unless the languages take on a wider view of what "programming" is, and
embody capabilites which more represent "design & build".

-- 
Rick Jones					You gotta stand for something
Tetra Ltd.  Maidenhead, Berks			Or you'll fall for anything
rick@tetrauk.uucp (...!ukc!tetrauk.uucp!rick)	     - John Cougar Mellencamp