Re: avoid cast to derived



"Grizlyk" <grizlyk1@xxxxxxxxx> wrote:

There is example below and answer on it

Your code has a poor design. If the userUser class can only work
with userBase objects, then define it as such. If it can work with
all Base objects, then it shouldn't need to dynamic_cast the Base
object it has.

Can anybody say the way to avoid cast to derived in the case?

I didn't want to be the first to respond to this because Grizlyk and I
have been discussing it over in comp.lang.c++. Thank you HS for your
extensive coverage.


Part of the problem with the code is the fact that userUser::user2()
treats different sub-types differently. The one acceptable use of
dynamic_cast that we have talked about is when it's "all or nothing"
i.e., either the object is the proper sub-type or the method doesn't
deal with it at all. This code, however, opens up the possibility of the
userUser::user2() method needing to be modified when some other sub-type
of Base is created that userUser must treat differently as well.

Fundamentally though, most of the problems in the code comes from the
fact that the "User" class is incomplete. It simply cannot stand on its
own, and needs a client to provide a Base object with every message
send. How can this be fixed? Well, HS Lahman has discussed this before,
separate the relationship instantiation from the method invocation.
Following this rule, and using RCMs "Intelligent Child" pattern, we end
up with something like this:

class User {
protected:
virtual Base& base() = 0;
public:
virtual void user1() {
base().method();
}
virtual void user2() {
base().method();
}
};

class userUser : public User {
userBase* itsUserBase;
protected:
Base& base() {
return *itsUserBase;
}
public:
void setBase( userBase* b ) {
itsUserBase = b;
}

void user2() {
itsUserBase->adjust();
itsUserBase->method();
itsUserBase->adjust();
itsUserBase->method();
}
};

'userUser' no longer needs to wonder if he has a userBase, or some other
sort of Base.

Now, who is to call setBase? Obviously the code that knows what kind of
Base to give the User, that would be the factory function. You see, in
the OPs code, the User class was incomplete, so the factory was creating
and returning an incomplete object. The client was then forced to use
the factory again to create and return the rest of the User object (or
otherwise knowing what kind of User it has so it can create a Base
object on its own.) Instead of the client building the object from parts
provided by the factory, the factory itself should be turned into a
builder.

struct Creator {
virtual User* createUser() = 0;
};

struct userCreator: public Creator
{
User* createUser() {
userUser* result = new userUser;
result->setBase( new userBase );
return result;
}
};

Yes, this disallows some client's ability to change the Base object
being used, but this is a good thing. Imagine a situation where User
objects contain state and Base objects contain state, and these states
interacted with each other such that the client needed to know what
state each was in before passing a Base to a User. That kind of
intrusive need from the client is exactly what OO paradigm tries to
avoid.
.



Relevant Pages

  • Re: McSkype - other person typing thing?
    ... userbase who have the option. ... Or is there an iChat compatible non Mac OS client? ... I've had a lot of success with it, it's integration with Growl for the TTS notifications is really quite handy. ...
    (uk.comp.sys.mac)
  • Firewall and nfs mounts
    ... What ports are necessarily opened on an nfs server? ... Does the client need any ... Just found a cool new feature? ... Add it to UserBase ...
    (Fedora)
  • Re: McSkype - other person typing thing?
    ... userbase who have the option. ... Or is there an iChat compatible non Mac OS client? ... JFGI. ...
    (uk.comp.sys.mac)
  • Re: McSkype - other person typing thing?
    ... Ceresole) wrote: ... userbase who have the option. ... Or is there an iChat compatible non Mac OS client? ...
    (uk.comp.sys.mac)