Re: Clean Object Class Design -- Circle/Ellipse

From: Dmitry Kazakov <dmitry_at_elros.cbb-automation.de>
Date: Mon, 27 Aug 2001 08:18:58 GMT
Message-ID: <3b89f7a6.1013500_at_news.cis.dfn.de>


On Sun, 26 Aug 2001 16:14:13 -0400, "Bob Badour" <bbadour_at_golden.net> wrote:

>>>>>>This definition of inheritance is in fact a LSP definition of a
>>>>>>constrained subtype. If you define subtypes as LSP-subtypes, then yes,
>>>>>>you can design Ellipse, so that no Circle cannot be made its subtype.
>>>>>
>>>>>Have you considered that one must apply LSP separately to variables and
>>>>>values. Values do not change -- ever. Variables do change. This makes
 them
>>>>>very different things.
>>>>
>>>>Surely. It is what I call in-, out- and inout-subtyping.
>>>>
>>>>>A circle value is always an ellipse value.
>>>>
>>>>Never. A circle value has the type Circle.
>>>
>>>It's most-specific type is Circle. A circle value also has the type
 Ellipse,
>>>due to the subtype relationship.
>>
>>It seems that you are mixing class-wide and specific values.
>
>You've lost me here. What exactly am I mixing? A value is a value.

Yes, and any value has a type.

>>Unfortunately most of OO languages do not differentiate them.
>
>Most OO languages do have the notion of a most-specific type, a declared
>type and a general (polymorphic) type.

Yes, but they do not differentiate specific and class-wide types. In C++ any value is considered depending on the context either as specific or as class-wide. A consequence of this is the necessity to always have type tag within the value. With in turn (1) makes impossible to have dispatching methods for small objects (like bool, int, double etc.) (2) makes multiple dispatch hard to implement (3) makes multiple inheritance space consuming

>>A specific Circle is not an instance of Ellipse. It is an instance of
>>Circle.
>
>An instance is not a value -- it is a variable.

No. An instance of a type is a value of that type. A variable of some given type may hold values (instances) of that type.

>I agree that Circle
>variables are not Ellipse variables and vice-versa. The same is true of any
>subtype variable and supertype variable, which is why I think well-thought
>languages must clearly distinguish between variable and value.

They are well distinguished in any language. More important is that one should distinguish between readonly (in) and full access (inout, out) variables.

>>In contrary to this both a class-wide Circle and a class-wide
>>Ellipse are instances of Ellipse'Class [not Ellipse!]. A class-wide
>>value consists of the type tag indicating the type and the value of
>>that type [In C++ the type tag is allways in the value, which leads to
>>confusion].
>
>Values have no type tags -- they just are. All of the above are simply
>implementation issues and are ultimately irrelevant to the discussion at
>hand -- as is the rest of your example.

Disagree. Values of class-wide types always have a tag associated with them. It is their key property. An representation issue is whether the tag is embedded in the value or not. Is it a pointer to vtab or an index, etc. But the tag *must* be present to have run-time dispatch.

>Your example did nothing to address the statement that a circle value, no
>matter the representation, is an ellipse. It has all the properties of an
>ellipse.

My point is that a circle value is not an ellipse. It mimics an ellipse, because there is a defined conversion to ellipse. The presense of the conversion makes possible to call ellipse's methods on circles. So one might imagine that circle is an ellipse. -1 is not double, yet fabs (-1) is legal.

>>>>When you
>>>>pass a cricle to a method of Ellipse, there is always a conversion
>>>>circle->ellipse [than could be null].
>>>
>>>This is not necessarily true and is ultimately an implementation issue.
>>
>>1. If you drop type conversions, you must obligatory have same
>>representation for all subtypes of a base type.
>
>One must have at least one possible representation for every type, and one
>can have several. This does not require any type conversion.

It would be to complex to have several representation for a type. How the compiler would distinguish them? If they are distinguishable, then why not to call them [sub]types? Different types? So you naturally come a cristal clear idea: a value has a type. A type has an implementation. Multiple representations/implementations can be easily achived via subtyping. It is actually what subtyping is for. Isn't it?

>>2. Conversion is not an implementation issue because it is exposed in
>>the subtype interface. A compiler can generate it for you in some
>>cases, but nevertheless it is in the interface.
>
>I disagree. Since the subtype has all of the properties of the supertype,
>any operation defined on Ellipse values can operate directly on Circle
>values without conversion.

No. Consider:

class Circle : public RosaElephant, public Ellipse {...}

You cannot pass such circle to an ellipse method without conversion.

>>>>In other words for any inherited
>>>>in-method f of Ellipse. There is a method f' of Circle defined as a
>>>>composition:
>>>>
>>>>Circle::f' = Ellipse:f o Circle::FromEllipse
>>>>
>>>>Although a circle value is not an ellipse value, there is a mapping
>>>>Circle::FromEllipse : circle->ellipse.
>>>
>>>These are all unecessary implementation details.
>>
>>Well, but how could it be done otherwise? I consider the above as a
>>definition of in-subtyping.
>
>It could be done without any conversion at all. In fact, physical
>independence almost requires it.

This

  1. Exposes representation, because it requires that a subtype have same representation as the base.
  2. Eliminates multiple inheritance.

Regards,
Dmitry Kazakov Received on Mon Aug 27 2001 - 10:18:58 CEST

Original text of this message