Re: Mixing OO and DB

From: David BL <davidbl_at_iinet.net.au>
Date: Tue, 19 Feb 2008 20:14:36 -0800 (PST)
Message-ID: <c5bd6b0e-4302-461c-8c97-e374f7b05ded_at_h11g2000prf.googlegroups.com>


On Feb 19, 11:18 pm, Robert Martin <uncle..._at_objectmentor.com> wrote:
> On 2008-02-18 21:09:48 -0600, David BL <davi..._at_iinet.net.au> said:
>
> > I have a more specific understanding of LSP. Let an object mean a
> > variable that has identity, state and behavior, but isn't assumed to
> > hold a value. If S,T are object types where S is a subtype of T then
> > LSP states that if q(x) is a property provable about objects x of type
> > T. Then q(y) should be true for objects y of type S.
>
> > In my mind LSP is inappropriate for value-types because it is
> > concerned with variables rather than values.
>
> Sorry to butt in but Liskov didn't make a distinction about
> value-types. The substitution principle simply says that every program
> P that works with T should also work with S.

I think you are oversimplifying what substitution means.

What exactly do you mean by "program"? It sounds like you refer to the entire source code for an application, but that would include the code that defines T and S. I presume therefore you are actually referring to substitution within some subset of the entire program. One could hardly expect substitution to be valid on every subset. How do you define a valid subset? Presumably it has something to do with being sufficiently self contained? Can you formalise that?

Moving on, you say that *every* program P that works with T should also work with S. Suppose S' is another subtype of T. Let program P be the following

    // declaration
    void foo(T* x);

    // call
    S' s;
    foo(&s);

We cannot replace T with S in this code, because the cast from S'* to S* isn't valid. So presumably you mean something else by "works with". What exactly? Can you formalise that?

Substitution can mean lots of different things. Eg it can relate to implicit compile time genericity in the source code, substitution of in-parameters, out-parameters, or type coercions of pointers. It follows that there are alternative notions of sub-typing (and inheritance as well).

C++ only makes a feeble attempt at supporting value types. For example when one writes

class B {};
class D : public B {};

the C++ compiler allows conversions as follows

    D x;
    B y = x; // ok : "value conversion"

    D* px;
    B* py = px; // ok : "pointer conversion"

    D** ppx;
    B** ppy = ppx; // Compiler error

It is worth asking why the first two are considered valid and the third is not. Also, how does this relate to an LSP notion of subtyping? In the interests of being precise I like to associate LSP specifically with the pointer conversion.

This pointer conversion is unreasonable for value-types. It is not at all surprising that C++ programmers don't think a circle is an ellipse. Eg suppose Circle subtypes Ellipse then

    Ellipse e;
    Circle c;
    Circle* px = &c;
    Ellipse* py = px; // pointer conversion     *py = e; // oops

> It is easy to envison a T and S that are pure value types. Imagine,
> for example, an Address type, and a subtype that supports 9 digit
> zipcodes. Any program written for Addess can safely use the subtype.
>
> BTW, while it is true that real numbers are a subtype of complex
> numbers (i.e. any algorithm that works when the I part is non-zero
> should work when the I part is zero)

What does that mean?

For example consider the following function to find roots of az^2 + bz + c = 0, where it is assumed 'a' is non zero.

pair<Complex,Complex> FindRoots(Complex a, Complex b, Complex c);

Equations with real valued coefficients can easily have complex valued roots. I think you need to elaborate on what you said above.

> , it is not true that Real inherits
> from Complex.

C.Date discusses a sense in which Real can subtype Complex, and Real can inherit all operations from Complex.

I don't consider LSP to be applicable to value-types. LSP is relevant to OO, and objects are variables not values. IMO the LSP notion of subtyping implies that a reference to a variable (ie object) can be passed to a function that expects a reference to a variable of any supertype. For value-types, this implies that subtype and supertype must represent the exact same set of values.

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. 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. Received on Wed Feb 20 2008 - 05:14:36 CET

Original text of this message