From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on polar.synack.me X-Spam-Level: X-Spam-Status: No, score=-0.3 required=5.0 tests=BAYES_00, REPLYTO_WITHOUT_TO_CC autolearn=no autolearn_force=no version=3.4.4 X-Google-Language: ENGLISH,ASCII-7-bit X-Google-Thread: 103376,2ea02452876a15e1 X-Google-Attributes: gid103376,public X-Google-Thread: 1108a1,c52c30d32b866eae X-Google-Attributes: gid1108a1,public X-Google-Thread: fac41,c52c30d32b866eae X-Google-Attributes: gidfac41,public From: donh@syd.csa.com.au (Don Harrison) Subject: Re: Real OO Date: 1996/05/14 Message-ID: X-Deja-AN: 154718314 sender: news@assip.csasyd.oz references: organization: CSC Australia reply-to: donh@syd.csa.com.au newsgroups: comp.lang.eiffel,comp.lang.ada,comp.object Date: 1996-05-14T00:00:00+00:00 List-Id: Tucker Taft writes: :Don Harrison (donh@syd.csa.com.au) wrote: :: Tucker Taft writes: : :: :Eiffel tries to support this effort through class invariants, but :: :I believe it gets tripped up a bit by the conflict between establishing :: :useful invariants for a specific type/class in the class :: :hierarchy, which might want to be quite "tight" so as to catch as :: :many errors as possible, and the appropriate invariants for the :: :entire class hierarchy, which want to be as "loose" as possible :: :to ensure flexibility of extension. There seems to be :: :a need to have two separate kinds of invariants -- those that apply to :: :an individual type/class, and a separate set that get "inherited", :: :and that apply throughout the class hierarchy. : :: Because Eiffel invariants are ANDed with ancestor invariants, they become :: successively tighter in descendants. Doesn't this fit the bill? : :No. : :The problem is that when you write an assertion, you must :decide whether you are trying to constrain the current type/class :as tightly as possible, to catch as many bugs in it as possible, or :to constrain it as loosely as possible, to allow as much flexibility :as possible in descendant type/classes. Don't think so. I think the objectives of catching bugs and making assertions about correct behaviour are the same. If you invent an assertion to assist in debugging, then it should imply a correctness condition. If you invent a correctness condition, it should also be valuable for debugging. What do others think? IMO, the issue is more one of determining the correct level of abstraction for an assertion. If it is placed at the correct level, then it will apply equally well to the current level and for all descendants. No greater flexibility in descendants should be allowable or needed. If a need for greater flexibility is perceived, then you know the design is incorrect in some way. The solution may be: a) Move the assertion lower down the inheritance hierarchy (If this implies duplicating it in several descendants, then you might consider grouping the common assertional behaviour by interposing an intermediate level of abstraction including the common assertion) b) Splitting it into a common component and a specific component and moving the specific component as in a) :Clearly, if the type is deferred/abstract, then the assertions :are only of interest in descendant types/classes, and so clearly :should be as loose as possible to maximize flexibility of :implementation. However, when you have a non-deferred/non-abstract :("concrete") class which nevertheless might have descendants, you end :up in the quandary. I don't think the generality of assertions for abstract and concrete classes should differ. In both cases, you are prescribing what their common behaviour should be. That common behaviour is expected of any legitimate children of that class. Whether or not that behaviour is actually implemented in the parent shouldn't matter, IMO. :It would be reasonable to have both kinds of assertions. : :Right now, I suspect a common scenario is that when someone first :writes a "concrete" type/class, they write the assertions :relatively tightly. Then, if they later start creating :descendants of it, they might discover an assertion that :is unnecessarily over constraining, and go back and loosen :it on the parent type/class. This loosening may eventually :result in overly weak assertions on the parent type/class, so :that future maintenance on the parent type/class itself might be :more likely to violate what must now be "unwritten" :assumptions/assertions. I think the situation you describe here should be dealt with by my suggestions above. :An alternative scenario might be to convert a concrete class :into an abstract/deferred one when it starts to run into :this problem, with the original concrete functionality being :moved down into a descendant, where the tighter assertions :may be retained. Better, IMO, would be to get the assertions right and leave the implementation where you intended it to be (if it's the right place). :Another way to look at the problem is whether the assertions :are focused on the "client" view or the "server" view. :For a deferred/abstract class, there is no real server, :so the assertions are clearly client oriented. However :for a concrete class, the client view and the server view :are different, and it makes sense to keep the client-oriented :assertions as weak as possible (but no weaker!), while making :a specific server's "local" assertions as strong as possible so :as to catch as many bugs as possible. Someone correct me if wrong, but in the contracting scheme of things, the client would need to be aware of *all* the requisite conditions that make a call legal, irrespective of whether they pertain to the target of the call or another object. In this sense, all preconditions are client-oriented. WRT assertions on abstract classes, they still express design intent that states preconditions that must be known to prevail by potential clients before they venture to call the operation. :The simplest solution might be to distinguish whether a given :assertion should apply only on a specific type/class, or should :apply on all descendant types/classes as well. Clients should of :course only be interested in those assertions that apply to all :descendant classes as well, whenever they want to operate without :knowledge of and/or dependence on the specific type/class :providing the implementation. ... which is always in Eiffel. Unless clients are aware of all conditions that are required for an operation to work properly, they can have no assurance that it will deliver what they expect. :-Tucker Taft stt@inmet.com http://www.inmet.com/~stt/ :Intermetrics, Inc. Cambridge, MA USA /// Don. (o o) =-=-=-=-=-=-=-=oOO=-(_)-=OOo=-=-=-=-=-=-=-=- Don Harrison donh@syd.csa.com.au