Re: Mixing OO and DB
Date: Thu, 21 Feb 2008 13:03:27 -0600
Message-ID: <2008022113032797157-unclebob_at_objectmentorcom>
On 2008-02-20 20:27:28 -0600, David BL <davidbl_at_iinet.net.au> said:
> On Feb 20, 11:32 pm, Robert Martin <uncle..._at_objectmentor.com> wrote:
>
> I thought you were saying before that S is a subtype of T if
> substitutability holds for all P. Now you are saying the subtype
> relationship is a function of P. Which is it?
The latter. If S is substitutable for T in P, then from P's point of view S is a subtype of T.
>> C++ programmers know that a circle is an ellipse.
>
> Sure they do. I was being facetious. I am a C++ programmer.
>
>> They also know that >> a program that represents a circle is not necessarily a program that >> represents an ellipse -- for the same reason that vector<circle> is not >> a subtype of vector<ellipse>.
>
> How does a program represent an ellipse? I would prefer it if you
> could be more careful with your terminology!
I think you know the answer to that. The source code in ellipse.{h,cpp} that defines the C++ class named ellipse *represents* an ellipse at runtime; but is not an ellipse.
>
>> >>> Eg suppose Circle subtypes Ellipse then >> >>> Ellipse e; >>> Circle c; >>> Circle* px = &c; >>> Ellipse* py = px; // pointer conversion >>> *py = e; // oops >> >> As I said. Exactly the same reasoning. A reference to S is not a >> subtype of a referenct to T.
>
> What has saying that a reference to S is not a subtype of a reference
> to T got to do with it? I thought that was only relevant to the B**,
> D** example.
>
> There is the following rule in C++
>
> S is subtype of T => allow implicit upcast S* to a T*
>
> I'm saying this rule is inappropriate for value types, because it is
> incompatible with Circle subtyping Ellipse. This is one of the main
> reasons why I say C++ doesn't support value types correctly.
I understand. The point is that a C++ class named "Circle" is not a circle. It "refers" to a circle in some sense; but it is not a circle in and of itself. The fact that the slicing rules of C++ make it impossible to properly upcast a Circle value to an Ellipse value is simply evidence that the Circle value is not a subtype of an Ellipse value. Nor should it be since a circle value is not a circle, and an Ellipse value is not an ellipse.
References do not inherit the subtyping of their referents.
>> We can define inheritance to mean anything we want. However, in the OO >> sense, a complex number composed of two real numbers cannot be the base >> class of the real numbers. If it were, then every real number would >> have two real numbers inside it...
>
> But why would anyone want to treat real or complex numbers "in the OO
> sense". These should obviously be treated as value types not object
> types.
For the same reason that people want to treat circle and ellipse in the OO sense. I once saw a geometry library where a circle was a subtype of arc. Same flaw.
> If you want to treat value types as object types, you will *always*
> find that subtyping is inappropriate amongst value types.
Not if they are substitutable. Again, the address with a zip code is substitutable for an address without one. It works as values just as well as it works as pointers.
>> >> struct Address { >> string street, city, state; >> >> }; >> >> struct AddressWithZip : Address { >> string zip; >> >> } >> >> void f(Address a); >> >> AddressWithZip az; >> f(az); // fine. Slices off the zip. Works great.
>
> I find that extremely ugly. It only takes another small step for the
> following
>
> struct Square { int width; };
> struct Rectangle : Square { int height; }
>
> void f(Square s);
>
> Rectangle r;
> f(r); // fine. Slices off the height. Works great.
But here you have made the error that the class of Square should be a subtype of the class of Rectangle, just because a true square is a true rectangle. That's the flaw! References do not inherit the subtype relationship of their referents.
>>> Here is a "proof": One expects variables of the supertype to support >>> assignment to any values of the supertype, hence we deduce that a >>> subtype is only allowed to contain a superset of all the values in a >>> supertype. >> >> Fine so far. subtype values must have every variable that the supertype has. >> >>> Also the function can read the value of the variable as a >>> value in the supertype, which shows that the values of the subtype >>> must be a subset of the values in the supertype. >> >> I didn't follow that one. Subtypes cannot contain subsets of variables >> of the supertype.
>
> I said values not variables. By definition a value type is a set of
> values plus operators on those values. Let S be a subtype of T. In C
> ++ an S* can be upcast to a T*. Let v denote some value in S stored
> in variable x (of type S). We can take the address of x, upcast it to
> a T* and dereference (to read the value v) and assume it is of type T.
> Therefore we have shown that upcasting pointers implies that the
> values in a subtype must be a subset of the values in a supertype.
You can what? If T does not have v then you cannot create a pointer to
a member variable S::v and try to access it on a T*. Sorry, the
language won't let you do that. You'd have to cast it back to an S*
Good thing too!
>
> Now of course this proof assumed that v wasn't sliced, so I guess you
> will say the proof is flawed.
It was flawed from the get-go. You cannot access a variable defined in a subclass using a pointer to the base class.
> However I find the idea that the
> compiler will silently slice away part of a value and allow you to
> reinterpret it as something else extremely suspicious.
You have to know that it's happening, of course, but other than that it's a perfectly reasonable action for a language to take, and it preserves LSP.
-- Robert C. Martin (Uncle Bob) | email: unclebob_at_objectmentor.com Object Mentor Inc. | blog: www.butunclebob.com The Agile Transition Experts | web: www.objectmentor.com 800-338-6716 |Received on Thu Feb 21 2008 - 20:03:27 CET