Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Design without a dynamic_cast

3 views
Skip to first unread message

Dmitry Shulga

unread,
Aug 30, 2002, 1:53:29 PM8/30/02
to
Hi. I have some base class B, I have other class C that maintains a vector of poiters to B. If I
have derivatives from B, say, D1, D2, that have additional member funcs, how could I access them
from a derivative of C, say, C1 *without a dynamic_cast*? How to change this structure to answer my
question?

class B{};
class D1 : public B
{
public:
void func1() {}
};
class D2 : public B
{
public:
void func2() {}
};

class C
{
protected:
vector<B*> _objs;
};

class C1 : public C
{
C1() { _obj.push_back(new D1); _obj.push_back(new D2); }
}

int main(...)
{
C1 c;
c->_objs[0]->func1(); //how?
c->_objs[1]->func2(); //how?
}

-d

Rolf Magnus

unread,
Aug 30, 2002, 3:16:52 PM8/30/02
to
Dmitry Shulga wrote:

> Hi. I have some base class B, I have other class C that maintains a vector
> of poiters to B. If I have derivatives from B, say, D1, D2, that have
> additional member funcs, how could I access them from a derivative of C,
> say, C1 *without a dynamic_cast*? How to change this structure to answer
> my question?

Put the functions as pure virtual functions into the base class and
implement them in the subclasses.

Carlos E. Cardenas S.

unread,
Aug 30, 2002, 3:16:45 PM8/30/02
to
Hi Dmitry,
I see, that you are using C++. In this case you need to insert in B func1 and func2, but they have
to be virtual. In the D1 and D2 you have also to declare func1 and func2 virtual.

Regards,
Carlos

Uncle Bob (Robert C. Martin)

unread,
Aug 30, 2002, 6:15:31 PM8/30/02
to

I'm not sure why you don't want to use dynamic_cast, since this
scenario is precisely what dynamic_cast was made for. But if you
really don't want to use it, then you can use the Visitor pattern.

class B
{
public:
virtual void accept(BVisitor*) = 0;
};

class BVisitor
{
public:
virtual void visit(D1*) = 0;
virtual void visit(D2*) = 0;
};

class D1 : public B
{
public:
void func1() {};

virtual void accept(BVisitor* v) {v.visit(this);}
};

class D2 : public B
{
public:
void func2() {};

virtual void accept(BVisitor* v) {v.visit(this);}
};

class Func1BVisitor : public BVisitor
{
public:
virtual void visit(D1* d1) {d1->func1();}
virtual void visit(D2*) {}; // perhaps throw exception.
};

class Func2BVisitor : public BVisitor
{
public:
virtual void visit(D1* ) {}; // perhaps throw exception.
virtual void visit(D2* d2) {d2->func2();}
};

main(...)
{
C1 c;
Func1BVisitor f1;
Func2BVisitor f2;
c->_objs[0].accept(f1);
c->_objs[1].accept(f2);
}

The Visitor pattern involves a technique called Dual Dispatch. You
can read about it in the Design Patterns book. You can also read
about it in "Design Patterns for dealing with dual inheritance
hierarchies": http://www.objectmentor.com/resources/articles/dih.pdf
and in "Acyclic Visitor"
http://www.objectmentor.com/resources/articles/acv.pdf


Robert C. Martin | "Uncle Bob"
Object Mentor Inc.| unclebob @ objectmentor . com
PO Box 5757 | Tel: (800) 338-6716
565 Lakeview Pkwy | Fax: (847) 573-1658 | www.objectmentor.com
Suite 135 | | www.XProgramming.com
Vernon Hills, IL, | Training and Mentoring | www.junit.org
60061 | OO, XP, Java, C++, Python |

You and I can enjoy the experience of not always seeing
eye-to-eye, but we can also respect each other, even when
you might find some idea of mine totally ludicrous.
-- Richard Riehle

Universe

unread,
Aug 30, 2002, 6:14:21 PM8/30/02
to
Rolf Magnus wrote:

> Dmitry Shulga wrote:

Design Patterns GoF calls this template subclasses or somesuch. They use
this most often in contradistinction to "casting about".

Elliott
--
http://www.radix.net/~universe ~*~ Enjoy! ~*~
Hail OO Modelling! * Hail the Wireless Web!
@Elliott 2002 my comments ~ newsgroups+bitnet OK

Uncle Bob (Robert C. Martin)

unread,
Aug 30, 2002, 6:16:58 PM8/30/02
to
On Fri, 30 Aug 2002 21:16:52 +0200, Rolf Magnus <rama...@t-online.de>
wrote:

I think the idea was that func1 cannot be used in D2, and func2 cannot
be used in D1. If that's so, then putting them both in B as pure
virtual functions violates the Liskov Substitution Principle (LSP) and
begs the question of how to implement func1 in D2 and vice versa.

Dmitry Shulga

unread,
Aug 30, 2002, 8:32:41 PM8/30/02
to

Exactly.

-d

Dmitry Shulga

unread,
Aug 30, 2002, 8:34:35 PM8/30/02
to

This is nice. I should not have forgetten to take a look at Gamma book. Thanks.

-d

Dmitry Shulga

unread,
Aug 30, 2002, 8:37:24 PM8/30/02
to
On 8/30/2002 5:15 PM, Uncle Bob (Robert C. Martin) wrote:

As for dynamic_cast then first, it costs a lot in RT and second, I consider using a dynamic_cast as
a bad programming slyle.

-d

Universe

unread,
Aug 30, 2002, 8:54:28 PM8/30/02
to
Dmitry Shulga wrote:

> On 8/30/2002 5:16 PM, Uncle Bob (Robert C. Martin) wrote:

>> On Fri, 30 Aug 2002 21:16:52 +0200, Rolf Magnus <rama...@t-online.de>
>> wrote:

>>>Dmitry Shulga wrote:
>>>>Hi. I have some base class B, I have other class C that maintains a vector
>>>>of poiters to B. If I have derivatives from B, say, D1, D2, that have
>>>>additional member funcs, how could I access them from a derivative of C,
>>>>say, C1 *without a dynamic_cast*? How to change this structure to answer
>>>>my question?

>>>Put the functions as pure virtual functions into the base class and
>>>implement them in the subclasses.

>> I think the idea was that func1 cannot be used in D2, and func2 cannot
>> be used in D1. If that's so, then putting them both in B as pure
>> virtual functions violates the Liskov Substitution Principle (LSP) and
>> begs the question of how to implement func1 in D2 and vice versa.

> Exactly.

Details. How where is LSP violated in the GoF's template mechanism which
they use in the Visitor pattern? That is essentially what Rolf Magnus us
laying out. At least that is what I unerstannd Magnus to be saying.

Also how is what Magnus' GoF Visitor, it seems he's explaining different
from RCM's "Acyclic" visitor that RCM obviously thinks doesn't have the
same LSP violation as that of GoFs?

[If you were agreeing that 2 buddies - social and or ideological - don't
get "really" mad at each other, then what's new?]

Uncle Bob (Robert C. Martin)

unread,
Aug 31, 2002, 12:58:37 AM8/31/02
to
On 31 Aug 2002 00:54:28 GMT, Universe <univer...@radix.net> wrote:

>Also how is what Magnus' GoF Visitor, it seems he's explaining different
>from RCM's "Acyclic" visitor that RCM obviously thinks doesn't have the
>same LSP violation as that of GoFs?

There is no LSP violation in GOF visitor.

Uncle Bob (Robert C. Martin)

unread,
Aug 31, 2002, 1:02:02 AM8/31/02
to
On Fri, 30 Aug 2002 19:34:35 -0500, Dmitry Shulga
<dmi...@cs.msstate.edu> wrote:

>This is nice. I should not have forgetten to take a look at Gamma book. Thanks.

Yes, it can be very useful. On the other hand, why are you interested
in avoiding dynanic_cast? Do you see that dynamic_cast and visitor
have a certain semantic equivalence? They also have differences.
Visitor is very fast, but introduces a rather nasty dependency cycle.
Dynamic_cast is a bit slow, but avoids the dependency cycle.

Daniel T.

unread,
Aug 31, 2002, 11:11:46 AM8/31/02
to
Dmitry Shulga <dmi...@cs.msstate.edu> wrote:

>Hi. I have some base class B, I have other class C that maintains a vector of
>poiters to B. If I
>have derivatives from B, say, D1, D2, that have additional member funcs, how
>could I access them
>from a derivative of C, say, C1 *without a dynamic_cast*? How to change this
>structure to answer my
>question?

I'm a little late comming in on this but I wanted to put my 2p. in.

The problem is that you have hidden information that some higher level
structure needs in order to do its job. You need to either expose the
information, or change the code so the higher level structure no longer
needs that information.


>class B{};
>class D1 : public B
>{
>public:
> void func1() {}
>};
>class D2 : public B
>{
>public:
> void func2() {}
>};
>
>class C
>{
>protected:
> vector<B*> _objs;
>};
>
>class C1 : public C
>{
> C1() { _obj.push_back(new D1); _obj.push_back(new D2); }
>}
>
>int main(...)
>{
> C1 c;
> c->_objs[0]->func1(); //how?
> c->_objs[1]->func2(); //how?
>}

So in the case above, you have decreed that 'main' needs to know which
objects being held by 'c' are 'D1' types and which are 'D2' types. You
can fix this problem either by exposing the information:

class C {
public:
vector<D1*> d1objs;
vector<D2*> d2objs;
};

Or you can make it so main doesn't need the information:

class C {
public:
virtual void doThing() = 0;
};

class C1: public C {
vector<D1*> d1objs;
vector<D2*> d2objs;
public:
virtual void doThing() {
// call func1 on d1objs;
// call func2 on d2objs;
}
};

int main() {
C1 c;
c.doThing();
}

Before someone asks something like, "But isn't this a pain for when
someone creates the D3 class?" My answer is yes, but it's a pain because
the design shows that D1 and D2 objects are not compatable at this level
(despite the fact that they derive from the same class,) so of course it
would be a pain to create yet another incompatable type.

Your best choices are to either expose the incompatibility, or change
the design so that they are compatable.

Tsolak Petrosian

unread,
Aug 31, 2002, 12:17:48 PM8/31/02
to
"Uncle Bob (Robert C. Martin)" <u.n.c.l.e.b.o.b.@.o.b.j.e.c.t.m.e.n.t.o.r.d.o.t.c.o.m> wrote in message news:<1vqvmukdsbgls26aa...@4ax.com>...

Just a note.
In your example interface B has direct exposure to its subtypes D1 and D2
through BVisitor which should definitely be avoided.

Tsolak Petrosian

Dmitry Shulga

unread,
Aug 31, 2002, 1:06:32 PM8/31/02
to

Okay, guys. I appreciate all comments I got. But I also became more and more sure that such cases
must not be in existance, code should not be written this way. Using simple derivation and further
detalization solves the problem. I do believe that RTTI is useful but should not be used if possible
since it makes a program slower. Using templates also helps.

-d

Uncle Bob (Robert C. Martin)

unread,
Aug 31, 2002, 11:10:03 PM8/31/02
to
On 31 Aug 2002 09:17:48 -0700, tso...@yahoo.com (Tsolak Petrosian)
wrote:

>Just a note.
>In your example interface B has direct exposure to its subtypes D1 and D2
>through BVisitor which should definitely be avoided.

Yes, this is the famous dependency cycle in the Visitor pattern. It
can cause some nasty compile-time thrashing when new derivatives are
added to the visited hierarchy. On the other hand, if the visited
hierarchy isn't volatile, then the cycle is harmless.

The cycle can be eliminated by using the Acyclic Visitor pattern
(which employs dynamic_cast or some other form of dynamic type
checking).

H. S. Lahman

unread,
Sep 1, 2002, 11:41:43 AM9/1/02
to
Responding to Shulga...

> Hi. I have some base class B, I have other class C that maintains a
> vector of poiters to B. If I have derivatives from B, say, D1, D2, that
> have additional member funcs, how could I access them from a derivative
> of C, say, C1 *without a dynamic_cast*? How to change this structure to
> answer my question?


If I understand this what you have is:

1 *
C ---------------- B
A
|
+-----+-----+
| |
D1 D2

where C needs to access the specific D1 and D2 specializations in
different contexts. The problem is that there is no instance of B. The
dynamic downcast represents one approach to navigating the relationship
path C -> B -> Dn by replacing B with a test of the subtype type. I
agree with you that I don't like dynamic downcast because it subverts
the notion of accessing B at the supertype level.

The answer is to instantiate direct relationships between Dn and C and
navigate those when C needs the specific specializations. There are a
couple of ways to do this. The obvious one is to instantiate them
directly as in:

1
+----------------------------------------+
| |
| 0..1 0..1 |
C -------------------- D1 -------+ |
| 0..1 | | *
| +-----> B
| 0..1 |
+--------------------- D2 -------+

where the 0..1 relationships represent the notion of the "current"
context of accessing a specific B. This tends to get messy if the
number of Dn specializations are large and it can also be messy to
instantiate the relationships. (Essentially the Visitor pattern is a
mechanism for instantiating the relationships systematically.)

One can get around these problems by introducing a collection class for
each Dn instead of collecting all Bs:

1 1 0..*
+------- D1C -------- D1 ---------+
| 1 |
C +-----> B
| 1 |
+------- D2C -------- D2 ---------+
1 1 0..*

but it is still messy if there are a lot of Dn.

Having said all that, none of these solutions are really attractive.
The core problem is that C must discriminate which specialization of B
it has in hand. That is, embedded in C's implementation is something like:

if <it's a D1> then
...
else
...

This is basically a context dependence within C's implementation and one
should look for ways to avoid it. The solutions above all strive to
remove the 'if' so that C "knows" it has a D1 when it navigates the D1
relationship. Regardless of the membership of B, navigating the
specific relationship always yields a D1. But that is only half the
problem.

The other half of the problem is that C's implementation may do things
differently if it is collaborating with a D1 than it would do when
collaborating with a D2. This is quite common in these sorts of
situations and it reflects another kind of context dependence.

The acid test is to look at what actually happens in the if...else...
clauses above. If all that happens is that one calls a method of Dn,
that's probably benign because one is just addressing a message in the
collaboration that announces what C has done. (In effect explicit
relationships provide this addressing to eliminate the if...else...)
But if C does something else as well its own behavior is dependent on
which subclass it has in hand, which is a no-no.

[Even if one is just addressing a message, the if...else... is probably
not a good idea on general principles. The reason is that in subsequent
maintenance it might be tempting to slip in some additional processing
that is context dependent. Without the clauses the maintainer probably
thinks just a bit more about where the behavior should go. A small
thing, but every bit helps.]

So I would check first for a context dependence in the behavior of C.
If there is one, figure out a way to reallocate the behavior in a
cleaner way (which will probably involve extracting another class or two
from the problem space). IME, getting the behaviors properly allocated
is very likely remove the B/Dn access problem as a side effect.


*************
There is nothing wrong with me that could
not be cured by a capful of Drano.

H. S. Lahman
h...@pathfindersol.com
Pathfinder Solutions -- We Make UML Work
http://www.pathfindersol.com
(888)-OOA-PATH


Dmitry Shulga

unread,
Sep 1, 2002, 1:15:11 PM9/1/02
to

Your elaborate post sounds like a conclusion of all we had here. As I have already said before,
having dynamic_casts in the program is a signal that there is something wrong with your design. You
have pointed out some workarounds that do solve the problem but yet remain it be a headache. The
goal of my question was to make sure that I was thinking right. And it was confirmed.

Thanks!

-d

Tsolak Petrosian

unread,
Sep 1, 2002, 8:17:07 PM9/1/02
to
No, casting has nothing to do with bad design; this is old and wrong
assumption.
In order to write dynamic system you have to use casting, its
inevitable and is right.
Unfortunately it doesn&#8217;t look good in static languages but is a
seamless part of dynamic languages.
That&#8217;s why instead of using awkward Visitor pattern do casting.

Tsolak Petrosian

Daniel T.

unread,
Sep 1, 2002, 9:06:40 PM9/1/02
to
tso...@yahoo.com (Tsolak Petrosian) wrote:

>No, casting has nothing to do with bad design; this is old and wrong
>assumption.

I can't agree with this, down casting makes it harder to define the
interface that works with a particular method...

def foo( object ):
object.do_foo()

With the above, it's obvious and static what interface 'object' must
support. (It must have the do_foo method.)

def badFoo( object ):
if object.has_foo():
object.do_foo()
elif object.has_bar():
object.do_bar()

With 'badFoo' defining the interface that 'object' must support has
suddenly become much tougher. (It must have a has_foo and has_bar
method, and can have do_foo or do_bar or both or neither, depending on
the what has_foo and has_bar return.)

>In order to write dynamic system you have to use casting, its
>inevitable and is right.
>Unfortunately it doesn&#8217;t look good in static languages but is a
>seamless part of dynamic languages.
>That&#8217;s why instead of using awkward Visitor pattern do casting.

Inevitable and correct casting looks good in static languages, they are
designed to automatically handle appropriate casting with no "mess".
Inappropriate casting is a mess no matter what the language used, it
just doesn't *look* as messy in dynamic languages.

Dmitry Shulga

unread,
Sep 1, 2002, 10:58:26 PM9/1/02
to

This is a far from the wrong assumption. But it might be old. I cannot give you a benchmarks of
measuring a performance of the systems with and without RTTI, but I bet the latter will beat the
former greatly. I cannot say I am a fun of a Visitor pattern so that is why I keep repeating that
such designs must not be used. If your program degradetes to dynamic casting then it is badly
written. Though dynmaic_cast was added purely because of the problems similar to this.

-d

Uncle Bob (Robert C. Martin)

unread,
Sep 2, 2002, 11:15:53 AM9/2/02
to
On Sun, 01 Sep 2002 21:58:26 -0500, Dmitry Shulga
<dmi...@cs.msstate.edu> wrote:

>This is a far from the wrong assumption. But it might be old. I cannot give you a benchmarks of
>measuring a performance of the systems with and without RTTI, but I bet the latter will beat the
>former greatly. I cannot say I am a fun of a Visitor pattern so that is why I keep repeating that
>such designs must not be used. If your program degradetes to dynamic casting then it is badly
>written. Though dynmaic_cast was added purely because of the problems similar to this.

dynamic_cast is a bit slower than Visitor; but I sincerly doubt that
you'd find many programs who's performance was measurably slower
simply because they used an occasional dynamic_cast.

I think it is wise to avoid cases where dynanic_cast must be used; but
I also think there are cases where it cannot be avoided. Therefore I
am a little concerned by your use of the term "must not be used".

H. S. Lahman

unread,
Sep 2, 2002, 11:54:07 AM9/2/02
to
Responding to Petrosian...

> No, casting has nothing to do with bad design; this is old and wrong
> assumption.


The problem is not casting per se, it is the particular type if casting.
If one has a relationship to a superclass that relationship means that
one doesn't care about any characteristics of the entity except those
defined in the superclass. The relationship is defining business rules
about what one entity exposes to another entity.

However, a downcast means that one does care about specializations not
defined in the superclass. That means that one has a design error
because the relationship is misplaced.

[In Daniel T.'s terms, the relationship defines the interface through
which the entity is accessed. Accessing the entity through any other
specialization interface is invalid relative to the business rules the
relationship is defining. See my post for another perspective on the
implied implementation level dependency. Either way the downcast
invites maintenance problems because one is navigating the wrong
relationship.]

H. S. Lahman

unread,
Sep 2, 2002, 12:22:21 PM9/2/02
to
Responding to Shulga...


> Your elaborate post sounds like a conclusion of all we had here. As I
> have already said before, having dynamic_casts in the program is a
> signal that there is something wrong with your design. You have pointed
> out some workarounds that do solve the problem but yet remain it be a
> headache. The goal of my question was to make sure that I was thinking
> right. And it was confirmed.


The interesting point is *why* it is wrong. The key issue is that a
relationship enforces business rules about what one entity exposes to
another entity. The use of a downcast violates those rules _by
definition_ because it exposes something about the specializations that
the relationship does not allow.

Thus the use of a downcast indicts a bad design because one is
navigating the wrong relationship. That bad design may be manifested in
difficulties with the interface (Daniel T.'s point) or it may be
manifested in implementation level context dependence (my point).

So the bottom line is that downcasts are not a headache; they are a
useful diagnostic of bad design. And the solution is to fix the design
rather than working around the downcast at the OOP level.

[Just because a feature is there doesn't mean it should be used. I can
remember a time when FORTRAN's assigned GOTO and COBOL's ALTER
statements were the epitome of Programmer Power. A decade later using
either one of them was grounds for summary dismissal in some shops.
More recently most OOPLs allowed direct access to public attributes but
a decade later private attributes and getters/setters became the Good
Practice de jour.]

Daniel T.

unread,
Sep 2, 2002, 7:20:41 PM9/2/02
to
"H. S. Lahman" <vze2...@verizon.net> wrote:
>Thus the use of a downcast indicts a bad design because one is
>navigating the wrong relationship. That bad design may be manifested in
>difficulties with the interface (Daniel T.'s point) or it may be
>manifested in implementation level context dependence (my point).
>
>So the bottom line is that downcasts are not a headache; they are a
>useful diagnostic of bad design. And the solution is to fix the design
>rather than working around the downcast at the OOP level.

Personally, I have used dynamic_cast. There have been several times when
I was at work at 8pm or later and needing to get something in before I
could go home...

Fixing the design is the best solution in the long run, but when you
have to get the build out tomorrow and your products maintenance phase
is near zero, anything goes! :-)

Tsolak Petrosian

unread,
Sep 2, 2002, 7:49:36 PM9/2/02
to
There is no strict rule where to use casting and how. Both up or down
casting are important and are used (at least by me) frequently.

For example in Jini which is based on Java when one looks up services
it gets them as type Object and then in turn down casts it to
appropriate type.

Or lets say we have type A which takes Object O1 of type B as
parameter in method M, but one of implementation of type A needs O1
not only be instance of B but also instance of C, what we do is try to
cast it to C and work with that type too. This is exactly why dynamic
languages are cleaner then static because we dont declare type of O1
in method signature and let the implementation to do the reflection,
although we can do the same in Java too by having O1 as type Object in
method signature but it will be less understandable and thats why we
declare the most important type and let the subclasses to do the
casting if necessary.

Also in real world if we need a person which is doctor and driver we
dont declare type DrivingDoctor but accept him/her as supertype Human
and then try to "cast" to type Doctor and Driver.

But of course as Daniel said one has to know when to use casting and
when not.

Tsolak Petrosian

Uncle Bob (Robert C. Martin)

unread,
Sep 2, 2002, 10:03:08 PM9/2/02
to
On Mon, 02 Sep 2002 16:22:21 GMT, "H. S. Lahman"
<vze2...@verizon.net> wrote:

>So the bottom line is that downcasts are not a headache; they are a
>useful diagnostic of bad design. And the solution is to fix the design
>rather than working around the downcast at the OOP level.

Downcasts *may* be indicative of a design flaw; but they aren't proof.
There are situations in which downcasts are harmless, or even
beneficial. Parallel inheritance hierarchies are a case in point.
Often in such hierarchies type information gets tranlsated from the
static to the dynamic realm. An object of type D is upcast to type B
and passed to a function that, because of the state of the system,
*knows* that the B is really a D. In such a case the downcast is not
a query, nor a test, it is simply a translation of type information
from the dynamic realm back to the static realm.

public class Employee {
}

public class HourlyEmployee extends Employee {
public void addTimeCard(TimeCard tc) {
...
}
}

public class EmployeeTransaction {
private Employee itsEmployee;
public EmployeeTransaction(Employee e) {
itsEmployee = e;
}
public void abstract execute();
protected Employee getEmployee() {
return itsEmployee;
}
}

public class AddTimeCardTransaction extends EmployeeTransaction {
TimeCard itsTimeCard = null;
public AddTimeCardTransaction(HourlyEmployee e,
Date date, double hours) {
super(e);
itsTimeCard = new TimeCard(date, hours);
}

public void execute() {
Employee e = getEmployee();
HourlyEmployee he = (HourlyEmployee) e; // downcast
he.addTimeCard(itsTimeCard);
}
}

In this case the creator of AddTimeCardTransaction *knows* that the
employee is an HourlyEmployee. The static type of this employee is
lost by the constructor, but is maintained by the state of the
addTimeCardTransaction object. When it comes time to call
addTimeCard, the static type is reasserted by the downcast.

So the type of the object was never lost, it just changed from static
knowledge to dynamic knowledge and back again.

H. S. Lahman

unread,
Sep 3, 2002, 11:38:53 AM9/3/02
to
Responding to Petrosian...

Let's be clear we are only talking about a very special case of casting:
downcasting to a specialization in an is-a relationship...

> There is no strict rule where to use casting and how. Both up or down
> casting are important and are used (at least by me) frequently.


There is for the special case of downcasting: Don't Do That. B-) A
downcast is a symptom of a design flaw where a relationship is misplaced
(i.e., the collaboration is assigned to the superclass rather than the
subclass where the problem space requires it to be). That is an
inescapable result of the OO definition of relationships, especially the
is-a.


>
> For example in Jini which is based on Java when one looks up services
> it gets them as type Object and then in turn down casts it to
> appropriate type.


This sort of registration process can be handled more generically and
robustly through an Observer of similar pattern so that the clients
never have to be touched when the services change.


>
> Or lets say we have type A which takes Object O1 of type B as
> parameter in method M, but one of implementation of type A needs O1
> not only be instance of B but also instance of C, what we do is try to
> cast it to C and work with that type too.


This is exactly the sort of thing that is fragile about downcasts. What
you are describing is:

1 *
A ----------------- O1
| |
+----+----+ +---+----+
| | | |
A1 A2 B C

But the problem is that A1 cannot collaborate with B; it can only
collaborate with C as a problem space constraint. That constraint is
not explicitly defined so it can only be enforced in the subclass
implementations (i.e., if...else...). That imposes context dependence
on those implementations. Keeping that straight as the trees get more
complex and the collaborations get more indirect through refactoring is
likely to eventually result in high risk shotgun refactoring.

Assuming A2 can collaborate with either B or C, then to properly capture
the problem space constraints one needs something like:

*
A +------- O1
| | |
+----+----+ | +---+----+
| | | | |
A1 A2 ---+ B C
| 1 1 | *
| |
+-----------------------------+

Once one codifies the problem space constraints correctly in the
relationships the need to downcast disappears (i.e., A1 will always
navigate to a C). More importantly, the A1/C relationship navigation is
explicitly identified for posterity. That means the maintainer can be
confident there will be no side effects on A1/C as subclasses of A and
O1 come and go. It also means that it will easy to recognize the
implications of a requirements change that does affect the collaboration
of A1 with O1.

> This is exactly why dynamic
> languages are cleaner then static because we dont declare type of O1
> in method signature and let the implementation to do the reflection,
> although we can do the same in Java too by having O1 as type Object in
> method signature but it will be less understandable and thats why we
> declare the most important type and let the subclasses to do the
> casting if necessary.


Dynamic binding only works when the types are compatible. The developer
is still responsible for providing a flow of control that will ensure
compatible instances for the collaboration.

In the downcast case one will get an error when the instance in hand is
a B and that error will have to be fielded in A1 -- which is an
implementation dependence because A1's implementation has to understand
O1's structure. This is double trouble. First one has the embedded
structural knowledge to expect the error. Then one uses the error
handling mechanism as a flow of control mechanism (i.e., one expects an
error that should not happen). Thus dynamic binding is just making the
design error of misplacing the relationship even more obvious.


>
> Also in real world if we need a person which is doctor and driver we
> dont declare type DrivingDoctor but accept him/her as supertype Human
> and then try to "cast" to type Doctor and Driver.


Actually, that is kind of the point. In the "real world" one *does*
need a driving doctor. That constraint needs to be explicitly enforced
in the relationships. (However, even a simple association can handle
this situation, so one is unlikely to try to capture that distinction in
an is-a relationship.)


>
> But of course as Daniel said one has to know when to use casting and
> when not.


I believe he was talking about casting in general vs. downcasting.

H. S. Lahman

unread,
Sep 3, 2002, 12:20:02 PM9/3/02
to
Responding to Daniel T....


> Personally, I have used dynamic_cast. There have been several times when
> I was at work at 8pm or later and needing to get something in before I
> could go home...
>
> Fixing the design is the best solution in the long run, but when you
> have to get the build out tomorrow and your products maintenance phase
> is near zero, anything goes! :-)


I see that as a process problem. B-)

<SOAPBOX>

The process should be repeatable and stable enough so that one doesn't
get into that situation (where one must throw out the process for a
deadline). In those rare situations where things do go wrong, the
organization should be mature enough to stick with the process and then
do process improvement after the fact to ensure the same root cause
doesn't occur again.

Unfortunately I spent most of my career hopping from one panic mode to
the next. Fortunately in the last decade we focused on process. I can
testify that software development is much more enjoyable when one can do
things right without overtime. Once one gets there, one never wants to
go back.

FWIW, I think Smalltalk, C++, OT, FP, P/R, .NET, translation, RAD,
client/server, N-tiers, etc., etc., etc. are all secondary issues.
[After 40+ years it all tends to just look like moving bits from one
pile to another. History also repeats itself; I ran across a journal
article where the author actually claimed the the first markup language
was invented in 1986! But I digress...]

The most important, challenging, and interesting problem facing the
industry today is creating good software development processes. We have
pretty much converted an art to a craft. If is now time to convert the
craft to an engineering discipline.

</SOAPBOX>

H. S. Lahman

unread,
Sep 3, 2002, 1:42:04 PM9/3/02
to
Responding to Martin...


>>So the bottom line is that downcasts are not a headache; they are a
>>useful diagnostic of bad design. And the solution is to fix the design
>>rather than working around the downcast at the OOP level.
>>
>
> Downcasts *may* be indicative of a design flaw; but they aren't proof.


No, they are *always* indicative of a misplaced relationship (as it is
in your example below). When one collaborates with the superclass one
*must* be able to collaborate with an instance any of its subclasses --
by definition. In fact, polymorphism is enabled by that definition;
without it polymorphic substitution could not work.

But the downcast means that one can only collaborate with some subset of
the subclasses. It implies a knowledge of the hierarchy that the
implementation of the caller must know about.


I hate these code examples; they're so verbose that it is difficult to
isolate the essentials. B-)) The distractions are things like what
"super(e)" is (an upcast, I assume) and why it is needed (it isn't).
It's even worse when one is only vaguely familiar with the language
(Java, I assume).

Anyway, the problem is that when AddTimeCardTransaction is created the
relationship to a HourlyEmployee specialization is defined in the
constructor argument. But then it is ignored when its "execute" method
is invoked. What needs to happen is to properly instantiate the
relationship navigation for "execute". One way to do that is to
initialize an "HourlyEmployee* he;" attribute in AddTimeCardTransaction
whose assignment replaces the upcast in the constructor. Now we can
eliminate the first two lines of "execute" and change "he." to "he->" in
the "execute" function to eliminate the downcast (assuming C++ syntax).

[There should be a sense of deja vu here because you provided a very
similar Employee/HourlyEmployee example awhile back to demonstrate
downcasts. In that one you even provided a UML model with the correct
relationship. However, like in this example, that relationship was
never instantiated and navigated in the actual code.]

Now let me anticipate two levels of response. The first is: why have a
redundant attribute to identify the employee? The answer is that the
attribute is essentially a referential attribute (foreign key) that just
represents instantiation of relationship navigation in a manner that
always ensures referential integrity. [Note that if itsEmployee was an
EmployeeID like SSN the problem would go away because it could be used
directly since it isn't tied to the type of Employee. The pointer is
just an optimization of attribute size vs. search overhead.]

The second is: what's the harm? In this case none as far as correct
behavior is concerned. I would argue, though, that this usage is more
obscure than implementing the relationship correctly and it will
probably be less efficient. More importantly, it is a very bad habit to
get into and it requires the developer to make decisions about whether
it really is dangerous on a case-by-case basis. Since the alternative
is clearer, consistent, and perhaps more efficient, that sort of
decision should be avoided.

In this sense it is akin to getter/setters. Sure there are situations
where one can be absolutely certain direct attribute access will never
be a problem. But rather than wasting time generating angst trying to
identify those situations we simply use getters/setters always.

[We've been down the road of the second point before. We have
established that I prefer discipline that prevents human error and you
prefer to maximize the opportunities for creativity. We aren't going to
budge, so let's not go there.]

Dmitry Shulga

unread,
Sep 3, 2002, 2:40:08 PM9/3/02
to
Gosh! I would never expect that the discussion will be so long :)

-d

Uncle Bob (Robert C. Martin)

unread,
Sep 3, 2002, 5:52:54 PM9/3/02
to
On Tue, 03 Sep 2002 17:42:04 GMT, "H. S. Lahman"
<vze2...@verizon.net> wrote:

>Responding to Martin...
>
>
>>>So the bottom line is that downcasts are not a headache; they are a
>>>useful diagnostic of bad design. And the solution is to fix the design
>>>rather than working around the downcast at the OOP level.
>>>
>>
>> Downcasts *may* be indicative of a design flaw; but they aren't proof.
>
>
>No, they are *always* indicative of a misplaced relationship (as it is
>in your example below). When one collaborates with the superclass one
>*must* be able to collaborate with an instance any of its subclasses --
>by definition. In fact, polymorphism is enabled by that definition;
>without it polymorphic substitution could not work.
>
>But the downcast means that one can only collaborate with some subset of
>the subclasses. It implies a knowledge of the hierarchy that the
>implementation of the caller must know about.

In general I agree that when you collaborate with a superclass you
have to be able to collaborate with all its subclasses. (i.e. LSP)
However, a violation of LSP is a design *choice*, not a design flaw.

This particular example does not violate LSP. Notice, in the code
below, that there is no way to pass the wrong type. The
HourlyEmployee is passed to the AddTimeCardTransaction which then
passes it to its base class. The base class simply puts it in a field
typed: Employee. Later, when execute is called, the HourlyEmployee is
pulled back out of the field. This object *must* be an
HourlyEmployee. There is no way for it to be anything else.
Therefore the cast is simply a way of asserting what is already known.
No type information has been lost.


>> public class Employee {
>> }
>>
>> public class HourlyEmployee extends Employee {
>> public void addTimeCard(TimeCard tc) {
>> ...
>> }
>> }
>>
>> public class EmployeeTransaction {
>> private Employee itsEmployee;
>> public EmployeeTransaction(Employee e) {
>> itsEmployee = e;
>> }
>> public void abstract execute();
>> protected Employee getEmployee() {
>> return itsEmployee;
>> }
>> }
>>
>> public class AddTimeCardTransaction extends EmployeeTransaction {
>> TimeCard itsTimeCard = null;
>> public AddTimeCardTransaction(HourlyEmployee e,
>> Date date, double hours) {
>> super(e);
>> itsTimeCard = new TimeCard(date, hours);
>> }
>>
>> public void execute() {
>> Employee e = getEmployee();
>> HourlyEmployee he = (HourlyEmployee) e; // downcast
>> he.addTimeCard(itsTimeCard);
>> }
>> }
>>

>I hate these code examples; they're so verbose that it is difficult to
>isolate the essentials. B-))

They are also the only way to be unambiguous.

>The distractions are things like what
>"super(e)" is (an upcast, I assume) and why it is needed (it isn't).

Super is a call to the base class constructor.

>It's even worse when one is only vaguely familiar with the language
>(Java, I assume).

Yes.

>Anyway, the problem is that when AddTimeCardTransaction is created the
>relationship to a HourlyEmployee specialization is defined in the
>constructor argument. But then it is ignored when its "execute" method
>is invoked. What needs to happen is to properly instantiate the
>relationship navigation for "execute". One way to do that is to
>initialize an "HourlyEmployee* he;" attribute in AddTimeCardTransaction
>whose assignment replaces the upcast in the constructor. Now we can
>eliminate the first two lines of "execute" and change "he." to "he->" in
>the "execute" function to eliminate the downcast (assuming C++ syntax).

Yes, this is the "Intelligent Children" design pattern. (See:
http://www.objectmentor.com/resources/articleTeasers/dualInheritance)

This simple code *could* be reconstructed in the fashion you suggest;
however, let's say that there is some indispensible service performed
by the base class that makes it inconvenient to put an HourlyEmployee
instance in the derived class.

>[There should be a sense of deja vu here because you provided a very
>similar Employee/HourlyEmployee example awhile back to demonstrate
>downcasts. In that one you even provided a UML model with the correct
>relationship. However, like in this example, that relationship was
>never instantiated and navigated in the actual code.]

Yes, it's the same basic code. The UML diagram I drew then used a
"dependency" relationship between AddTimeCardTransaction and
HourlyEmployee. Or perhaps it was a <<parameter>> association. These
relationships are not instantiated with fields. They are dynamic in
nature coming from arguments or return values. The relationship *is*
traversed, by the way, it is traversed by the 'he' variable.

>Now let me anticipate two levels of response. The first is: why have a
>redundant attribute to identify the employee? The answer is that the
>attribute is essentially a referential attribute (foreign key) that just
>represents instantiation of relationship navigation in a manner that
>always ensures referential integrity. [Note that if itsEmployee was an
>EmployeeID like SSN the problem would go away because it could be used
>directly since it isn't tied to the type of Employee. The pointer is
>just an optimization of attribute size vs. search overhead.]

OK, lets assume we have a function:

Employee getEmployee(String ssn);

We could assume that AddTimeCardTransaction could call this function
to get an Employee. Then it would have to cast the employee to an
HourlyEmployee in order to call addTimeCard.


>The second is: what's the harm? In this case none as far as correct
>behavior is concerned. I would argue, though, that this usage is more
>obscure than implementing the relationship correctly and it will
>probably be less efficient.

It is certainly less efficient than saving the HourlyEmployee in the
AddTimeCardTransaction. However, it is not "inefficient" per se.
Morevoer, if we were to fetch the employee from its SSN, we'd still
have to do the cast.

>More importantly, it is a very bad habit to
>get into and it requires the developer to make decisions about whether
>it really is dangerous on a case-by-case basis. Since the alternative
>is clearer, consistent, and perhaps more efficient, that sort of
>decision should be avoided.

I don't like that kind of reasoning. Casts can be dangerous, so avoid
all casts. Multiple inheritance is dangerous so avoid all multiple
inheritance. Inheritance is dangerous... switch is dangerous... if
is dangerous... programming is dangerous...

No, I want to *understand* what the issues are and make good design
choices based upon that understanding. There are cases where casts of
the form described above are harmless and convenient.

BTW, presume we had written this in Ruby, or Python. The logic would
be identical, but there would have been no cast because there would
have been no static type information.

>In this sense it is akin to getter/setters. Sure there are situations
>where one can be absolutely certain direct attribute access will never
>be a problem. But rather than wasting time generating angst trying to
>identify those situations we simply use getters/setters always.

I don't. I *often* use getters and setters in those situations where
getters and setters are necessary and are not indicative of a design
flaw (which often they are). But there are times when I use a raw
public variable.

>[We've been down the road of the second point before. We have
>established that I prefer discipline that prevents human error and you
>prefer to maximize the opportunities for creativity. We aren't going to
>budge, so let's not go there.]

But you went there...

Uncle Bob (Robert C. Martin)

unread,
Sep 3, 2002, 5:54:33 PM9/3/02
to
On Tue, 03 Sep 2002 13:40:08 -0500, Dmitry Shulga
<dmi...@cs.msstate.edu> wrote:

>Gosh! I would never expect that the discussion will be so long :)
>

It's acutally a pretty interesting topic. The proscription of
downcasts is common enough, but not well justified. It has become a
kind of religious decree that all programmers are expected to obey,
without question.

I think questioning these things is a worthwhile activity. And the
discussion that springs up is often illuminating.

Costin Cozianu

unread,
Sep 3, 2002, 6:28:39 PM9/3/02
to
These kind of generalization and absolute rules are bogus, especially in
matters related to OO design.


>> Downcasts *may* be indicative of a design flaw; but they aren't proof.
>
> No, they are *always* indicative of a misplaced relationship (as it is
> in your example below).

Your assertion is trivially false.

Most of the times they are indicative of the clumsiness of the type
system. Proper type systems are not easy to implement and especially in
OO domain they are a rara avis. So before shouting against downcasts
make sure you are able to construct a fabulous type system yourself,
we'd all be grateful.

When the type system doesn't help you , you have a "loss of type"
phenomena to which you need to apply the only legitimate solution:
downcasting. The alternatives are to allow redundancy either in code or
data structures, which is generally worse for obvious reasons.

> When one collaborates with the superclass one
> *must* be able to collaborate with an instance any of its subclasses --
> by definition.
>
> In fact, polymorphism is enabled by that definition;
> without it polymorphic substitution could not work.

There are several types of polymorphism, some of which do not imply
substitution in the LSP sense. Common examples are: implementation
inheritance (without subtyping) and multi-methods.

>
> But the downcast means that one can only collaborate with some subset of
> the subclasses. It implies a knowledge of the hierarchy that the
> implementation of the caller must know about.

The base class A is declared to collaborate with type B and the derived
class A' is semantically restricted (by integrity constraints) to
"collaborate" with subtype B'<B. Class A' and A need not be in a subtype
relation in the LSP sense

<snip other points, they were addressed by RCM>

> Anyway, the problem is that when AddTimeCardTransaction is created the
> relationship to a HourlyEmployee specialization is defined in the
> constructor argument. But then it is ignored when its "execute" method
> is invoked.

That's very simple to explain: the type system (in this case Java)
cannot handle the proper design constructs. It happens very often in
Java as well as with other languages. You have to choose the lesser
evil: redundancies or downcasts.

Can you really claim that redundancies are better than downcasts ?

You can find a detailed discussion of how and why these things happen in
the Kim Bruce's "Foundation of Object Oriented Languages: Types and
Semantics".

Costin Cozianu

Tsolak Petrosian

unread,
Sep 3, 2002, 10:25:02 PM9/3/02
to
I cant see how we can capture this into code without downcasting.


*
A +------- O1
| | |
+----+----+ | +---+----+
| | | | |
A1 A2 ---+ B C
| 1 1 | *
| |
+-----------------------------+

>Dynamic binding only works when the types are compatible. The


>developer is still responsible for providing a flow of control that
will >ensure compatible instances for the collaboration.

>In the downcast case one will get an error when the instance in hand
>is a B and that error will have to be fielded in A1 -- which is an
>implementation dependence because A1's implementation has to
>understand O1's structure. This is double trouble. First one has
the >embedded structural knowledge to expect the error. Then one uses
the
>error handling mechanism as a flow of control mechanism (i.e., one
>expects an error that should not happen). Thus dynamic binding is
>just making the design error of misplacing the relationship even more
>obvious.

Nobody assumed we are going to cast blindly, that's why I said
developer has to use reflection before casting if needed.

There is no error situation. Both implementation can be sure that O1
will always be of type B, but the implementation A2 does extra
checking to see if O1 is also of type C to do additional work with O1
or even throw exception since the contract says that M can throw
exception any time whether because of I/0 or simple O1 is not of type
C.

>> Also in real world if we need a person which is doctor and driver
we
>> dont declare type DrivingDoctor but accept him/her as supertype
>>Human and then try to "cast" to type Doctor and Driver.

>Actually, that is kind of the point. In the "real world" one *does*
>need a driving doctor. That constraint needs to be explicitly
>enforced in the relationships. (However, even a simple association
>can handle this situation, so one is unlikely to try to capture that
>distinction in an is-a relationship.)

No there is no such thing as driving doctor (no such understanding or
profession). What we have is someone who IS doctor AND a driver.
I guess you wouldn't define type DrivingDoctor if modeling this
situation?

Tsolak Petrosian

Daniel T.

unread,
Sep 3, 2002, 10:39:48 PM9/3/02
to
"Uncle Bob (Robert C. Martin)"
<u.n.c.l.e.b.o.b.@.o.b.j.e.c.t.m.e.n.t.o.r.d.o.t.c.o.m> wrote:

>On Mon, 02 Sep 2002 16:22:21 GMT, "H. S. Lahman"
><vze2...@verizon.net> wrote:
>
>>So the bottom line is that downcasts are not a headache; they are a
>>useful diagnostic of bad design. And the solution is to fix the design
>>rather than working around the downcast at the OOP level.
>
>Downcasts *may* be indicative of a design flaw; but they aren't proof.
>There are situations in which downcasts are harmless, or even
>beneficial. Parallel inheritance hierarchies are a case in point.
>Often in such hierarchies type information gets tranlsated from the
>static to the dynamic realm. An object of type D is upcast to type B
>and passed to a function that, because of the state of the system,
>*knows* that the B is really a D. In such a case the downcast is not
>a query, nor a test, it is simply a translation of type information
>from the dynamic realm back to the static realm.

Off we go again. I think we do this one about twice a year don't we? :-)

>public class Employee {
>}
>
>public class HourlyEmployee extends Employee {
> public void addTimeCard(TimeCard tc) {
> ...
> }
>}
>
>public class EmployeeTransaction {
> private Employee itsEmployee;
> public EmployeeTransaction(Employee e) {
> itsEmployee = e;
> }
> public void abstract execute();
> protected Employee getEmployee() {
> return itsEmployee;
> }
>}
>
>public class AddTimeCardTransaction extends EmployeeTransaction {
> TimeCard itsTimeCard = null;
> public AddTimeCardTransaction(HourlyEmployee e,
> Date date, double hours) {
> super(e);
> itsTimeCard = new TimeCard(date, hours);
> }
>
> public void execute() {
> Employee e = getEmployee();
> HourlyEmployee he = (HourlyEmployee) e; // downcast
> he.addTimeCard(itsTimeCard);
> }
>}

Why not just:

public class TimeCard {
public TimeCard( Date date, double hours ) {
// ...
}
}

public class HourlyEmployee {
public void addTimeCard( TimeCard tc ) {
//...
}
}

interface EmployeeTransaction {
public void execute();
}

public class AddTimeCardTransaction implements EmployeeTransaction {
private TimeCard itsTimeCard = null;
private HourlyEmployee itsEmployee = null;

public AddTimeCardTransaction( HourlyEmployee e,
Date date, double hours ) {
itsEmployee = e;
itsTimeCard = new TimeCard( date, hours );
}
public void execute() {
itsEmployee.addTimeCard( itsTimeCard );
}
}

Even if code is introduced that would require the Employee class to
return, there is no justification for EmployeeTransaction to have an
'itsEmployee' field and no justification for the downcast.

Uncle Bob (Robert C. Martin)

unread,
Sep 3, 2002, 11:44:19 PM9/3/02
to
On Wed, 04 Sep 2002 02:39:48 GMT, "Daniel T." <notda...@gte.net>
wrote:

>Off we go again. I think we do this one about twice a year don't we? :-)

It is a recurring topic, yes. But then there are new people around.

>Even if code is introduced that would require the Employee class to
>return, there is no justification for EmployeeTransaction to have an
>'itsEmployee' field and no justification for the downcast.

Daniel, we *have* been through this before, so you don't need to start
with the trivial objections. Remember, this is the part where I claim
that the variable in the base class was just a pedagogical
convenience. In reality the argument to the AddTimeCardTransaction
constructor was the key into the Employee database, and the
getEmployee method of EmployeeTransaction holds the code that reads
the database given the key.

Anyway, if you recall, we went through the whole discussion eight
months ago and found that in the general case the solutions that
managed to get rid of the downcast were variants of the Visitor
pattern; which, in turn, is semantically equivalent to a downcast.

H. S. Lahman

unread,
Sep 4, 2002, 12:36:38 PM9/4/02
to
Responding to Petrosian...

> I cant see how we can capture this into code without downcasting.
>
>
> *
> A +------- O1
> | | |
> +----+----+ | +---+----+
> | | | | |
> A1 A2 ---+ B C
> | 1 1 | *
> | |
> +-----------------------------+


There are several ways. The most likely is to have a generic collection
class for each relationship (just as one would have for the single A/O1
relationship). The A2/O1 collection class would have pointers to an O1
type and membership could be either Bs or Cs. The A1/C collection would
have pointers to a C type and memebrship would be restricted to Cs.
When A1 needs to get to a C it does so through that collection class.
By traversing that relationship A1 is guaranteed to always get Cs back
and no knowledge of O1's hierarchy is needed in its implementation.


>
>
>>Dynamic binding only works when the types are compatible. The
>>developer is still responsible for providing a flow of control that
>>will ensure compatible instances for the collaboration.
>
>
>>In the downcast case one will get an error when the instance in hand
>>is a B and that error will have to be fielded in A1 -- which is an
>>implementation dependence because A1's implementation has to
>>understand O1's structure. This is double trouble. First one has
>>the embedded structural knowledge to expect the error. Then one uses
>>the error handling mechanism as a flow of control mechanism (i.e., one
>>expects an error that should not happen). Thus dynamic binding is
>>just making the design error of misplacing the relationship even more
>>obvious.
>>
>
> Nobody assumed we are going to cast blindly, that's why I said
> developer has to use reflection before casting if needed.
>
> There is no error situation. Both implementation can be sure that O1
> will always be of type B, but the implementation A2 does extra
> checking to see if O1 is also of type C to do additional work with O1
> or even throw exception since the contract says that M can throw
> exception any time whether because of I/0 or simple O1 is not of type
> C.


I'm afraid I don't follow this. Generalizations cannot be reflective;
only associations and dependencies can. Also, if B and C are subclasses
of O1 their membership must be disjoint subsets of O1 so a B cannot also
be a C. (There is an exception for multiple inheritance relationships
from the same superclass, but let's not go there.)

So I get the impression you are thinking of other casting contexts than
downcasting. Downcasting is unique to generalization relationships in
that it casts a superclass to a specific specialization subclass. In
languages that support downcasting, attempting a downcast on an object
that is actually of a different subclass than that specified by the
downcast is an error. [An exception may not be thrown but then the
error will be indicated in the returned reference (e.g., a NULL pointer).]


>
>
>>>Also in real world if we need a person which is doctor and driver
>>>we
>>>dont declare type DrivingDoctor but accept him/her as supertype
>>>Human and then try to "cast" to type Doctor and Driver.
>>>
>
>>Actually, that is kind of the point. In the "real world" one *does*
>>need a driving doctor. That constraint needs to be explicitly
>>enforced in the relationships. (However, even a simple association
>>can handle this situation, so one is unlikely to try to capture that
>>distinction in an is-a relationship.)
>>
>
> No there is no such thing as driving doctor (no such understanding or
> profession). What we have is someone who IS doctor AND a driver.
> I guess you wouldn't define type DrivingDoctor if modeling this
> situation?


As I said, I don't think is-a is the way to describe that constraint.
It can be enforced with a simple association:

* calls
Patient -------------------------- Doctor
drives to 1

When a relationship is instantiated between a particular Patient and a
particular Doctor, whoever instantiates that relationship must ensure
that the candidate Doctor qualifies for the role by also being a driver.

H. S. Lahman

unread,
Sep 4, 2002, 2:03:31 PM9/4/02
to
Responding to Martin...


>>No, they are *always* indicative of a misplaced relationship (as it is
>>in your example below). When one collaborates with the superclass one
>>*must* be able to collaborate with an instance any of its subclasses --
>>by definition. In fact, polymorphism is enabled by that definition;
>>without it polymorphic substitution could not work.
>>
>>But the downcast means that one can only collaborate with some subset of
>>the subclasses. It implies a knowledge of the hierarchy that the
>>implementation of the caller must know about.
>>
>
> In general I agree that when you collaborate with a superclass you
> have to be able to collaborate with all its subclasses. (i.e. LSP)
> However, a violation of LSP is a design *choice*, not a design flaw.


I agree LSP is a specific design technique. But I see downcasting as a
more fundamental issue of how OT defines collaborations, which makes a
misplaced relationship a design flaw.

For that matter, the downcast itself is a low level OOP issue. In an
OOPL that did not support downcasts one would have to find some other
tactic to navigate the relationship. Yet the <correct> relationships at
the OOA level representing the problem space would remain unchanged in
terms of OO fundamentals. IOW, the decisions made in OOD/P have a lot
of latitude but one cannot arbitrarily ignore the fundamentals.

>>I hate these code examples; they're so verbose that it is difficult to
>>isolate the essentials. B-))
>>
>
> They are also the only way to be unambiguous.

>
>
>>The distractions are things like what
>>"super(e)" is (an upcast, I assume) and why it is needed (it isn't).
>>
>
> Super is a call to the base class constructor.


Not so ambiguous when one doesn't know the language, is it? An OOA
model, OTOH, is unambiguous, far more compact, AND language independent
when talking about OO fundamentals.


>>Anyway, the problem is that when AddTimeCardTransaction is created the
>>relationship to a HourlyEmployee specialization is defined in the
>>constructor argument. But then it is ignored when its "execute" method
>>is invoked. What needs to happen is to properly instantiate the
>>relationship navigation for "execute". One way to do that is to
>>initialize an "HourlyEmployee* he;" attribute in AddTimeCardTransaction
>>whose assignment replaces the upcast in the constructor. Now we can
>>eliminate the first two lines of "execute" and change "he." to "he->" in
>>the "execute" function to eliminate the downcast (assuming C++ syntax).
>>
>
> Yes, this is the "Intelligent Children" design pattern. (See:
> http://www.objectmentor.com/resources/articleTeasers/dualInheritance)
>
> This simple code *could* be reconstructed in the fashion you suggest;
> however, let's say that there is some indispensible service performed
> by the base class that makes it inconvenient to put an HourlyEmployee
> instance in the derived class.


I don't see a problem. Inheritance from the base class still works so
it is still accessible through the subclass. Employee for
HourlyEmployee is a different generalization than EmployeeTranslaction
for AddTimeCardTransation. Each specialization should inherit its own
base class generalization correctly.

Besides, one isn't "putting in" an HourlyEmployee. One is instantiating
a relationship through a pointer that points to exactly the same
instance in this case. The pointer is simply resolving the typing
issues without a downcast.


>
>
>>[There should be a sense of deja vu here because you provided a very
>>similar Employee/HourlyEmployee example awhile back to demonstrate
>>downcasts. In that one you even provided a UML model with the correct
>>relationship. However, like in this example, that relationship was
>>never instantiated and navigated in the actual code.]
>>
>
> Yes, it's the same basic code. The UML diagram I drew then used a
> "dependency" relationship between AddTimeCardTransaction and
> HourlyEmployee. Or perhaps it was a <<parameter>> association. These
> relationships are not instantiated with fields. They are dynamic in
> nature coming from arguments or return values. The relationship *is*
> traversed, by the way, it is traversed by the 'he' variable.


Sure, there are lots of options for implementing relationship
navigation. But one still has to navigate the proper relationship!
Choosing to navigate a different relationship than the one that enforces
the type constraint is not an option.


>
>
>>Now let me anticipate two levels of response. The first is: why have a
>>redundant attribute to identify the employee? The answer is that the
>>attribute is essentially a referential attribute (foreign key) that just
>>represents instantiation of relationship navigation in a manner that
>>always ensures referential integrity. [Note that if itsEmployee was an
>>EmployeeID like SSN the problem would go away because it could be used
>>directly since it isn't tied to the type of Employee. The pointer is
>>just an optimization of attribute size vs. search overhead.]
>>
>
> OK, lets assume we have a function:
>
> Employee getEmployee(String ssn);
>
> We could assume that AddTimeCardTransaction could call this function
> to get an Employee. Then it would have to cast the employee to an
> HourlyEmployee in order to call addTimeCard.


The point is one can't do it that way with identifier attributes. One
has to have a static Find in HourlyEmployee that will find the correct
instance using the SSN. It will always return an HourlyEmployee handle
so no cast is necessary. [Of course now the smarts to ensure the SSN is
for an hourly employee resides with whoever invokes
AddTimeCardTransaction's behavior. But that is more logical than having
it in AddTimeCardTransaction's implementation.]


>
>
>
>>The second is: what's the harm? In this case none as far as correct
>>behavior is concerned. I would argue, though, that this usage is more
>>obscure than implementing the relationship correctly and it will
>>probably be less efficient.
>>
>
> It is certainly less efficient than saving the HourlyEmployee in the
> AddTimeCardTransaction. However, it is not "inefficient" per se.
> Morevoer, if we were to fetch the employee from its SSN, we'd still
> have to do the cast.


I said probably, which was based on the fact that the downcast solution
requires more executable statements and the efficiency of the downcast
is probably language/compiler dependent.


>
>
>>More importantly, it is a very bad habit to
>>get into and it requires the developer to make decisions about whether
>>it really is dangerous on a case-by-case basis. Since the alternative
>>is clearer, consistent, and perhaps more efficient, that sort of
>>decision should be avoided.
>>
>
> I don't like that kind of reasoning. Casts can be dangerous, so avoid
> all casts. Multiple inheritance is dangerous so avoid all multiple
> inheritance. Inheritance is dangerous... switch is dangerous... if
> is dangerous... programming is dangerous...


You are generalizing to different contexts. Those examples don't always
have viable alternatives. As this example indicates the alternative to
downcasting is certainly no worse and may even be better.


>
> No, I want to *understand* what the issues are and make good design
> choices based upon that understanding. There are cases where casts of
> the form described above are harmless and convenient.


All the world loves a straight man:

Then understand the fundamentals of how OO relationships enforce problem
space rules and why they must be honored in the design.

The devil made me do it.


>
> BTW, presume we had written this in Ruby, or Python. The logic would
> be identical, but there would have been no cast because there would
> have been no static type information.


But they would still throw up at run time if the instance wasn't an
HourlyEmployee. That means that it is incumbent on the caller to be
sure that the instance really is an HourlyEmployee. So the caller
implementation must understand the inheritance tree. At best it is
misplaced because whoever calls AddTimeCardTransaction's behavior is
more logically suited (as a third party) to understanding
AddTimeCardTransaction's relationship to HourlyEmployee.

In this particular case that implementation dependency may be benign,
but it will rarely be so in general. Why bother worrying about special
processing for some rare situation when it has no advantage over the way
one resolves it in the cases where it would be a problem?

Daniel T.

unread,
Sep 4, 2002, 6:23:26 PM9/4/02
to
"Uncle Bob (Robert C. Martin)"
<u.n.c.l.e.b.o.b.@.o.b.j.e.c.t.m.e.n.t.o.r.d.o.t.c.o.m> wrote:

>On Wed, 04 Sep 2002 02:39:48 GMT, "Daniel T." <notda...@gte.net>
>wrote:
>
>>Off we go again. I think we do this one about twice a year don't we? :-)
>
>It is a recurring topic, yes. But then there are new people around.
>
>>Even if code is introduced that would require the Employee class to
>>return, there is no justification for EmployeeTransaction to have an
>>'itsEmployee' field and no justification for the downcast.
>
>Daniel, we *have* been through this before, so you don't need to start
>with the trivial objections. Remember, this is the part where I claim
>that the variable in the base class was just a pedagogical
>convenience. In reality the argument to the AddTimeCardTransaction
>constructor was the key into the Employee database, and the
>getEmployee method of EmployeeTransaction holds the code that reads
>the database given the key.
>
>Anyway, if you recall, we went through the whole discussion eight
>months ago and found that in the general case the solutions that
>managed to get rid of the downcast were variants of the Visitor
>pattern; which, in turn, is semantically equivalent to a downcast.

I thought that we found that the solution was to have the db produce an
employee of the correct type. (Actually I remember most people agreeing
that having employee sub-types based on criteria that can easily change
(such as his hourly/salary status) was a bad idea.

Uncle Bob (Robert C. Martin)

unread,
Sep 4, 2002, 6:51:01 PM9/4/02
to
On Wed, 04 Sep 2002 18:03:31 GMT, "H. S. Lahman"
<vze2...@verizon.net> wrote:

>Not so ambiguous when one doesn't know the language, is it?

I think opaque would be a better word. It's completely unambiguous,
but you have to know the language.

> An OOA
>model, OTOH, is unambiguous, far more compact, AND language independent
>when talking about OO fundamentals.

Clearly an OOA model is not language independent since the modeling
language is a language. Moreover, if you don't know that modeling
language it's just as opaque as java is to someone who doesn't know
java.

Uncle Bob (Robert C. Martin)

unread,
Sep 4, 2002, 6:51:03 PM9/4/02
to
On Wed, 04 Sep 2002 18:03:31 GMT, "H. S. Lahman"
<vze2...@verizon.net> wrote:

>The point is one can't do it that way with identifier attributes. One
>has to have a static Find in HourlyEmployee that will find the correct
>instance using the SSN. It will always return an HourlyEmployee handle
>so no cast is necessary. [Of course now the smarts to ensure the SSN is
>for an hourly employee resides with whoever invokes
>AddTimeCardTransaction's behavior. But that is more logical than having
>it in AddTimeCardTransaction's implementation.]

I think I know what you are saying, but I'd like to see the code. I
think if you wrote the code you'd find there'd either be a cast, or
there'd be a substantial amount of code duplication to avoid the cast.
I think duplication is a greater evil than this particular cast.

Uncle Bob (Robert C. Martin)

unread,
Sep 4, 2002, 6:51:08 PM9/4/02
to
On Wed, 04 Sep 2002 18:03:31 GMT, "H. S. Lahman"
<vze2...@verizon.net> wrote:

>> BTW, presume we had written this in Ruby, or Python. The logic would
>> be identical, but there would have been no cast because there would
>> have been no static type information.
>
>
>But they would still throw up at run time if the instance wasn't an
>HourlyEmployee.

But logically the instance *can't* be anything but an hourly employee.
So the exception will never happen.

Tsolak Petrosian

unread,
Sep 4, 2002, 7:16:23 PM9/4/02
to
>> I cant see how we can capture this into code without downcasting.
>>
>>
>> *
>> A +------- O1
>> | | |
>> +----+----+ | +---+----+
>> | | | | |
>> A1 A2 ---+ B C
>> | 1 1 | *
>> | |
>> +-----------------------------+

>There are several ways. The most likely is to have a generic
collection class >for each relationship (just as one would have for
the single A/O1 >relationship). The A2/O1 collection class would have
pointers to an O1 type >and membership could be either Bs or Cs. The
A1/C collection would have >pointers to a C type and memebrship would
be restricted to Cs.
>When A1 needs to get to a C it does so through that collection class.
>By traversing that relationship A1 is guaranteed to always get Cs
back
>and no knowledge of O1's hierarchy is needed in its implementation.

So your suggesting having M accept object of type Collection instead
of B or C, but since Collection is generic the A1 or A2 have to cast
the generic object inside Collection to C or B.
What you are achieving is same as having object passed to M to
implement both B and C types except we pass it as B and not as
Collection.

Well what I am talking about is reflection. But the confusion arises
because in order to use reflection you have to use downcasting. Here
is how its done in Java.

Lets say you get object of type B, since its instance of Object and
any type is subclass of Object we upcast instance of B to Object and
then downcast it to C
instead of directly casting from B to C (although it seams that we do
so).

Tsolak Petrosdian

Daniel T.

unread,
Sep 5, 2002, 9:09:03 AM9/5/02
to
"Uncle Bob (Robert C. Martin)"
<u.n.c.l.e.b.o.b.@.o.b.j.e.c.t.m.e.n.t.o.r.d.o.t.c.o.m> wrote:

>On Wed, 04 Sep 2002 18:03:31 GMT, "H. S. Lahman"
><vze2...@verizon.net> wrote:
>
>>The point is one can't do it that way with identifier attributes. One
>>has to have a static Find in HourlyEmployee that will find the correct
>>instance using the SSN. It will always return an HourlyEmployee handle
>>so no cast is necessary. [Of course now the smarts to ensure the SSN is
>>for an hourly employee resides with whoever invokes
>>AddTimeCardTransaction's behavior. But that is more logical than having
>>it in AddTimeCardTransaction's implementation.]
>
>I think I know what you are saying, but I'd like to see the code. I
>think if you wrote the code you'd find there'd either be a cast, or
>there'd be a substantial amount of code duplication to avoid the cast.
>I think duplication is a greater evil than this particular cast.

I don't think that is quite a fair requirement. First produce some code
that actually *requires* a cast. The code sample you posted in this
thread so far doesn't have such a requirement.

IE let's see a sample which requires either substantial code
duplication, or down-casting.

H. S. Lahman

unread,
Sep 5, 2002, 11:39:03 AM9/5/02
to
Responding to Petrosian...

>>>I cant see how we can capture this into code without downcasting.
>>>
>>>
>>> *
>>> A +------- O1
>>> | | |
>>> +----+----+ | +---+----+
>>> | | | | |
>>> A1 A2 ---+ B C
>>> | 1 1 | *
>>> | |
>>> +-----------------------------+
>>>
>
>>There are several ways. The most likely is to have a generic
>>
> collection class >for each relationship (just as one would have for
> the single A/O1 >relationship). The A2/O1 collection class would have
> pointers to an O1 type >and membership could be either Bs or Cs. The
> A1/C collection would have >pointers to a C type and memebrship would
> be restricted to Cs.
>
>>When A1 needs to get to a C it does so through that collection class.
>>By traversing that relationship A1 is guaranteed to always get Cs
>>back
>>and no knowledge of O1's hierarchy is needed in its implementation.
>>
>
> So your suggesting having M accept object of type Collection instead
> of B or C, but since Collection is generic the A1 or A2 have to cast
> the generic object inside Collection to C or B.
> What you are achieving is same as having object passed to M to
> implement both B and C types except we pass it as B and not as
> Collection.


Alas, I screwed up because in the original problem the A/O1 relationship
was 1:1, not 1:*. Sorry about that. When I made the original diagram I
made a typo and used '*'. When you asked your question I responded
based upon the diagram in the message. So we really have two cases that
are separate:

Case 1: Assume the relationship A/O1 really was 1:*. In that case one
would probably implement that relationship with some sort of collection
class that returned elements of O1 type. One would then have to
downcast that element to get to C specializations. The diagram above
does the same thing except that there are two distinct relationships,
each with its own collection class that returns elements of O1 to A2 and
elements of C to A1. (This explains my answer to your question.)

Case 2: The real original problem where the A/O1 relationship was 1:1.
In that case no collection classes are needed; simple pointers will do.
So the A2/O1 relationship would have a pointer-to-O1 in A2 and the
A1/C relationship would have a pointer-to-C in A1. (This is the answer
to the real situation.)

In either case, no casting is required in either A1 or A2 if one uses
the two relationship approach to enforce the typing constraint.


>
> Well what I am talking about is reflection. But the confusion arises
> because in order to use reflection you have to use downcasting. Here
> is how its done in Java.
>
> Lets say you get object of type B, since its instance of Object and
> any type is subclass of Object we upcast instance of B to Object and
> then downcast it to C
> instead of directly casting from B to C (although it seams that we do
> so).


Well, that is *really* ugly and my position would be: Don't Do That. If
one specifies the type access constraints correctly in the OOA/D
relationships, then implementing collaboration across those
relationships without that sort of nonsense is trivial (as above).
Moreover, one can do it the same way in any OOPL.

H. S. Lahman

unread,
Sep 5, 2002, 1:20:48 PM9/5/02
to
Responding to Martin...


>>The point is one can't do it that way with identifier attributes. One
>>has to have a static Find in HourlyEmployee that will find the correct
>>instance using the SSN. It will always return an HourlyEmployee handle
>>so no cast is necessary. [Of course now the smarts to ensure the SSN is
>>for an hourly employee resides with whoever invokes
>>AddTimeCardTransaction's behavior. But that is more logical than having
>>it in AddTimeCardTransaction's implementation.]
>>
>
> I think I know what you are saying, but I'd like to see the code. I
> think if you wrote the code you'd find there'd either be a cast, or
> there'd be a substantial amount of code duplication to avoid the cast.
> I think duplication is a greater evil than this particular cast.


Callee::do_hourly(EmployeeID ssn)
{
HourlyEmployee* he;

he = HourlyEmployee::Find(ssn);

// complete the processing

AddTimeCardTransaction atct = new(he);
...
}

Note that this is structurally identical to your example. If one
replaces ssn with an Employee reference and the 'he' assignment with a
downcast, it would be the same as your "execute" method. They also both
depend upon someone else ensuring that the referenced instance is an
hourly employee.

As far as duplicate code is concerned, there isn't any. But there will
be a boat load of additional code because of the Find. That is the
reason why we very rarely implement relationship navigation with
referential data attributes; we pay in both size and performance no
matter how we gussy up instance indexing. The only point of my bringing
up ssn was that both the caller and callee can refer to the same
identifier without casting because the ssn is orthogonal to the subclassing.

H. S. Lahman

unread,
Sep 5, 2002, 1:54:32 PM9/5/02
to
Responding to Martin...


>>>BTW, presume we had written this in Ruby, or Python. The logic would
>>>be identical, but there would have been no cast because there would
>>>have been no static type information.
>>>
>>
>>But they would still throw up at run time if the instance wasn't an
>>HourlyEmployee.
>>
>
> But logically the instance *can't* be anything but an hourly employee.
> So the exception will never happen.


The point I was trying to make was that this is a syntactic issue
between languages. One still accesses a specific specialization from a
superclass and one had better make sure it is the right specialization
before doing so. And in both cases it will be a run-time error if one
doesn't.

Just because a language allows one to do something does not mean one
should do it. Over the years we have had a parade of foot shooting as a
result of features that seemed to be a good idea at the time. The issue
here is honoring access constraints that are explicitly <and correctly>
defined in OOA/D relationships.

When one ignores the constraint and navigates by an unconstrained
relationship one is not implementing the OOA/D correctly. Whether one
has to use a kludge like dynamic downcast or it can be done seamlessly
in the specific language does not change the fact that constraint was
not implemented correctly.

Nor does it matter that one can construct a particular application
around it to ensure that it will always work correctly. For years
people developed applications loaded with GOTOs, assigned GOTOs, and
ALTER statements that really worked. But nobody is advocating today
that we should still develop that way just because one can make it work.

Dave Harris

unread,
Sep 5, 2002, 3:02:00 PM9/5/02
to
vze2...@verizon.net (H. S. Lahman) wrote (abridged):

> Downcasting is unique to generalization relationships in
> that it casts a superclass to a specific specialization subclass. In
> languages that support downcasting, attempting a downcast on an object
> that is actually of a different subclass than that specified by the
> downcast is an error. [An exception may not be thrown but then the
> error will be indicated in the returned reference (e.g., a NULL
> pointer).]

It's not always an error.

I used a downcast today, as part of an equality test. It looked like (in
C++):

bool MyClass::operator==( const Object &rhs ) const {
const MyClass *pRhs = dynamic_cast<const MyClass *>(&rhs);
if (pRhs == NULL)
return false;
return this->field == pRhs->field;
}

Here when the dynamic cast returns a NULL pointer, it just means the other
object isn't equal to this one. It is not an error to compare objects of
different class. Indeed, the code which calls this should not need to know
anything about derived classes.

If it helps, the code is part of a word processor. The class hierarchy
represents characters and other things found in a text stream. The calling
code was part of a "Find" routine.

Dave Harris, Nottingham, UK | "Weave a circle round him thrice,
bran...@cix.co.uk | And close your eyes with holy dread,
| For he on honey dew hath fed
http://www.bhresearch.co.uk/ | And drunk the milk of Paradise."

Dave Harris

unread,
Sep 5, 2002, 3:23:00 PM9/5/02
to
u.n.c.l.e.b.o.b.@.o.b.j.e.c.t.m.e.n.t.o.r.d.o.t.c.o.m (Uncle Bob (Robert
C. Martin)) wrote (abridged):

> This particular example does not violate LSP. Notice, in the code
> below, that there is no way to pass the wrong type. The
> HourlyEmployee is passed to the AddTimeCardTransaction which then
> passes it to its base class. The base class simply puts it in a field
> typed: Employee. Later, when execute is called, the HourlyEmployee is
> pulled back out of the field. This object *must* be an
> HourlyEmployee. There is no way for it to be anything else.
> Therefore the cast is simply a way of asserting what is already known.
> No type information has been lost.

This is an example where a human has a proof, but it is not accessible to
the compiler. If the compiler were smarter, and/or we redesigned the code
somehow, it should be able to figure it out and the cast wouldn't be
necessary. This cast is due to what Costin Cozianu called, "the clumsiness

of the type system".

I just posted a different example: an equality test, where objects were
equal if they are the same class and their fields match. That also needed
a dynamic cast. It is a different kind of cast, in that we genuinely don't
know what the object's type is. Most of the time the cast will "fail" and
return NULL, and that is not an error. I think that kind of cast is
intrinsic. The type information hasn't been lost; it was never there, so
it has to be acquired somehow. Redesigning the application or improving
the type system won't take away the need for type-discovery mechanisms.

Uncle Bob (Robert C. Martin)

unread,
Sep 5, 2002, 3:45:03 PM9/5/02
to
On Thu, 05 Sep 2002 13:09:03 GMT, "Daniel T." <notda...@gte.net>
wrote:

>"Uncle Bob (Robert C. Martin)"

public class Employee {
}

public class HourlyEmployee extends Employee {
public void addTimeCard(TimeCard tc) {
...
}
}

public class EmployeeTransaction
public EmployeeTransaction(int id) {
itsEmployeeId = id;
}
public void abstract execute();

protected Employee getEmployee()
{
return fetchEmployee(itsEmployeeId);
}

private Employee fecthEmployee(int employeeId);
private int itsEmployeeId;
}

public class AddTimeCardTransaction extends EmployeeTransaction {
TimeCard itsTimeCard = null;

public AddTimeCardTransaction(int hourlyEmployeeId,
Date date, double hours) {
super(hourlyEmployeeId);


itsTimeCard = new TimeCard(date, hours);
}

public void execute() {
Employee e = getEmployee();
HourlyEmployee he = (HourlyEmployee) e; // downcast
he.addTimeCard(itsTimeCard);
}
}

Robert C. Martin | "Uncle Bob"

Tsolak Petrosian

unread,
Sep 5, 2002, 6:41:56 PM9/5/02
to
Alas, I screwed up because in the original problem the A/O1
relationship
was 1:1, not 1:*. Sorry about that. When I made the original diagram
I
made a typo and used '*'. When you asked your question I responded
>Case 1: Assume the relationship A/O1 really was 1:*. In that case
one
>would probably implement that relationship with some sort of
collection
>class that returned elements of O1 type. One would then have to
>downcast that element to get to C specializations. The diagram above
>does the same thing except that there are two distinct relationships,
>each with its own collection class that returns elements of O1 to A2
and
>elements of C to A1. (This explains my answer to your question.)

>Case 2: The real original problem where the A/O1 relationship was
1:1.
>In that case no collection classes are needed; simple pointers will
do.
>So the A2/O1 relationship would have a pointer-to-O1 in A2 and the
>A1/C relationship would have a pointer-to-C in A1. (This is the
answer
>to the real situation.)

>In either case, no casting is required in either A1 or A2 if one uses
>the two relationship approach to enforce the typing constraint.

I still cant see how we can change signature of method M in A so that
A1's implementation of M will take O1 and A2' C?

> Well, that is *really* ugly and my position would be: Don't Do That. If
> one specifies the type access constraints correctly in the OOA/D
> relationships, then implementing collaboration across those
> relationships without that sort of nonsense is trivial (as above).
> Moreover, one can do it the same way in any OOPL.

We cant specifies the type access constraints in the OOA/D
relationships because we dont have controll on impplementations, what
you are suggesting is that any implementation of M of A has to deal
with B only.
For example in the example of Doctor and DrivingDoctor in
"patient.accept(Doctor d)" method implementation we have to only work
stricly with Doctor and if there is special "patient" that needs
drving soctor we have to define additional metod with new type
"patient.accept(DrivingDoctor d)" or "patient.accept(Doctor, Driver
d)" wich current languages dont support.

Tsolak Petrosian

Uncle Bob (Robert C. Martin)

unread,
Sep 5, 2002, 7:15:56 PM9/5/02
to

Granted.

>As far as duplicate code is concerned, there isn't any.

I disagree.

HourlyEmployee::find
SalariedEmployee:find
CommissionedEmployee::find

These functions are virtually identical.

>But there will
>be a boat load of additional code because of the Find. That is the
>reason why we very rarely implement relationship navigation with
>referential data attributes; we pay in both size and performance no
>matter how we gussy up instance indexing.

Unless, of course, you downcast. It seems to me that the avoidance of
downcast is costing you.

Daniel T.

unread,
Sep 5, 2002, 11:11:01 PM9/5/02
to
In article <lmcfnu4t9npo1fgq7...@4ax.com>,

Where is the example with the "substantial code duplication"?

Daniel T.

unread,
Sep 6, 2002, 7:54:15 AM9/6/02
to
"Ilja Preuß" <pre...@disy.net> wrote:

>> Where is the example with the "substantial code duplication"?
>

>Shouldn't it be the other way around: "Where is 'your' version of that code
>without the cast and substantial code duplication"?

Why would I post code with "substantial code duplication"? I don't think
it is necessary, Robert does...

H. S. Lahman

unread,
Sep 6, 2002, 11:16:06 AM9/6/02
to
Responding to Petrosian...


>>In either case, no casting is required in either A1 or A2 if one uses
>>the two relationship approach to enforce the typing constraint.
>>
>
> I still cant see how we can change signature of method M in A so that
> A1's implementation of M will take O1 and A2' C?


Let's do the original 1:1 case where A's subtypes provide different
implementations for a doit() method:

A1::A1(C* c)
{
C* aC = c;
}

A1::doit()
{
aC->whatever(); // processes only Cs.
}


A2::A2(O1* o1)
{
O1* anO1 = o1;
}

A2::doit()
{
anO1->somethingesle(); // doesn't care about B vs. C
}

There is no need to change any signatures because the relationship
navigation is implemented directly to the correct type, as specified in
the OOA/D relationship. In this case the relationship is instantiated
by the constructor.

[Note that this is effectively the same thing as in Martin's example.
The same external intelligence was required to ensure the constructor
got an appropriate type. And that is what sustains his argument that it
Just Works. The difference is that his example did not follow through
to navigate the relationship as above. Instead he navigated the
unconstrained generalization relationship so he was forced to resort to
a downcast.]

>>Well, that is *really* ugly and my position would be: Don't Do That. If
>>one specifies the type access constraints correctly in the OOA/D
>>relationships, then implementing collaboration across those
>>relationships without that sort of nonsense is trivial (as above).
>>Moreover, one can do it the same way in any OOPL.
>>
>
> We cant specifies the type access constraints in the OOA/D
> relationships because we dont have controll on impplementations, what
> you are suggesting is that any implementation of M of A has to deal
> with B only.


Au contraire! That is exactly what OOA/D relationships do in the case
of subclasses. Referring the my diagram the following constraints are
explicitly defined:

(1) At least one subclass of A or O1 cannot collaborate solely as
generalizations _in any way_ (i.e., there is no A/O1 direct relationship).

(2) An A2 subclass collaborates with O1 only at the generalization level
(i.e., A2 is indifferent to whether O1 is a B or a C because the
relationship is with O1 rather than B or C).

(3) An A1 subclass may only collaborate with a C subclass (i.e., its
only relationship is to a C), which is the crucial constraint in the
downcast discussion.

[BTW, I detect a point of divergence here. You are correct that one
does not have control over the implementation types in the OOA/D.
However, one does have control over the classes from which those types
are derived. That is why I have been careful to talk about subclasses.
The downcast is just a symptom. The real problem is an incorrect
mapping of OOA/D class relationship constraints when providing the
implementation.]


> For example in the example of Doctor and DrivingDoctor in
> "patient.accept(Doctor d)" method implementation we have to only work
> stricly with Doctor and if there is special "patient" that needs
> drving soctor we have to define additional metod with new type
> "patient.accept(DrivingDoctor d)" or "patient.accept(Doctor, Driver
> d)" wich current languages dont support.


One <of the many> reasons why passing object references as parameters is
a very bad idea is that it requires whoever is doing the passing to
ensure the correct Doctor is passed. That is usually done in code
(i.e., IF statements) and it has to be done in every context where
patient.accept is invoked.

Worse, it introduces an implementation dependency in the caller because
that caller must understand the semantics of Patient that require a
Doctor who drives. That breaks encapsulation because what kind of
Doctor a Patient needs is the Patient's business (to be defined in the
<orthogonal> Patient/Doctor relationship). Moreover, if Patient has
multiple clients, each client is going to have to have those IFs. And
if other Patient behaviors also depend on a driving Doctor, their
clients will also have to have those IFs. IOW: spaghetti code that will
result in painful refactoring when the Patient/Doctor relationship changes.

A far better approach, which also eliminates the casting problems, is to
properly implement relationship navigation. If access to a driving
Doctor is implemented with a pointer when Patient is created, Patient
will *always* access the correct Doctor for any context for the rest of
its natural life. And if the Patient/Doctor relationship changes no one
will care except whoever instantiates the relationship. That is, the
smarts to ensure the correct Doctor only has to be applied once in one
place -- when the relationship is instantiated (e.g., usually by whoever
invokes Patient's constructor).

H. S. Lahman

unread,
Sep 6, 2002, 11:39:15 AM9/6/02
to
Responding to Harris...


> It's not always an error.
>
> I used a downcast today, as part of an equality test. It looked like (in
> C++):
>
> bool MyClass::operator==( const Object &rhs ) const {
> const MyClass *pRhs = dynamic_cast<const MyClass *>(&rhs);
> if (pRhs == NULL)
> return false;
> return this->field == pRhs->field;
> }
>
> Here when the dynamic cast returns a NULL pointer, it just means the other
> object isn't equal to this one. It is not an error to compare objects of
> different class. Indeed, the code which calls this should not need to know
> anything about derived classes.


Curious. If rhs *is* a pRhs but pRhs->field happens to be false, the
return value will indicate rhs is not a pRhs. But I digress...

The problem lies in the client that is invoking the '==' operator. That
client is the one with the dependency on the specialization. The client
is trying to navigate to that specialization (pRhs->field) through the
generalization rather than through the direct OOA/D relationship (to the
pRhs subclass) that defines the specialization dependency constraint.

To clarify, my issue is not with downcast per se. (As it happens I can
think of no situation where one could use a downcast where there was not
also a specialization dependency that needed to be formally defined in
an OOA/D relationship, but that is beside the point.) I see downcast as
only a symptom of the real problem. The real problem is that the OOA/D
specifies a collaboration constraint at the specialization level but
that relationship is not actually implemented. Instead navigation is
implemented through the generalization relationship, which is
unconstrained. That is what leads to the downcast kludge.

H. S. Lahman

unread,
Sep 6, 2002, 11:52:49 AM9/6/02
to
Responding to Martin...


>>As far as duplicate code is concerned, there isn't any.
>>
>
> I disagree.
>
> HourlyEmployee::find
> SalariedEmployee:find
> CommissionedEmployee::find
>
> These functions are virtually identical.


Actually, they are exactly the same. A translation engine would
standardize the instance tracking mechanism so only one function would
be needed for all classes (e.g., as Object::Find). Thus there would be
no redundancy.


>
>
>>But there will
>>be a boat load of additional code because of the Find. That is the
>>reason why we very rarely implement relationship navigation with
>>referential data attributes; we pay in both size and performance no
>>matter how we gussy up instance indexing.
>>
>
> Unless, of course, you downcast. It seems to me that the avoidance of
> downcast is costing you.


You are deflecting. The only reason ssn came up was to demonstrate that
there are alternative means of identity that do not require casting.

If one wants to avoid the problems of identity based on data attributes,
one can implement it as pointer-based relationship navigation -- which
turned out to be simpler than downcasting in your own example.

Daniel T.

unread,
Sep 6, 2002, 4:13:02 PM9/6/02
to
"Ilja Preuß" <pre...@disy.net> wrote:

> "Daniel T." <notda...@gte.net> schrieb im Newsbeitrag
> news:notdanielt3-1226...@news.bellatlantic.net...

> Sorry, I meant "code without the cast and *without* substantial code
> duplication". After all, Robert wouldn't be able to prove that the code
> duplication is necessary whatever the number of examples with duplication he
> provides, but you could be able to prove that it isn't by simply providing
> one example.
>
> Looking forward to your example,

How can I show that an example can be written without code duplication
when no code duplication has been shown to exist?

Dave Harris

unread,
Sep 6, 2002, 6:32:00 PM9/6/02
to
vze2...@verizon.net (H. S. Lahman) wrote (abridged):
> Curious. If rhs *is* a pRhs but pRhs->field happens to be false, the
> return value will indicate rhs is not a pRhs. But I digress...

The code returns true if the two objects are equal, where "equal" means
the same class (hence the same fields present) and every field is also
equal. Any difference between their values makes them unequal. Why is this
curious?


> The problem lies in the client that is invoking the '==' operator.
> That client is the one with the dependency on the specialization. The
> client is trying to navigate to that specialization (pRhs->field)
> through the generalization rather than through the direct OOA/D
> relationship (to the pRhs subclass) that defines the specialization
> dependency constraint.

I don't see this. The caller was like:

// True if the objects at i in stream match those in pattern.
// Stream and pattern are null-terminated arrays.
bool isOccurenceAt( const Object *stream[], int i,
const Object *pattern[] ) {
for (int j = 0; pattern[j] != NULL; ++j)
if (!(*stream[i+j] == *pattern[j]))
return false;
return true;
}

// Index of first occurrence of pattern in stream.
int find( const Object *stream[], const Object *pattern[] ) {
for (int i = 0; stream[i] != NULL; ++i)
if (isoccurenceat( stream, i, pattern ))
return i;
return -1;
}

Find() is like the C routine strstr(), except characters are represented
by pointers to instances of (subclasses of) Object instead of chars. Some
Object subclasses represent ASCII characters, some UNICODE, some embedded
pictures, some support automatic list numbering, and so forth. Many
subclasses.

The caller is not trying to navigate to fields of the objects in the
arrays. It does not care about their structure. It only cares whether they
are equivalent. I don't want to have to update find() every time I add a
new subclass of Object.


> The real problem is that the OOA/D specifies a collaboration
> constraint at the specialization level but that relationship is
> not actually implemented.

How would you implement that relationship here?


> Instead navigation is implemented through the generalization
> relationship, which is unconstrained.

If I understand this, and I'm not sure I do, I think that making the
subclasses responsible for the navigation avoids placing an burden on
find() - and that burden would be truly odious.

Costin Cozianu

unread,
Sep 6, 2002, 6:57:04 PM9/6/02
to

>
> How can I show that an example can be written without code duplication
> when no code duplication has been shown to exist?

Oh boy are you so lacking of imagination and are you so lacking in
studying existing code that you can't figure out examples by yourself ??


To explain Uncle Bob's bottom line example is about a Base Class X which
has the responsibility of managing the relationship to a generic Class
Y, while specialized class X1, X2 (derived from X no direct relation
between them), are constrained to collaborate with specific derived in
some way classes Y1 and Y2 respectively.

Putting the relationship management code (add/remove/iterate etc) in
base class X is the natural thing to do , and automatically implies
downcasting because the class X1 will invoke the base class's
relationship implementation to get to the related object and that would
be a Y, not an Y1 from the compiler.

If you want to use specialized knowledge in class X1 that it can only
relate to Y1, and similarly in X2 <-> Y2 you'll have duplicated code in
X2 and X1 (duplicated in the sense that it differs only by types but
will reflect the same basic operations that manager a relationship
"add/remove/iterate"). That's where the duplication occurs.

For further study you can pick examples how many you like from Java's
standard libraries. One obvious is Java's AWT and Swing event
dispatching mechanism which contains both downcasts *and* code duplication.

And by the way, this problem (loss of typing vs. code duplication) is
well documented in numerous papers and books on OO and types, even for
type systems more powerful than Java's, should you and Mr. Lahman ever
bother to read before coming out in public with stupid decrees like
"downcast is always bad"TM.

If you work with Java you downcast 10 times a day easily, or else you're
doing something very wrong.

If you work with C++, downcasts should be relatively rare and should
generally be avoided but to say that they are always bad, you have to be
either a genius which came up with a new theory, or to be ignorant both
of practical problems and type theory.

Please choose between the two.

Costin Cozianu

Uncle Bob (Robert C. Martin)

unread,
Sep 7, 2002, 12:47:42 AM9/7/02
to
To summarize this post:

- unecessary insults.
- extraordinarily lucid and compelling description.
- unecessary insults.

Costin, you know your stuff. You are bright enough not to have to
depend upon the insults. I strongly recommend dropping them from
future postings.

Robert C. Martin | "Uncle Bob"

Uncle Bob (Robert C. Martin)

unread,
Sep 7, 2002, 12:47:44 AM9/7/02
to
On Fri, 06 Sep 2002 15:52:49 GMT, "H. S. Lahman"
<vze2...@verizon.net> wrote:

>>>As far as duplicate code is concerned, there isn't any.
>>>
>>
>> I disagree.
>>
>> HourlyEmployee::find
>> SalariedEmployee:find
>> CommissionedEmployee::find
>>
>> These functions are virtually identical.
>
>
>Actually, they are exactly the same. A translation engine would
>standardize the instance tracking mechanism so only one function would
>be needed for all classes (e.g., as Object::Find). Thus there would be
>no redundancy.

Except for the downcast, which *would* be necessary:

HourlyEmployee* HourlyEmployee::find(int id) {
return dynamic_cast<HourlyEmployee*>(Object::find(id));
}

CommissionedEmployee* CommissionedEmployee::find(int id) {
return dynamic_cast<CommissionedEmployee*>(Object::find(id));
}

SalariedEmployee* SalariedEmployee::find(int id) {
return dynamic_cast<SalariedEmployee*>(Object::find(id));

Leaving us with both downcasts *and* duplication.

Daniel T.

unread,
Sep 7, 2002, 8:48:44 AM9/7/02
to
Costin Cozianu <c_co...@hotmail.com> wrote:

> >
> > How can I show that an example can be written without code duplication
> > when no code duplication has been shown to exist?
>

> [snip]


>
> To explain Uncle Bob's bottom line example is about a Base Class X which
> has the responsibility of managing the relationship to a generic Class
> Y, while specialized class X1, X2 (derived from X no direct relation
> between them), are constrained to collaborate with specific derived in
> some way classes Y1 and Y2 respectively.
>
> Putting the relationship management code (add/remove/iterate etc) in
> base class X is the natural thing to do , and automatically implies
> downcasting because the class X1 will invoke the base class's
> relationship implementation to get to the related object and that would
> be a Y, not an Y1 from the compiler.

So you are saying that we have this: ( 'A' shows an inheritance
relationship, <>---> is aggrigation, and --> is a using relationship.)

[ X ]------------->[ Y ]
A A
+------+ +-------+
| | | |
| [ X2 ]<>--->[ Y2 ] |
[ X1 ]<>------------------>[ Y1 ]

Why not translate it to this?

+-->[ X ]------->[ Y ]
| A
+------+ +-------+
| | | |
| [ X2 ]<>--->[ Y2 ] |
[ X1 ]<>------------------>[ Y1 ]

In other words, put the relationship management code (add/remove/iterate
etc) in a helper class (not a base class) which doesn't imply
downcasting.

There is no reason intrinsic to the problem why X1 and X2 must both
derive from the same class. (There may be reasons outside of this
particular problem why they might both implement the same interface
though.)

> If you want to use specialized knowledge in class X1 that it can only
> relate to Y1, and similarly in X2 <-> Y2 you'll have duplicated code in
> X2 and X1 (duplicated in the sense that it differs only by types but
> will reflect the same basic operations that manager a relationship
> "add/remove/iterate"). That's where the duplication occurs.

I agree that code should be placed in a third class, if there is
"substantial code duplication". But there is no reason why it *must* be
placed in a Base class which would force X1 and X2 to down-cast.

>[snip]


>
> If you work with Java you downcast 10 times a day easily, or else you're
> doing something very wrong.
>
> If you work with C++, downcasts should be relatively rare and should
> generally be avoided but to say that they are always bad, you have to be
> either a genius which came up with a new theory, or to be ignorant both
> of practical problems and type theory.

I will accept that in some languages, down-casting is both accepted and
required. It is my position that down-casting is acceptable as long as
we are able to statically determine that the down-cast in question will
always succeed. Preferably without looking outside the class that
contains the downcast.

In the first example Mr. Martin gave, that was the case. In the second,
that was not the case, and (in past discussions on this issue) Mr.
Lahman (and others) have shown that there are other (and IMO better)
design options available.

Daniel T.

unread,
Sep 7, 2002, 9:08:28 AM9/7/02
to
In article <r4linu01tuslvb5eu...@4ax.com>,

"Uncle Bob (Robert C. Martin)"
<u.n.c.l.e.b.o.b.@.o.b.j.e.c.t.m.e.n.t.o.r.d.o.t.c.o.m> wrote:

> On Fri, 06 Sep 2002 15:52:49 GMT, "H. S. Lahman"
> <vze2...@verizon.net> wrote:
>
> >>>As far as duplicate code is concerned, there isn't any.
> >>>
> >>
> >> I disagree.
> >>
> >> HourlyEmployee::find
> >> SalariedEmployee:find
> >> CommissionedEmployee::find
> >>
> >> These functions are virtually identical.
> >
> >
> >Actually, they are exactly the same. A translation engine would
> >standardize the instance tracking mechanism so only one function would
> >be needed for all classes (e.g., as Object::Find). Thus there would be
> >no redundancy.
>
> Except for the downcast, which *would* be necessary:
>
> HourlyEmployee* HourlyEmployee::find(int id) {
> return dynamic_cast<HourlyEmployee*>(Object::find(id));
> }
>
> CommissionedEmployee* CommissionedEmployee::find(int id) {
> return dynamic_cast<CommissionedEmployee*>(Object::find(id));
> }
>
> SalariedEmployee* SalariedEmployee::find(int id) {
> return dynamic_cast<SalariedEmployee*>(Object::find(id));
>
> Leaving us with both downcasts *and* duplication.
>
>
> }

Why not:

HourlyEmployee* HorlyEmployee::find(int id) {
HourlyEmployee result = new HourlyEmployee();
fillWithEmployeeInfo( result ); // get's employee data from DB
// throws if he isn't hourly
return result;
}

and simular with the others. I would not call this "substantial code
duplication" BTW any more than I would call a pointer dereference
"substantial code duplication".

H. S. Lahman

unread,
Sep 7, 2002, 10:43:19 AM9/7/02
to
Responding to Harris...


>>Curious. If rhs *is* a pRhs but pRhs->field happens to be false, the
>>return value will indicate rhs is not a pRhs. But I digress...
>>
>
> The code returns true if the two objects are equal, where "equal" means
> the same class (hence the same fields present) and every field is also
> equal. Any difference between their values makes them unequal. Why is this
> curious?


If rhs is not a pRhs the return value is false to indicate that. But if
it is a pRhs, the return value is whatever pRhs->field happens to be.
That is, it isn't checking equality of field values, it is simply
accessing the field value.

IOW, you are hard-coding part of the pattern recognition described for
the client below. That is, the pattern is "Must be a pRhs AND field
must be true". So the client is not providing a pattern for comparison,
the whole pattern is locked into the '==' operator.

Because this is so far from the stated goal I suspect the real
disconnect here results from the fact that the example was paraphrased
for simplicity.

>>The problem lies in the client that is invoking the '==' operator.
>>That client is the one with the dependency on the specialization. The
>>client is trying to navigate to that specialization (pRhs->field)
>>through the generalization rather than through the direct OOA/D
>>relationship (to the pRhs subclass) that defines the specialization
>>dependency constraint.
>>
>
> I don't see this. The caller was like:
>
> // True if the objects at i in stream match those in pattern.
> // Stream and pattern are null-terminated arrays.
> bool isOccurenceAt( const Object *stream[], int i,
> const Object *pattern[] ) {
> for (int j = 0; pattern[j] != NULL; ++j)
> if (!(*stream[i+j] == *pattern[j]))
> return false;
> return true;
> }


This is a different problem. What I was responding to was the access of
the value of pRhs->field from the specialization. That implies a
collaboration constrained to the collaboration. Here the issue is
proper responsibilities for the abstraction and language implementation.

In effect what you are saying is any object in the stream should have a
public responsibility for knowing what specific type of object it is.
That sort of knowledge should be captured in an attribute (which may be
an ADT that is not implemented as a data store).

I will buy the fact that in this particular case (stream is a C++
OStream) you will need a downcast, though. (This is the example I
mentioned elsewhere in the tread that I couldn't think of where downcast
is used outside the context of problem space constraints.) That's
because (a) the language itself provides no other mechanism for handling
the problem and (b) the inheritance from Object is not a problem space
feature, it is a pure language implementation feature. IOW, what you
are really checking is for the type of objects that are completely
unrelated in the problem space.


>>The real problem is that the OOA/D specifies a collaboration
>>constraint at the specialization level but that relationship is
>>not actually implemented.
>>
>
> How would you implement that relationship here?


It is not a matter of relationship here. One needs to determine what
type an arbitrary object is before one can access it for any
collaboration. The situation is analogous to providing state
information in an XML string -- one has to parse the string before
processing the state information. In this case that "parsing" is very
dependent on the language implementation.


>>Instead navigation is implemented through the generalization
>>relationship, which is unconstrained.
>>
>
> If I understand this, and I'm not sure I do, I think that making the
> subclasses responsible for the navigation avoids placing an burden on
> find() - and that burden would be truly odious.


Actually the goal is to remove context dependence. In your example the
client's implementation must understand the inheritance from Object.
That is part of the implementation description of the stream objects
(and, as it happens, itself). It has nothing to do with the client's
mission in life, which is to select particular types of objects from a
stream of arbitrary objects. That depends on the intrinsic type of each
object.

In this case that implementation dependence is unavoidable because the
language provides no direct means of determining intrinsic object type
as a knowledge responsibility of the object itself. It is also be
fairly benign (i.e., it would be a lot riskier to override Object).

But the real issue is using downcasts for relationships defined in the
problem space. There it is important honor the problem space
constraints and avoid breaking encapsulation by forcing clients to
depend upon the inheritance structure of objects they collaborate with.
The client should be completely indifferent to whether a collaborating
object is subclassed or not. (See my recent response to Petrosian for a
more detailed discussion of the implementation dependence implications
for a driving Doctor.)

H. S. Lahman

unread,
Sep 7, 2002, 12:03:04 PM9/7/02
to
Responding to Martin...


>>Actually, they are exactly the same. A translation engine would
>>standardize the instance tracking mechanism so only one function would
>>be needed for all classes (e.g., as Object::Find). Thus there would be
>>no redundancy.
>>
>
> Except for the downcast, which *would* be necessary:


All one needs is a simple cast rather than a downcast:


>
> HourlyEmployee* HourlyEmployee::find(int id) {
> return dynamic_cast<HourlyEmployee*>(Object::find(id));


return (HourlyEmployee*)Object::find(id);


> }


Object::find returns an object that does not in any way depend upon
whether or not its class is a member of a subclassing taxonomy; it only
depends on the individual class membership. The underlying
infrastructure is entirely orthogonal to any is-a relationship. The
interesting thing is why one can use the simple cast with impunity.

That's because when modeling the 'find' mechanism there will be
relationships between Class, Instance, Index (an infrastructure to
manage the class' instances), and SuperClass (where Object::find lives)
that will constrain navigation of Object::find to a particular Index
collection whose members are only instances of a particular Class.

The class constructor will instantiate using a relationship constraint
to ensure that only instances of the class will be members of Index.
The Object::find implementation will also navigate a constraint
relationship when it accesses the proper Index infrastructure via 'this'.

Thus Object::find and the constructor explicitly honor the relationship
constraints (albeit in a rather mechanical way) so that no downcasts are
necessary when HourlyEmployee::find does a direct cast. That's because
HourlyEmployee's declaration of its own static Index infrastructure
instance provides the relationship that HourlyEmployee::find navigates
with the plain cast.

Thus so long as the relationhip constraints are properly instantiated
and navigated (in this case quite trivially because of OOPL constructs
like 'this') HourlyEmployee::find can be absolutely confident in the
simple cast. To the extent that tennis is all in the wrists, so OT is
all in the relationships. B-)

Dave Harris

unread,
Sep 7, 2002, 3:29:00 PM9/7/02
to
vze2...@verizon.net (H. S. Lahman) wrote (abridged):
> If rhs is not a pRhs the return value is false to indicate that. But
> if it is a pRhs, the return value is whatever pRhs->field happens to
> be. That is, it isn't checking equality of field values, it is simply
> accessing the field value.

I think you are misreading the code. The relevant line was:

return this->field == pRhs->field;

This isn't returning pRhs->field. It is returning the result of comparing
pRhs->field with this->field.

I don't know whether you are familiar with C++, but "==" compares for
equality, "->" gets a field from an object and "this" refers to the
current object.


> In effect what you are saying is any object in the stream should have a
> public responsibility for knowing what specific type of object it is.
> That sort of knowledge should be captured in an attribute (which may be
> an ADT that is not implemented as a data store).

OK... so in this implementation I am using the C++ vtable pointer, or
object class ID, to track the object's type. I don't need an extra
attribute. Doing it this way also gives me dynamic polymorphism on the
object's type, which is handy.


> I will buy the fact that in this particular case (stream is a C++
> OStream) you will need a downcast, though. (This is the example I
> mentioned elsewhere in the tread that I couldn't think of where
> downcast is used outside the context of problem space constraints.)
> That's because (a) the language itself provides no other mechanism for
> handling the problem and (b) the inheritance from Object is not a
> problem space feature, it is a pure language implementation feature.
> IOW, what you are really checking is for the type of objects that are
> completely unrelated in the problem space.

I'm not sure what kind of feature you have in mind for (a). Do you mean
multi-methods, or some such?

For (b), I agree the inheritance from Object is not a concern of the
problem space of find(). Find() only cares about Objects, not about
subclasses of Object. However, there are other occasions where code does
need to collaborate with specific subclasses.

For example, there is an "end of paragraph" object which carries
formatting information which applies to the paragraph. Most of the time
the Paragraph can store an explicit link to its EOP object and navigate
that without any downcasting. However, sometimes text is presented just as
a flat stream of objects, without being structured into paragraphs. In
order to recover the structure, some kind of type discovery must take
place.

In general what is happening here is an interplay between different
subsystems. For the find() subsystem and its ilk, all Objects are alike.
For paragraph formatting, the EOP subclass is special. When objects get
passed around between the subsystems, static type information gets lost
and has to be recovered via dynamic type discovery mechanisms (of which
dynamic cast is the most basic).

Often, perhaps always, it is possible to argue that the static type
information needed by one subsystem could be preserved by every subsystem.
However, that puts a burden on the other subsystems. For this example, a
stream of character objects, it isn't clear to me how it could be done and
even if theoretically possible, I am pretty sure that it shouldn't be
done. I think using dynamic type discovery is simpler, more efficient and
more maintainable.

Tsolak Petrosian

unread,
Sep 7, 2002, 4:01:38 PM9/7/02
to
I see that you are always assuming that the callers of such
implementations have to know about cast and do some rechecking (call
appropriated constructor or pass appropriated pointer).But that's what
we are avoiding by using casts, to separate the caller of directly
knowing what particular implementation needs.
In case of Doctor example system will load any implementation of
Patient without assuming its constructor (simply invokes empty
constructor) and will keep on passing Doctors until the current
Patient implementation will accept one of them or having Patient go
other list of doctors and see which one of them is Driver too.
This way we totally encapsulate the logic of choosing doctor inside
patient and not into other third party component (Patient knows what
it needs and Doctor knows what it can do and a third party component
only keeps list of available doctors).
Otherwise with your approach every time a new patient gets loaded with
new requirements we have to change the middle component to know what
Doctor to pass to it.

> A far better approach, which also eliminates the casting problems, is to
> properly implement relationship navigation. If access to a driving
> Doctor is implemented with a pointer when Patient is created, Patient
> will *always* access the correct Doctor for any context for the rest of
> its natural life. And if the Patient/Doctor relationship changes no one
> will care except whoever instantiates the relationship. That is, the
> smarts to ensure the correct Doctor only has to be applied once in one
> place -- when the relationship is instantiated (e.g., usually by whoever
> invokes Patient's constructor).

Its not true the implementation of Patient will still care about
doctor being a Driver and implementation Doctor has to care to be a
Driver.

Tsolak Petrosian


"H. S. Lahman" <vze2...@verizon.net> wrote in message news:<3D78C76B...@verizon.net>...

Uncle Bob (Robert C. Martin)

unread,
Sep 7, 2002, 5:07:31 PM9/7/02
to

I would call this substantial code duplication. I'd also call it very
error prone. Every new derivative of Employee must have its own
particular variant of this code.

Also, I believe there is either a downcast in the function
'fillWithEmployeeInfo', or there is one fillWithEmployeeInfo for each
different type of employee.

Uncle Bob (Robert C. Martin)

unread,
Sep 7, 2002, 5:15:08 PM9/7/02
to
On Sat, 07 Sep 2002 16:03:04 GMT, "H. S. Lahman"
<vze2...@verizon.net> wrote:

>Responding to Martin...
>
>
>>>Actually, they are exactly the same. A translation engine would
>>>standardize the instance tracking mechanism so only one function would
>>>be needed for all classes (e.g., as Object::Find). Thus there would be
>>>no redundancy.
>>>
>>
>> Except for the downcast, which *would* be necessary:
>
>
>All one needs is a simple cast rather than a downcast:
>
>
>>
>> HourlyEmployee* HourlyEmployee::find(int id) {
>> return dynamic_cast<HourlyEmployee*>(Object::find(id));
>
>
> return (HourlyEmployee*)Object::find(id);
>
>
>> }
>
>
>Object::find returns an object that does not in any way depend upon
>whether or not its class is a member of a subclassing taxonomy; it only
>depends on the individual class membership. The underlying
>infrastructure is entirely orthogonal to any is-a relationship. The
>interesting thing is why one can use the simple cast with impunity.

All you are saying is that the dynamic state of the system *knows*
that the Object is an HourlyEmployee, and can therefore cast with
impunity. This was precisely my original argument.

However, I think you need to brush up on your C++. There are a number
of very good reasons why the dynamic_cast is a better choice than the
straight cast. 1. If there's any virtual inheritance in the path from
Object to HourlyEmployee, the straight cast will fail. 2. If there is
any multiple inheritance in the path you could get into trouble too.
So I'd only use the straight cast (or rather a static_cast) in those
rare instances where the real-time cost of the dynamic_cast was too
high to pay.

Costin Cozianu

unread,
Sep 7, 2002, 7:51:18 PM9/7/02
to

"Daniel T." <a@b.c> wrote in message
news:a-4EC709.08...@nnrp05.earthlink.net...

Oh really ? How so?

In languages without higher order typing *any* implementation of sets,
relations etc
will contain either duplication or force clients to downcast.

That's because relationship are higher order (types parameterized by other
types) in nature.
And of course, dependin on whether types are really higher order or whether
they are just
more of a mechanism to generate code (like C++ template) without really
integrating with
the semantic nature of the type system, you will have the same problem in
more flexible
type systems.

> There is no reason intrinsic to the problem why X1 and X2 must both
> derive from the same class. (There may be reasons outside of this
> particular problem why they might both implement the same interface
> though.)

Since "downcast is always bad is a sweeping generalization, it has to prove
it's (un)worthiness in all cases.

> > If you want to use specialized knowledge in class X1 that it can only
> > relate to Y1, and similarly in X2 <-> Y2 you'll have duplicated code in
> > X2 and X1 (duplicated in the sense that it differs only by types but
> > will reflect the same basic operations that manager a relationship
> > "add/remove/iterate"). That's where the duplication occurs.
>
> I agree that code should be placed in a third class, if there is
> "substantial code duplication". But there is no reason why it *must* be
> placed in a Base class which would force X1 and X2 to down-cast.

Place it anywhere you like, you'll have to downcast nevertheless, and this
is probably the smallest and most convenient price you have to pay.

> >[snip]
> >
> > If you work with Java you downcast 10 times a day easily, or else you're
> > doing something very wrong.
> >
> > If you work with C++, downcasts should be relatively rare and should
> > generally be avoided but to say that they are always bad, you have to be
> > either a genius which came up with a new theory, or to be ignorant both
> > of practical problems and type theory.
>
> I will accept that in some languages, down-casting is both accepted and
> required. It is my position that down-casting is acceptable as long as
> we are able to statically determine that the down-cast in question will
> always succeed. Preferably without looking outside the class that
> contains the downcast.

Have you heard of typecase ?

It's the equivalent of the clumsy:
if ( o instanceof Type)
((Type) o). doSomething();

Unfortunately no en-vogue language has it.

> In the first example Mr. Martin gave, that was the case. In the second,
> that was not the case, and (in past discussions on this issue) Mr.
> Lahman (and others) have shown that there are other (and IMO better)
> design options available.

Like C-style cast instead of downcats, that is a worse solution than ever.

Furthermore no particular example of "redesign" is going to be "convincing"
since what is needed is a *proof* that any and all situations involving a
downcast
can be *conveniently* transformed into an equivalent implementatiion without
downcast.

Well, considering the posts I've seen so far, I don't even a slight attempt
towards this goal have been tried nor any signs or shown that such an
attempt might
be within grasp.

Costin Cozianu


H. S. Lahman

unread,
Sep 8, 2002, 10:58:57 AM9/8/02
to
Responding to Petrosian...

Once again I have to point out that we are not talking about casting in
general. We are only talking about downcasting superclass to subclass.

> I see that you are always assuming that the callers of such
> implementations have to know about cast and do some rechecking (call
> appropriated constructor or pass appropriated pointer).But that's what
> we are avoiding by using casts, to separate the caller of directly
> knowing what particular implementation needs.


No, what I am assuming is that Patient's implementation must understand
Doctor's subclassing structure in order to do a downcast. That
structure is a private implementation matter for a particular Doctor
instance and Patient has no business knowing about it.

But this is beside the point since one doesn't need to subclass Doctor
in this situation so there is no need for a downcast.


> In case of Doctor example system will load any implementation of
> Patient without assuming its constructor (simply invokes empty
> constructor) and will keep on passing Doctors until the current
> Patient implementation will accept one of them or having Patient go
> other list of doctors and see which one of them is Driver too.
> This way we totally encapsulate the logic of choosing doctor inside
> patient and not into other third party component (Patient knows what
> it needs and Doctor knows what it can do and a third party component
> only keeps list of available doctors).
> Otherwise with your approach every time a new patient gets loaded with
> new requirements we have to change the middle component to know what
> Doctor to pass to it.


The relationship exists independently of individual Patient or Doctor
characteristics. It defines a logical relationship for inter-object
collaborations, not individual class abstractions. That is an external
context for an object so a participating object should know as little
about its semantics as possible. IOW, it should be possible for the
object to participate in other collaborations without change when the
solution flow of control is modified.

As a general rule of thumb the implementation of an object should only
have enough knowledge of relationships sufficient for addressing
messages (i.e., selecting the correct relationship to navigate). The
semantics of defining the relationship (i.e., who the specific
participants are) is a matter of overall problem solution context, not
the implementations of participating instances.

Sometimes such instantiation semantics may be logically the
responsibility of one of the participants (e.g., in a composition
relationship) but that is the exception rather than the rule. For
downcasting there is no way that that knowledge should include specifics
of the collaborating object's subclassing structure. That goes well
beyond any reasonable notion of context independence for object
implementations.


>
>
>>A far better approach, which also eliminates the casting problems, is to
>>properly implement relationship navigation. If access to a driving
>>Doctor is implemented with a pointer when Patient is created, Patient
>>will *always* access the correct Doctor for any context for the rest of
>>its natural life. And if the Patient/Doctor relationship changes no one
>>will care except whoever instantiates the relationship. That is, the
>>smarts to ensure the correct Doctor only has to be applied once in one
>>place -- when the relationship is instantiated (e.g., usually by whoever
>>invokes Patient's constructor).
>>
>
> Its not true the implementation of Patient will still care about
> doctor being a Driver and implementation Doctor has to care to be a
> Driver.


I think this is going off on a tangent. This particular context was not
about casting of any sort; it was about why passing instances as method
arguments is a poor way of instantiating relationship navigation.

I don't understand what this has to do with what I said. The dependence
here is in the caller of Patient.whatever(Doctor). Passing a Doctor
instance (or any instance) as a method argument places the
responsibility in the caller of ensuring that the instance is consistent
with the callee. That means that the caller must be aware of the
semantics of the Patient/Doctor relationship, regardless of its own
context. That breaks encapsulation because the Patient/Doctor
relationship is a matter between them alone.

Instantiating the relationship in one place at one time is clearly
vastly superior from a maintenance viewpoint to instantiating the
relationship repeatedly in every context where Patient's behavior is
invoked. The whole point of properly instantiating the relationship
navigation once is to avoid the clients of Patient needing to worry
about providing the right Doctor instance in every method call.
Instantiating the relationship properly between Patient and Doctor
eliminates the need for clients to pass a Doctor instance.

Daniel T.

unread,
Sep 8, 2002, 11:26:44 AM9/8/02
to
"Uncle Bob (Robert C. Martin)"
<u.n.c.l.e.b.o.b.@.o.b.j.e.c.t.m.e.n.t.o.r.d.o.t.c.o.m> wrote:

> On Sat, 07 Sep 2002 13:08:28 GMT, "Daniel T." <a@b.c> wrote:
> >Why not:
> >
> >HourlyEmployee* HorlyEmployee::find(int id) {
> > HourlyEmployee result = new HourlyEmployee();
> > fillWithEmployeeInfo( result ); // get's employee data from DB
> > // throws if he isn't hourly
> > return result;
> >}
> >
> >and simular with the others. I would not call this "substantial code
> >duplication" BTW any more than I would call a pointer dereference
> >"substantial code duplication".
>
> I would call this substantial code duplication.

I guess we have different definitions of what "substantial code
duplication" is. I posit that if:

a = b + c;

is in one place in the code and:

d = e + f;

is in another. This is not "substantial code duplication", despite the
fact that the same two functions may be getting called in both places.
The same goes with the code I wrote above...


>I'd also call it very
> error prone. Every new derivative of Employee must have its own
> particular variant of this code.

Because of the design choice being made, every new derivative of
Employee must have whole bunch of code that is a "particular variant"
(in this case a whole new "add transaction" hierarchy must be created
for each employee sub-type). That is what usually happens in parallel
hierarchy type problems... Change the design so that Employee's can be
treated generically and this problem goes away.

> Also, I believe there is either a downcast in the function
> 'fillWithEmployeeInfo', or there is one fillWithEmployeeInfo for each
> different type of employee.

Of course there isn't. That would imply that 'fillWithEmployeeInfo'
contains an if..else if.. else if series, which simply isn't necessary
because any sub-type related work can be in the particular subtype
creation function.

H. S. Lahman

unread,
Sep 8, 2002, 11:53:55 AM9/8/02
to
Responding to Harris...


> I think you are misreading the code. The relevant line was:
>
> return this->field == pRhs->field;
>
> This isn't returning pRhs->field. It is returning the result of comparing
> pRhs->field with this->field.


You're right, I misread it. Sorry about that.


>
> I don't know whether you are familiar with C++, but "==" compares for
> equality, "->" gets a field from an object and "this" refers to the
> current object.


I should know better. Many, many moons ago when we actually did coding
we used C++. We had a coding standard requiring level 4 warnings should
be always on and there should be none expressly for this situation. So
your statement would raise an L4 warning unless it was parenthesized and
using the parentheses forced the author to at least think a bit more
about it.


>>I will buy the fact that in this particular case (stream is a C++
>>OStream) you will need a downcast, though. (This is the example I
>>mentioned elsewhere in the tread that I couldn't think of where
>>downcast is used outside the context of problem space constraints.)
>>That's because (a) the language itself provides no other mechanism for
>>handling the problem and (b) the inheritance from Object is not a
>>problem space feature, it is a pure language implementation feature.
>>IOW, what you are really checking is for the type of objects that are
>>completely unrelated in the problem space.
>>
>
> I'm not sure what kind of feature you have in mind for (a). Do you mean
> multi-methods, or some such?


Actually, I have no idea and I am skeptical there is a satisfactory
solution. For example:

One approach would be that each class has an identity that is checkable
at the instance level. I can see why OOPL developers don't provide
that, though. It would allow a whole lot of abuses outside the OStream
context. In effect it would be a downcast that one could apply to any
object.

OTOH, they could have provided a similar facility that was exclusive to
OStream. But then one couldn't make home grown OStream equivalents (per
your example below). Basically a downcast provides that facility but
the problem is that it applies to any supertype, not just Object.

So maybe what they need is a downcast that can only be applied to the
root superclass. It wouldn't really be a downcast because any problem
space subclassing would be flattened to the leaf class in hand. That
flattening means that it could be applied any place so we are back to
the abuses of the first paragraph.

So maybe the OOPL developers did think about it an selected the lesser
of evils. B-) But that means it is up to the methodologies to control
the abuses by nixing things like downcasting as a relationship
navigation tool.


>
> For (b), I agree the inheritance from Object is not a concern of the
> problem space of find(). Find() only cares about Objects, not about
> subclasses of Object. However, there are other occasions where code does
> need to collaborate with specific subclasses.
>
> For example, there is an "end of paragraph" object which carries
> formatting information which applies to the paragraph. Most of the time
> the Paragraph can store an explicit link to its EOP object and navigate
> that without any downcasting. However, sometimes text is presented just as
> a flat stream of objects, without being structured into paragraphs. In
> order to recover the structure, some kind of type discovery must take
> place.


I agree that any sort of generic object stream is a candidate for
downcasting. But I also see that as a quite different context than
relationship navigation for collaborations.


>
> In general what is happening here is an interplay between different
> subsystems. For the find() subsystem and its ilk, all Objects are alike.
> For paragraph formatting, the EOP subclass is special. When objects get
> passed around between the subsystems, static type information gets lost
> and has to be recovered via dynamic type discovery mechanisms (of which
> dynamic cast is the most basic).
>
> Often, perhaps always, it is possible to argue that the static type
> information needed by one subsystem could be preserved by every subsystem.
> However, that puts a burden on the other subsystems. For this example, a
> stream of character objects, it isn't clear to me how it could be done and
> even if theoretically possible, I am pretty sure that it shouldn't be
> done. I think using dynamic type discovery is simpler, more efficient and
> more maintainable.


True. One could cite XML as an example. But that sort of static type
transfer has it price in performance because it somehow needs to get
encoded and decoded for each transfer.

However, one can also argue that each subsystem is created with its own
view of typing suited to the problem at hand. More importantly, it is
defined as part of the subsystem development independently of other
subsystems. So one really only needs a mapping from one view to the
other. Pure data message interfaces accomplish this through the message
identifier and data packet structure. Each subsystem is then free to
interpret the data packet elements according to its own type view once
it knows the message identifier.

This is simple and generic. It is also quite versatile because the
subsystems can have very different views of the types (e.g., The Balance
attribute of an Account class in one subsystem maps to a full Control
class in a GUI subsystem). No downcasting but the price is still
encoding/decoding the messages. Fortunately at the subsystem level that
price can usually be absorbed.

H. S. Lahman

unread,
Sep 8, 2002, 12:48:08 PM9/8/02
to
Responding to Martin...


>>Object::find returns an object that does not in any way depend upon
>>whether or not its class is a member of a subclassing taxonomy; it only
>>depends on the individual class membership. The underlying
>>infrastructure is entirely orthogonal to any is-a relationship. The
>>interesting thing is why one can use the simple cast with impunity.
>>
>
> All you are saying is that the dynamic state of the system *knows*
> that the Object is an HourlyEmployee, and can therefore cast with
> impunity. This was precisely my original argument.


But the main point here was that one doesn't need the downcast.

However, there is a very subtle difference. In all these examples one
has to assume that relationship navigation has been properly
instantiated so that one has the right instance in hand (i.e.,
referential integrity is guaranteed for the navigation). In my examples
I specifically implemented the OOA/D relationship constraint
specification. That is easily traced against the specification. So
long as that specification was correct, one is assured of referential
integrity.

But in your case you are not implementing the specification because you
are navigating an unconstrained relationship. As a result it is not
traceable against the specification. So you have to explicitly validate
that navigation to ensure it behaves _as if_ it were implemented against
the specification for all relevant cases. IOW, you are in the position
of validating a unique special case every time you use downcast.

The situation is <tenuously> analogous to inserting Assembly code among
BLISS 3GL statements (which is legal in BLISS). BLISS maintains all
sorts of scope integrity through the language specification. But as
soon as one inserts that Assembly one risks violating that integrity
because the Assembly is unconstrained by the language specification. So
one has to validate *all* integrity explicitly rather than relying on
the language specification to ensure some things will Just Work.


>
> However, I think you need to brush up on your C++. There are a number
> of very good reasons why the dynamic_cast is a better choice than the
> straight cast. 1. If there's any virtual inheritance in the path from
> Object to HourlyEmployee, the straight cast will fail. 2. If there is
> any multiple inheritance in the path you could get into trouble too.
> So I'd only use the straight cast (or rather a static_cast) in those
> rare instances where the real-time cost of the dynamic_cast was too
> high to pay.


Remember I'm a translationist; I haven't written 1KLOC of C++ total in
the last decade. B-)

But the fact that C++ is broken is beside the point. The issue is
whether one should honor OOA/D relationship constraints in the
implementation. [I'm not going to waste the time to verify (1) but it
seems bizarre to me that a leaf instance has not resolved all
inheritance concretely for a cast. (While C++ allows one to create
instances that are not leaf instances, anyone who does that gets what
they deserve.) If one employs multiple inheritance that has its own set
of problems that downcasting won't resolve anyway.]

Tsolak Petrosian

unread,
Sep 8, 2002, 4:12:16 PM9/8/02
to
Ok, I see that we see downcasting in different context.

> No, what I am assuming is that Patient's implementation must understand
> Doctor's subclassing structure in order to do a downcast. That
> structure is a private implementation matter for a particular Doctor
> instance and Patient has no business knowing about it.

I guess what you are saying is that Patient doesnt have to care about
class that implements Doctor type, for example I agree that casting
from Doctor to SpecificDoctorImpl is wrong.
But thats why I explained how A2 was accesing C from B, its still
downcasting except we dont aceess subclass of B but the other type O1
was implementing, in this case C (or Driver in Doctor case).

> I don't understand what this has to do with what I said. The dependence
> here is in the caller of Patient.whatever(Doctor). Passing a Doctor
> instance (or any instance) as a method argument places the
> responsibility in the caller of ensuring that the instance is consistent
> with the callee. That means that the caller must be aware of the
> semantics of the Patient/Doctor relationship, regardless of its own
> context. That breaks encapsulation because the Patient/Doctor
> relationship is a matter between them alone.

Yes because thats the contract of Patient which has method
"accept(Doctor d)", which clearly defines what it needs.

> Instantiating the relationship in one place at one time is clearly
> vastly superior from a maintenance viewpoint to instantiating the
> relationship repeatedly in every context where Patient's behavior is
> invoked. The whole point of properly instantiating the relationship
> navigation once is to avoid the clients of Patient needing to worry
> about providing the right Doctor instance in every method call.
> Instantiating the relationship properly between Patient and Doctor
> eliminates the need for clients to pass a Doctor instance.

All caller has to care is that it passes object of type Doctor to
Patient, the rest is done by Patient to ensure that object also
implements Driver type.Bascially caller will always assume that
Patient might not accept all Doctors passed to it, so all it has to do
is try to pass as many Doctors as it has until Patient accepts one of
them.

Tsolak Petrosian

Uncle Bob (Robert C. Martin)

unread,
Sep 8, 2002, 9:10:12 PM9/8/02
to
On Sun, 08 Sep 2002 16:48:08 GMT, "H. S. Lahman"
<vze2...@verizon.net> wrote:

>Remember I'm a translationist; I haven't written 1KLOC of C++ total in
>the last decade. B-)

Then perhaps you should not be making authorotative statements about
C++ coding techniques.

Dave Harris

unread,
Sep 9, 2002, 4:04:00 AM9/9/02
to
vze2...@verizon.net (H. S. Lahman) wrote (abridged):
> > return this->field == pRhs->field;
>
> I should know better. Many, many moons ago when we actually did coding
> we used C++. We had a coding standard requiring level 4 warnings
> should be always on and there should be none expressly for this
> situation. So your statement would raise an L4 warning unless it was
> parenthesized and using the parentheses forced the author to at least
> think a bit more about it.

Well, it's probably not worth going into, but I've compiled it with VC++
6, warning level 4, and it is silent. Indeed, I see nothing there which
requires parenthesis. I can't imagine how it could be parsed wrongly.


> I agree that any sort of generic object stream is a candidate for
> downcasting. But I also see that as a quite different context than
> relationship navigation for collaborations.

OK. So downcasting isn't always an error, just an error in certain
contexts.


> However, one can also argue that each subsystem is created with its own
> view of typing suited to the problem at hand. More importantly, it is
> defined as part of the subsystem development independently of other
> subsystems. So one really only needs a mapping from one view to the
> other. Pure data message interfaces accomplish this through the
> message identifier and data packet structure. Each subsystem is then
> free to interpret the data packet elements according to its own type
> view once it knows the message identifier.

I'm happy to see a lot of agreement here :-)

H. S. Lahman

unread,
Sep 9, 2002, 1:59:41 PM9/9/02
to
Responding to Petrosian...


>>No, what I am assuming is that Patient's implementation must understand
>>Doctor's subclassing structure in order to do a downcast. That
>>structure is a private implementation matter for a particular Doctor
>>instance and Patient has no business knowing about it.
>>
>
> I guess what you are saying is that Patient doesnt have to care about
> class that implements Doctor type, for example I agree that casting
> from Doctor to SpecificDoctorImpl is wrong.


Then I don't understand why we exchanged all these messages. I thought
htat all this time you were arguing that downcasting was OK to do. B-)


> But thats why I explained how A2 was accesing C from B, its still
> downcasting except we dont aceess subclass of B but the other type O1
> was implementing, in this case C (or Driver in Doctor case).


I don't follow this. One can't access C from B. They are disjoint sets
of O1. A given instance of O1 must be *either* a B or a C but it can't
be both.

[Actually one can access a *different* O1 instance from a B instance
that happens to be a C -- provided a relationship exists between them at
the subclass level. However, that does not involve downcasting.]

The rest of this discussion is really going far afield and has nothing
to do with any sort of casting, much less downcasting...


>>I don't understand what this has to do with what I said. The dependence
>>here is in the caller of Patient.whatever(Doctor). Passing a Doctor
>>instance (or any instance) as a method argument places the
>>responsibility in the caller of ensuring that the instance is consistent
>>with the callee. That means that the caller must be aware of the
>>semantics of the Patient/Doctor relationship, regardless of its own
>>context. That breaks encapsulation because the Patient/Doctor
>>relationship is a matter between them alone.
>>
>
> Yes because thats the contract of Patient which has method
> "accept(Doctor d)", which clearly defines what it needs.


That is only true if Patient.accept is actually just a mechanism for
instantiating the relationship outside of the Patient (or Doctor)
constructor (e.g., it initializes a private pointer-to-Doctor within
Patient). Usually this is only done when the relationship is
conditional during the execution time span or one needs to swap the
participants in the relationship during the execution.

That is a quite different ball game because a different suite of rules
apply to ensure referential integrity. For example, it almost always
means that Patient.accept must be called within the same scope (method)
as the Patient constructor if the relationship is unconditional. IOW,
this is a relationship instantiation technique where different rules and
policies apply that have nothing <directly> to do with individual
Patient or Doctor responsibilities.


>
>
>>Instantiating the relationship in one place at one time is clearly
>>vastly superior from a maintenance viewpoint to instantiating the
>>relationship repeatedly in every context where Patient's behavior is
>>invoked. The whole point of properly instantiating the relationship
>>navigation once is to avoid the clients of Patient needing to worry
>>about providing the right Doctor instance in every method call.
>>Instantiating the relationship properly between Patient and Doctor
>>eliminates the need for clients to pass a Doctor instance.
>>
>
> All caller has to care is that it passes object of type Doctor to
> Patient, the rest is done by Patient to ensure that object also
> implements Driver type.Bascially caller will always assume that
> Patient might not accept all Doctors passed to it, so all it has to do
> is try to pass as many Doctors as it has until Patient accepts one of
> them.


That is not a good approach because the caller must still know about the
semantics of the Patient/Doctor collaboration. In particular, it must
know that the "current" Doctor it is providing may not be acceptable so
it has to have code to deal with that possibility (i.e., to try a
different Doctor). That is logically no different that the client doing
the filtering and always providing the correct Doctor.

But from the Patient's view there is also a dependency. To reject a
Doctor Patient must know that it is possible for the client to pass an
inappropriate Doctor. That means Patient also has a context dependence
for which code must be explicitly supplied. In particular, it must know
why it can't collaborate with all Doctors. That is a matter of solution
flow of control, not Patient responsibilities. For example, which
Doctors are relevant may depend on something unrelated to medicine like
physical location. In that case, such knowledge would severely break
the cohesiveness of the Patient abstraction.

OTOH, if the relationship is instantiated once (possibly with the
interpretation of Patient.accept above), that can be done by whoever
understands the semantics of the constraint (e.g., who can compute
shortest commuting time between Patients and Doctors). From then
onwards no client of Patient needs to know about the relationship
because Patient will always collaborate privately with the correct
Doctor via the relationship.

Nor does Patient's implementation need to be aware that any other
Doctors even exist, much less why they are inappropriate for
collaboration. Thus Patient implements only the intrinsic properties of
a Patient in the problem context.

H. S. Lahman

unread,
Sep 9, 2002, 2:44:16 PM9/9/02
to
Responding to Harris...


>>I should know better. Many, many moons ago when we actually did coding
>>we used C++. We had a coding standard requiring level 4 warnings
>>should be always on and there should be none expressly for this
>>situation. So your statement would raise an L4 warning unless it was
>>parenthesized and using the parentheses forced the author to at least
>>think a bit more about it.
>>
>
> Well, it's probably not worth going into, but I've compiled it with VC++
> 6, warning level 4, and it is silent. Indeed, I see nothing there which
> requires parenthesis. I can't imagine how it could be parsed wrongly.


The problem we were trying to avoid was typos where one forgets one of
the '=', converting a logical comparison to an assignment.

if (x == y) // logical expression has no warning

if (x = y) // logical expression yields an L4 warning

if ((x = y)) // possible arithmetic expression has no warning


The parentheses tell the compiler it may be an arithmetic expression
instead of a logical expression so it doesn't yield a warning. It's a
nice trick to deal with a common typo and inspection blind spot.
(Clearly not bullet proof, but quite effective.)

I think I know why the compiler was silent for your expression. I
haven't done C++ in years but I think the compiler infers the expected
type of expression from the return value signature. So for a bool return

return x == y; // logical expression; no warning

return x = y; // logical expression; L4 warning

return (x = y); // possible arithmetic expression; no warning

which means I had the sense of when to parenthesize wrong for return
values. The mind is always the second thing to go.

OTOH, I have no idea whether this still works in v6; the last one I used
was v5 and we had the standard even before that.

Tsolak Petrosian

unread,
Sep 9, 2002, 10:49:49 PM9/9/02
to
I guess I had to draw this diagram instead of simply explaining the situation.
Same applies to Patient-Doctor example.

A---------+ Object
| | |
+----@----+ | +---@---+
| | | | |
A1 A2 +----+B C +-+
| | | |
| +---@---+ |
| | |
| O1 |
+----------------------+

@ - is inheritance sign.

Here A1 and A2 are conctrete classes which implement interface A and
O1 is conctrete class which implements B and C interfaces.
Basically A2 will cast instance of O1 to C.
What kind of casting is this then?

Tsolak Petrosian

H. S. Lahman

unread,
Sep 10, 2002, 1:36:37 PM9/10/02
to
Responding to Petrosian...


Interface inheritance is a different issue. The is-a relationship and
downcasting are about subclassing, not inheritance. Nonetheless, what
you have drawn says that a B specialization of O1 is only accessible
through B and O1 interfaces and a C specialization of O1 is only
accessible through C and O1 interfaces. So B anc C are still disjoint
subclasses in the is-a relationship.

I've never used a language with pure interface inheritance so I don't
know the syntax issues around identifying the interface to use.
However, I would expect that the mechanism where the relationship
constrains the valid specialization to be substantially the same. So
one should be able to establish navigation exclusively to one of those
subclasses w/o regard to the other.

For example, I would expect one can define a navigation pointer in A2
that points exclusively to an instance having a C and O1 interface that
does not require a cast of O1 (i.e., A2 does not know syntactically
about the O1 inheritance; it sees the combination of C and O1 interfaces
as a single interface for a C specialization subclass). IOW, in A2
syntax like

C* Cptr;

is possible to restrict access to only members of the C subclass and the
C and O1 interfaces will be automatically resolved for those members
without explicit casting.

peter_douglass

unread,
Sep 10, 2002, 2:10:39 PM9/10/02
to
Responding to Tsolak Petrosian ...

Hi,
I'm confused as to why A2 will cast instances of O1 to C. The interface
of O1 already includes the interface for C. I'm probably missing something
here.

--PeterD

Paul C

unread,
Sep 11, 2002, 4:01:59 AM9/11/02
to

"Uncle Bob (Robert C. Martin)"
<u.n.c.l.e.b.o.b.@.o.b.j.e.c.t.m.e.n.t.o.r.d.o.t.c.o.m> wrote in message
news:s8j0nu8uem6sjljpm...@4ax.com...
> On Fri, 30 Aug 2002 19:34:35 -0500, Dmitry Shulga

> Visitor is very fast, but introduces a rather nasty dependency cycle.
> Dynamic_cast is a bit slow, but avoids the dependency cycle.

I would argue that the cyclid dependency still exists with the downcast.

Granted the cycle of static type references has been broken which is
good but the part of the code that builds the castable object tree is
still IMO conceptually dependent on the code doing the downcast.
It must afterall not only honour the contract of its own interfaces, but
honour the "contract" it has with the downcaster.

Thus we still have a cycle of defacto interfaces (because I'm treating
the assumptions that the downcaster makes as an interface) but
we have downgraded one of those intefaces from a real one to
an interface-by-convention in order to solve geometric time
compilation in say c++.

This is fine in a small example but I question whether this creates a
worse problem than in solves in come cases: we now have a
dependency that is not visible in the static type declarations.
A maintenance programmer now can now no longer assume
that he has merely to stay within the contracts of the class
interfaces - he has to examine potentially *all* client code in
order to determine that he is not breaking any implicit contracts
with downcasting clients.

Reading "Large scale C++ design", the most exaustive text I know
of on the subject of the evils of cyclic depencies, Lakos points
out (in so many words) that there are bassically three dowsides to cyclic
dependency:
1) geometric compile times
2) reduced unit testibility
3) reduced undertandability

IMO the dynamic cast solution only solves 1 and if anything aggravates
3.

Paul C.


Uncle Bob (Robert C. Martin)

unread,
Sep 11, 2002, 10:33:46 AM9/11/02
to


A client who does this:

Employee* e = getEmployee(ssn);
if (HourlyEmployee* he = dynamic_cast<HourlyEmployee*>(e))
he->addTimeCard(tc);

only needs to know about Employee and HourlyEmployee. It does not
need to know about "potenitally *all* client code". More specifically
it knows nothing at all about CommissionedEmployee or
SalariedEmployee. Indeed, it knows no more than any other client of
HourlyEmployee would know.

So I would argue that, in this case, there is no cycle; either
physically or conceptually.

Daniel T.

unread,
Sep 11, 2002, 2:48:59 PM9/11/02
to
"Uncle Bob (Robert C. Martin)"
<u.n.c.l.e.b.o.b.@.o.b.j.e.c.t.m.e.n.t.o.r.d.o.t.c.o.m> wrote:
> A client who does this:
>
> Employee* e = getEmployee(ssn);
> if (HourlyEmployee* he = dynamic_cast<HourlyEmployee*>(e))
> he->addTimeCard(tc);
>
> only needs to know about Employee and HourlyEmployee. It does not
> need to know about "potenitally *all* client code". More specifically
> it knows nothing at all about CommissionedEmployee or
> SalariedEmployee. Indeed, it knows no more than any other client of
> HourlyEmployee would know.
>
> So I would argue that, in this case, there is no cycle; either
> physically or conceptually.

I agree that there is no cycle. We can tell that by just looking at the
UML

[Client]--->[Employee]
| A
| |
+----->[HourlyEmployee]

The problem is that there is a dependency in Client on the more than
just Employee and HourlyEmployee. There is also a dependency on the
*relationship* between Employee and HourlyEmployee. IE we cannot break
the relationship between Employee and HourlyEmployee without also
breaking Client.

Also, as I pointed out earlier. The implication with the code above is
that there is a separate client for each sub-type of Employee. Anytime
an Employee sub-type is added, removed, or changed, the client structure
must be modified as well. Better would be to redesign so no client
depends on a particular sub-class of employee, or at least break the
dependency between client and the Employee/HourlyEmployee relationship.

This is the same basic argument as LoD.

Tsolak Petrosian

unread,
Sep 11, 2002, 7:00:22 PM9/11/02
to
Before answering I want to point out that O1 is concrete class and not
an interface or abstract class, same as A1 or A2.

Tsolak Petrosian

"H. S. Lahman" <vze2...@verizon.net> wrote in message news:<3D7E2E5D...@verizon.net>...

Tsolak Petrosian

unread,
Sep 11, 2002, 7:05:11 PM9/11/02
to
The reason is that A interface has "M(B b)" method which conctrete
class A1 and A2 implement. But the issue is that A2 implementation
checks if the instance (from the diagram it will be instance of class
O1) passed to M also implements C too so that A2 will query additional
services from that instance.

Tsolak Petrosian


"peter_douglass" <bai...@gis.net> wrote in message news:<zAqf9.257785$_91.2...@rwcrnsc51.ops.asp.att.net>...

peter_douglass

unread,
Sep 11, 2002, 9:39:48 PM9/11/02
to
Responding to Tsolak Petrosian...

> The reason is that A interface has "M(B b)" method which conctrete
> class A1 and A2 implement. But the issue is that A2 implementation
> checks if the instance (from the diagram it will be instance of class
> O1) passed to M also implements C too so that A2 will query additional
> services from that instance.

Perhaps it is not that important, but method M in A2 doesn't need any C, but
only a C which is also a B. O1 is the only class on the diagram which
inherits from both B and C, so if a cast were needed, it would seem that a
cast to O1 would be appropriate, rather than to C, or do you disagree?

I suppose the argument could be made that at some point an O2 might be added
which also inherits from B and C, but in that case, I would think it would
make sense to have an abstract class Ozero which inherits from B and C, and
have both O1 and O2 inherit from Ozero.

However, if your diagram is changed to


A---------+ Object
| | |
+----@----+ | +---@---+
| | | | |
A1 A2 +----+B C
| | |

| +---@---+
| |
+-------------O1

It becomes clear that what is depicted is covariant inheritance. A2 is a
subclass of A, method M in A requires a B, but method M in A2 requires a
subclass of B, namely O1.

I know that covariant inheritance is common in Eiffel programming. It is
less common in C++, and in fact I have never seen an instance of it.

Questions for HSL. Can covariant inheritance be an acceptable part of what
you would consider a well designed OO model? Only at the OOP level? At any
level?

Question for anyone. Are there problems for which covariant inheritance is
a much better solution than any contravariant alternative? Can one, or how
would one, implement covariant inheritance in a language like C++ without a
downcast? (I'm sure I could research this myself, but I imagine the answers
might be interesting to others as well).

--PeterD

Tsolak Petrosian

unread,
Sep 12, 2002, 11:24:47 AM9/12/02
to
You are missing the point here.
Neither A1 nor A2 need to know about O1 (O1 is conctrete class and not
a interface or type), the casting A2 does is implementation detail and
shouldn't be exposed outside (i.e with covariant inheritance).

Tsolak Petrosian

"peter_douglass" <bai...@gis.net> wrote in message news:<EfSf9.275984$_91.3...@rwcrnsc51.ops.asp.att.net>...

H. S. Lahman

unread,
Sep 13, 2002, 3:40:16 PM9/13/02
to
Responding to Petrosian wrote:

> Before answering I want to point out that O1 is concrete class and not
> an interface or abstract class, same as A1 or A2.


As are B and C (or else we aren't talking about downcasting).

Downcasting is about accessing a concrete member of a particular
specialization from among the many specializations accessible through
the superclass. IOW, a downcast selects a particular concrete
*implementation*.

If B and C are not concrete, then all we is talking about is selecting
an appropriate interface for a single implementation, which is a quite
different thing. This is why it is important to distinguish between
subclassing and inheritance.

H. S. Lahman

unread,
Sep 13, 2002, 4:48:14 PM9/13/02
to
Responding to Douglass...


> However, if your diagram is changed to
> A---------+ Object
> | | |
> +----@----+ | +---@---+
> | | | | |
> A1 A2 +----+B C
> | | |
> | +---@---+
> | |
> +-------------O1
>
> It becomes clear that what is depicted is covariant inheritance. A2 is a
> subclass of A, method M in A requires a B, but method M in A2 requires a
> subclass of B, namely O1.


This interpretation assumes a lot (e.g., overriding of implementation
inheritance). There is nothing in the relationships to suggest the same
method is involved. The A/B relationship just says that _in that
relationship context_ both A1 and A2 must deal exclusively with a B.

In fact, the diagram probably only makes sense if different methods are
involved. The A/B relationship is an invariant for all 'A's, regardless
of their implementation. Therefore it would be inconsistent for a
subclass to broaden that constraint (i.e., require a C as well as a B)
for a given behavior. That would only be consistent if the
relationships were different (i.e., represented different contexts).
But relationships are almost always associated with unique roles and
that implies different behaviors, which implies different methods.


>
> I know that covariant inheritance is common in Eiffel programming. It is
> less common in C++, and in fact I have never seen an instance of it.
>
> Questions for HSL. Can covariant inheritance be an acceptable part of what
> you would consider a well designed OO model? Only at the OOP level? At any
> level?


My short answer is: I have no clue. I don't know a precise definition
of 'covariant inheritance'. I looked in 11 different OO books plus the
UML v1.4 spec an not one mentioned it.

peter_douglass

unread,
Sep 13, 2002, 5:24:05 PM9/13/02
to
Responding to H. S. Lahman...

A discussion of covariance can be found in Meyer's OOSC2 chapter 17.3.
Also in _Covariance and Contravariance: Conflict without a Cause_ by
Giuseppe Castagna found at http://citeseer.nj.nec.com/172025.html, and also
many other places on the web.

The protypical example of covariance is a model of animals and their eating
habits. The abstract class "Animal" has an eat(Food f) method. However,
individual animals use _subtypes_ of Food in their eat methods. eg
Lion::eat(Meat m), Cow::eat(Grass m) etc. where Meat and Grass are
subtypes of Food.

Expressed another way, if a method in a subclass takes a subclass (or
subtype) of the arguments declared for the method in the supertype, one has
covariant inheritence.

What Tsolak Petrosian seems to want to do is to have the *signatures* be
the same for methods in the parent and child classes, but have the behaviors
"covariant". In the Animal::eat(Food f) example, he would want
Cow::eat(Food f) to typecheck, but have the behavior of the cow not accept
the Food unless the Food is in fact Grass. Statically it would be
no-variance, but dynamically, it seems to me to be a form of covariance. (I
say seems to me. TP seems to think not, and that I am on the wrong track,
which may very well be the case.)

--PeterD

H. S. Lahman

unread,
Sep 14, 2002, 12:07:07 PM9/14/02
to
Responding to Douglass...


> A discussion of covariance can be found in Meyer's OOSC2 chapter 17.3.
> Also in _Covariance and Contravariance: Conflict without a Cause_ by
> Giuseppe Castagna found at http://citeseer.nj.nec.com/172025.html, and also
> many other places on the web.
>
> The protypical example of covariance is a model of animals and their eating
> habits. The abstract class "Animal" has an eat(Food f) method. However,
> individual animals use _subtypes_ of Food in their eat methods. eg
> Lion::eat(Meat m), Cow::eat(Grass m) etc. where Meat and Grass are
> subtypes of Food.


Then the short answer is No, we use contravariance. But that is
methodological. We use relationships to enforce problem space
constraints so in your example we would never have an Animal/Food
relationship for the eating role. To properly describe the eating
constraints we would require individual relationships for Cow/Grass,
Lion/Meat, etc. Those relationships would be on a pure class-to-class
basis (i.e., their instantiation is independent of either hierarchy).

However, enabling that view goes much deeper. We only employ
specialization in the sense of adding characteristics (i.e., we do not
allow implementation overrides). [In fact, we do not even allow
concrete behavior in superclasses. That is, all significant behavior is
described in Mealy/Moore state machines and we do not employ Harel for
inheritance (though other translation methodologies do). Indirectly
this constrains the way we identify objects and allocate responsibilities.]

But all this is from the OOA model level where subclassing is crucial
but subtyping isn't very relevant. In translation all bets are off at
the OOP level because the translation engine is responsible for type
safety. If it can guarantee type safety using covariance, that's fine
because the application developer doesn't maintain the code.

[In fact, for this situation the translation engine can always guarantee
type safety because the participants (instances) must be defined in OOA
action language when the relationship is instantiated dynamically. So
the OOA model developer is responsible for ensuring referential
integrity and, consequently, type safety during that instantiation.]

*******

At the risk or reopening an old can of worms, I think this just
underscores the point that in OOA/D one thinks about things differently.
It is unnatural to think about the problem in the terms Castagna
employs for OOPL typing. (My initial reaction to his his first few
paragraphs was: this is not about OO application development, it is
about OOPL design.) In his world one is forced to deal with things in
terms of functions because that is the only paradigm in town for 3GLs.

For example, when I glance at Cow::eat(Grass m) and think: Don't Do
That!!! That's because OOA/D is built around the notion of
maintainability. Thus Cow::eat(Grass m) implies unnecessary
implementation dependence in the caller. Every caller in every context
who invokes Cow:eat must understand that only a Grass instance of Food
can be passed to a Cow every time. So if one decides that some Cows eat
sage, every caller's implementation must be modified because of its
implementation dependence on Cow's needs.

But in my OOA/D world that is not a problem. One instantiates the
relationship between a Cow and a Grass once (usually when a Cow is
created) and everyone invokes Cow::eat() oblivious to what Cows actually
eat. If one decides Cows also eat Sage, only one context needs to be
changed: where the relationship is instantiated. So one does not think
in terms of callers providing objects; one thinks in terms of objects
themselves navigating to the objects they collaborate with.

The point here is that in OOA/D a whole different suite of
methodological techniques play together to get to the same place as
covariance/contravariance in OOP typing. So the road traveled is quite
different. (It is also why a coherent methodology is so important in
OOA/D.)

Tsolak Petrosian

unread,
Sep 14, 2002, 2:24:44 PM9/14/02
to
> Downcasting is about accessing a concrete member of a particular
> specialization from among the many specializations accessible through
> the superclass. IOW, a downcast selects a particular concrete
> *implementation*.

Well I dont want to play with word meanings, but I assumed that
"Downcasting" in general means casting from superclass to subclass
where both can be concrete classes or interfaces.
For example casting from A to B
A
|
@
B

is still downcasting althought both of them are interfaces.
Or maybe we should call this casting differently?

Tsolak Petrosian

"H. S. Lahman" <vze2...@verizon.net> wrote in message news:<3D823FDB...@verizon.net>...

Tsolak Petrosian

unread,
Sep 14, 2002, 2:27:15 PM9/14/02
to
"peter_douglass" <bai...@gis.net> wrote in message news:<VHsg9.309504$aA.54382@sccrnsc02>...

Interesting but why would we want to do that?

Tsolak Petrosian

Bala Paranj

unread,
Sep 14, 2002, 4:23:38 PM9/14/02
to
> Hi,

> Inevitable and correct casting looks good in static languages, they are
> designed to automatically handle appropriate casting with no "mess".
> Inappropriate casting is a mess no matter what the language used, it
> just doesn't *look* as messy in dynamic languages.

I would say downcasting is fine as long as you don't violate the Open
Closed Principle. Any comments ?

Regards,
Bala

H. S. Lahman

unread,
Sep 15, 2002, 11:53:26 AM9/15/02
to
Responding to Petrosian...

>>Downcasting is about accessing a concrete member of a particular
>>specialization from among the many specializations accessible through
>>the superclass. IOW, a downcast selects a particular concrete
>>*implementation*.
>>
>
> Well I dont want to play with word meanings, but I assumed that
> "Downcasting" in general means casting from superclass to subclass
> where both can be concrete classes or interfaces.
> For example casting from A to B
> A
> |
> @
> B
>
> is still downcasting althought both of them are interfaces.
> Or maybe we should call this casting differently?


If B is a subclass of A (i.e., a disjoint subset of A instances), then
it is downcasting. If B is just an interface for A (i.e., an interface
to all members of the A instance set), then it is interface selection
via inheritance. I don't do Java so I don't know what the idiom is in
that world; perhaps interface selection is called downcasting there.

If so, then we are talking about quite different things. The problems
with downcasting are only relevant to subclassing.

peter_douglass

unread,
Sep 19, 2002, 9:55:42 AM9/19/02
to
Responding to Tsolak Petrosian...

> > Expressed another way, if a method in a subclass takes a subclass (or
> > subtype) of the arguments declared for the method in the supertype, one has
> > covariant inheritence.

> Interesting but why would we want to do that?

I have never used covariance, so any answer I give is only an
outsiders view. If one wants to "model the world", covariance seems
to occur naturally in some cases. In particular, if there are two
parallel hierarchies, they are often "naturally" related in a
covariant way. The example of
Animal::eat(Food f)
Cow::eat(Grass g)
illustrates this, where the two parallel hierarchies, are animals and
foods,

Another case where covariance has been argued to be useful is with
"binary methods". Binary methods are methods, such as equality
methods, which require two objects of the same identical class. For
example, if one has 2D and 3D points, it is often useful to have some
methods behave polymorphicly between the classes of 2D and 3D points.
However, for equality it seems appropriate to have the following
methods

Point2D::equals(Point2D p)
Point3D::equals(Point3D p)

--PeterD

It is loading more messages.
0 new messages