OOPs way to do this in C++?

Discussion in 'C++' started by Jim Langston, Apr 10, 2007.

  1. Jim Langston

    Jim Langston Guest

    I want to change a class's methods at run time. I'm making a game and want
    NPC objects to have different methods depending on a game designer. That
    is, they may decide that this mob should be able to fly for movement, be
    agressivie for combat, etc... Rather than a bunch of switch statements or
    if..else I was thinking it would be good to be able to plug in
    functions/methods to the class.

    Well, there's the old C way, which I've done here, but am not happy with it
    at all. For a few reasons:
    1. I have to friend every function
    2. I have to assign a function pointer, and things can go wrong there, plus
    it gets to be a pain to maintain
    3. It's just not, IMO, the best way it could be, but I don't know of a
    better way.

    I've only shown the function here for movement, but in reality there would
    be one for movement, one for combat, one for guarding (if it's a guard),
    etc...

    Any suggestions on how to improve the design?

    #include <iostream>
    #include <string>

    class Base
    {
    friend void Walk( Base& base );
    friend void Fly( Base& base );
    friend void Swim( Base& base );
    friend void Float( Base& base );

    public:

    Base( int X, const std::string Movement ): X(X), Move_( NULL )
    {
    if ( Movement == "Flies" )
    Move_ = Fly;
    else if ( Movement == "Walks" )
    Move_ = Walk;
    else if ( Movement == "Swims" )
    Move_ = Swim;
    else if ( Movement == "Floats" )
    Move_ = Float;
    else
    Move_ = Walk;
    }

    void Move() { if ( Move_ != NULL ) { Move_( *this ); } }

    private:
    void (*Move_)( Base& );
    int X;
    };

    void Walk( Base& base )
    {
    base.X += 5;
    std::cout << "Walked to " << base.X << "\n";
    }

    void Fly( Base& base )
    {
    base.X += 7;
    std::cout << "Flew to " << base.X << "\n";
    }

    void Swim( Base& base )
    {
    base.X += 3;
    std::cout << "Swam to " << base.X << "\n";
    }

    void Float( Base& base )
    {
    base.X += 2;
    std::cout << "Floated to " << base.X << "\n";
    }

    int main()
    {
    Base MyBase( 10, "Flies" );

    MyBase.Move();

    std::string wait;
    std::getline( std::cin, wait );
    }

    Regards,

    Jim Langston
    Jim Langston, Apr 10, 2007
    #1
    1. Advertising

  2. Jim Langston

    aiooua Guest

    aiooua, Apr 10, 2007
    #2
    1. Advertising

  3. Jim Langston

    aiooua Guest

    aiooua, Apr 10, 2007
    #3
  4. Jim Langston

    Zeppe Guest

    Jim Langston wrote:

    > Any suggestions on how to improve the design?


    The correct answer, as aiouua said, is to use the decorator pattern.
    Anyway, I wanted to point out that even with your design you don't need
    any friend function. Consider this:

    #include <iostream>
    #include <string>
    #include <functional>

    class Base
    {
    private:
    void Walk();
    void Fly();
    void Swim();
    void Float();

    public:

    Base( int X, const std::string Movement ): Move_( NULL ), X(X)
    {
    if ( Movement == "Flies" )
    Move_ = std::mem_fun_ref(&Base::Fly);
    else if ( Movement == "Walks" )
    Move_ = std::mem_fun_ref(&Base::Walk);
    else if ( Movement == "Swims" )
    Move_ = std::mem_fun_ref(&Base::Swim);
    else if ( Movement == "Floats" )
    Move_ = std::mem_fun_ref(&Base::Float);
    else
    Move_ = std::mem_fun_ref(&Base::Walk);
    }

    void Move() { Move_(*this); }

    private:
    std::mem_fun_ref_t<void, Base> Move_;
    int X;
    };

    void Base::Walk()
    {
    X += 5;
    std::cout << "Walked to " << X << "\n";
    }

    void Base::Fly()
    {
    X += 7;
    std::cout << "Flew to " << X << "\n";
    }

    void Base::Swim()
    {
    X += 3;
    std::cout << "Swam to " << X << "\n";
    }

    void Base::Float()
    {
    X += 2;
    std::cout << "Floated to " << X << "\n";
    }

    int main()
    {
    Base MyBase( 10, "Flies" );

    MyBase.Move();

    std::string wait;
    std::getline( std::cin, wait );
    return 0;
    }


    As already said, it;s better to decorate, having a Base with

    virtual Move() = 0;

    and each derived class implement a different Move().

    Regards,

    Zeppe
    Zeppe, Apr 10, 2007
    #4
  5. Jim Langston wrote:
    > I want to change a class's methods at run time. I'm making a game and want
    > NPC objects to have different methods depending on a game designer. That
    > is, they may decide that this mob should be able to fly for movement, be
    > agressivie for combat, etc... Rather than a bunch of switch statements or


    switch statements are bad for extesibility.


    > if..else I was thinking it would be good to be able to plug in
    > functions/methods to the class.


    There are techniques that you can load up DLL's/DSO's and have methods
    register themselves. The Austria C++ generic factories do this.

    >
    > Well, there's the old C way, which I've done here, but am not happy with it
    > at all. For a few reasons:
    > 1. I have to friend every function
    > 2. I have to assign a function pointer, and things can go wrong there, plus
    > it gets to be a pain to maintain
    > 3. It's just not, IMO, the best way it could be, but I don't know of a
    > better way.



    The code below does what you describe - no friends, no function
    pointers, and is extensible by simply loading a DLL/DSO that registers
    methods on the fly. Note that it does not show how to unregister them
    when a DLL is unloaded.


    .......................................................
    #include <iostream>
    #include <string>
    #include <map>

    // define a position type
    class Position
    {
    public:
    Position( int X )
    : X(X)
    {
    }

    int X;
    };

    // define an abstract class that knows how to move a position
    class Mover
    {
    public:

    virtual void Move(
    Position & io_position
    ) const = 0;
    };

    // define a registry of movers - this should be handled as
    // a static local variable to a getter function...
    std::map<std::string, const Mover *> s_registry;


    class Base
    {
    public:

    Base( int X, const Mover * i_mover )
    : m_mover( i_mover ),
    m_position(X)
    {
    assert( m_mover );
    }

    Base( int X, const std::string & i_mover )
    : m_mover( s_registry[ i_mover ] ),
    m_position(X)
    {
    assert( m_mover );
    }

    void Move()
    {
    m_mover->Move( m_position );
    }

    private:

    const Mover * m_mover;
    Position m_position;
    };

    // helper class to register a type
    template <typename T>
    class Register
    {
    public:
    Register( const std::string & name )
    {
    s_registry[ name ] = new T();
    }
    };

    // define a walk method
    class Walk : public Mover
    {
    virtual void Move(
    Position & io_position
    ) const
    {
    io_position.X += 5;
    std::cout << "Walked to " << io_position.X << "\n";
    }
    };

    // register the walk method
    Register<Walk> reg_walker( "Walks" );


    // define and register the rest
    class Fly : public Mover
    {
    virtual void Move(
    Position & io_position
    ) const
    {
    io_position.X += 7;
    std::cout << "Flew to " << io_position.X << "\n";
    }
    };

    Register<Fly> reg_flyer( "Flies" );

    class Swim : public Mover
    {
    virtual void Move(
    Position & io_position
    ) const
    {
    io_position.X += 3;
    std::cout << "Swam to " << io_position.X << "\n";
    }
    };

    Register<Swim> reg_swimmer( "Swims" );


    class Float : public Mover
    {
    virtual void Move(
    Position & io_position
    ) const
    {
    io_position.X += 2;
    std::cout << "Floated to " << io_position.X << "\n";
    }
    };

    Register<Float> reg_floater( "Floats" );


    int main()
    {
    Base MyBase( 10, "Flies" );

    MyBase.Move();

    std::string wait;
    std::getline( std::cin, wait );
    }
    Gianni Mariani, Apr 10, 2007
    #5
  6. Jim Langston

    Noah Roberts Guest

    Noah Roberts, Apr 10, 2007
    #6
  7. Jim Langston

    James Kanze Guest

    On Apr 10, 12:44 pm, "aiooua" <> wrote:
    > On Apr 10, 3:40 pm, "aiooua" <> wrote:


    > > On Apr 10, 2:55 pm, "Jim Langston" <> wrote:
    > > > I want to change a class's methods at run time.

    > > try considering:
    > >http://en.wikipedia.org/wiki/Double_dispatch
    > >http://en.wikipedia.org/wiki/Visitor_pattern


    > and probably a more relevant:http://en.wikipedia.org/wiki/Decorator_pattern


    The pattern he's looking for is most likely strategy. (I'd also
    be leary about quoting the Wikipedia. It's reputation for
    accuracy is not very good.)

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
    James Kanze, Apr 10, 2007
    #7
  8. Jim Langston

    BobR Guest

    Jim Langston <> wrote in message...
    > I want to change a class's methods at run time. I'm making a game and

    want
    > NPC objects to have different methods depending on a game designer. That
    > is, they may decide that this mob should be able to fly for movement, be
    > agressivie for combat, etc... Rather than a bunch of switch statements or
    > if..else I was thinking it would be good to be able to plug in
    > functions/methods to the class.
    >
    > Well, there's the old C way, which I've done here, but am not happy with

    it
    > at all. For a few reasons:
    > 1. I have to friend every function
    > 2. I have to assign a function pointer, and things can go wrong there,

    plus
    > it gets to be a pain to maintain
    > 3. It's just not, IMO, the best way it could be, but I don't know of a
    > better way.
    >
    > I've only shown the function here for movement, but in reality there would
    > be one for movement, one for combat, one for guarding (if it's a guard),
    > etc...
    > Any suggestions on how to improve the design?


    You may want to check out "Thinking in C++" vol.2, "Patterns"->"Abstract
    factories". Might give you an idea.
    Couple that with what the other posters have suggested.


    Get "Thinking in C++", 2nd ed. Volume 1&2 by Bruce Eckel
    (available for free here. You can buy it in hardcopy too.):
    http://www.mindview.net/Books/TICPP/ThinkingInCPP2e.html
    --
    Bob R
    POVrookie
    BobR, Apr 10, 2007
    #8
  9. Jim Langston

    aiooua Guest

    On Apr 10, 9:50 pm, "James Kanze" <> wrote:
    > On Apr 10, 12:44 pm, "aiooua" <> wrote:
    >
    > > On Apr 10, 3:40 pm, "aiooua" <> wrote:
    > > > On Apr 10, 2:55 pm, "Jim Langston" <> wrote:
    > > > > I want to change a class's methods at run time.
    > > > try considering:
    > > >http://en.wikipedia.org/wiki/Double_dispatch
    > > >http://en.wikipedia.org/wiki/Visitor_pattern

    > > and probably a more relevant:http://en.wikipedia.org/wiki/Decorator_pattern

    >
    > The pattern he's looking for is most likely strategy.


    well, given that the additional features might need to applied/revoked
    dynamically, i think it's a classic case for decorator. also, neither
    state nor strategy support composition of features as naturally as
    decorator does. what if the mobile needs to swim & fly?

    ----
    #include<iostream>
    using namespace std;

    class Base{ public:
    virtual void move() { /* X += 7; */ cout <<"move me." << endl;}
    };

    class Decorator: public Base{
    protected:
    Base *base;
    public:
    Decorator(Base *b) : base(b){};
    void move(){base->move();}
    };

    class FlyDecorator: public Decorator{
    public:
    FlyDecorator(Base *b):Decorator(b){};
    void move() { cout << "I flew, "; base->move();}
    };

    class WalkDecorator: public Decorator{
    public:
    WalkDecorator(Base *b):Decorator(b){};
    void move() { cout << "I walked, "; base->move();}
    };

    class SwimDecorator: public Decorator{
    public:
    SwimDecorator(Base *b):Decorator(b){};
    void move() { cout << "I swam, "; base->move();}
    };

    int main(){
    Base *plain = new Base;
    plain->move();
    WalkDecorator *more = new WalkDecorator(plain);
    more->move();
    SwimDecorator *even_more = new SwimDecorator(more);
    even_more->move();
    FlyDecorator *lot_more = new FlyDecorator(even_more);
    lot_more->move();
    }
    ----

    this does all that the OP's code did, plus has all the additional
    benefits of decorator.

    thanks,
    aiooua, Apr 11, 2007
    #9
  10. Jim Langston

    Guest

    Hello,

    Below is simple way to do it.


    #include <iostream>
    #include <string>

    enum ACTIONTYPE {WALK, FLY, SWIM, FLOAT, COMBAT, GUARD};

    class Base
    {
    void Walk(void)
    {
    X += 5;
    std::cout << "Walked to " << X << "\n";
    }

    void Fly(void)
    {
    X += 7;
    std::cout << "Flew to " << X << "\n";
    }

    void Swim(void)
    {
    X += 3;
    std::cout << "Swam to " << X << "\n";
    }

    void Float(void)
    {
    X += 2;
    std::cout << "Floated to " << X << "\n";
    }

    void Combat(void)
    {
    X += 2;
    std::cout << "Combating around " << X << "\n";
    }

    void Guard(void)
    {
    X += 2;
    std::cout << "Gruarding around " << X << "\n";
    }

    public:


    Base( int X, const ACTIONTYPE Action1 = WALK ): X(X),
    Action( Action1 )
    {
    std::cout << "Action initialized to " << Action << "\n";
    }

    //void Move() { if ( Move_ != NULL ) { Move_( *this ); } }
    void doAction(void)
    {
    switch(Action) {
    case WALK:
    Walk(); break;
    case FLY:
    Fly(); break;
    case SWIM:
    Swim(); break;
    case FLOAT:
    Float(); break;
    case COMBAT:
    Combat(); break;
    case GUARD:
    Guard(); break;
    }
    }

    void changeAction(const ACTIONTYPE Action1)
    {
    Action = Action1;
    std::cout << "Action changed to " << Action << "\n";
    }



    private:
    //void (*Move_)( Base& );
    ACTIONTYPE Action;
    int X;
    };


    int main()
    {
    Base MyBase( 10, WALK);

    MyBase.doAction();
    MyBase.changeAction(FLY);
    MyBase.doAction();
    MyBase.changeAction(SWIM);
    MyBase.doAction();

    std::string wait;
    std::getline( std::cin, wait );
    }
    , Apr 12, 2007
    #10
  11. wrote:
    >
    > Hello,
    >
    > Below is simple way to do it.


    I believe that the OP said no switch statement ?

    Contrary to popular belief, enums are not very OO.

    A good method extensibility by "plug in", as the OP requested is to have
    a registry of methods and select the method based on a key (name of
    developer, time of day etc). The design requirement should be that
    there are no changes to the core application to extend via plug-in. In
    the design you propose below, you have:

    a) an enum to change - requires compiling all files when changed
    b) a method in the main class to add
    c) a new case in your switch statement

    In the design I proposed earlier, the only thing required is to add a
    new file to your linker or dynamically load a file containing the class
    type derived from an application class and a "registration" object to
    register the new method. No changes to the main code at all. The only
    thing to be careful is to force the linker to load the .obj/.o into the
    executable or dll/dso.

    i.e.

    // define a walk method
    class Walk : public Mover
    {
    virtual void Move(
    Position & io_position
    ) const
    {
    io_position.X += 5;
    std::cout << "Walked to " << io_position.X << "\n";
    }
    };

    // register the walk method
    Register<Walk> reg_walker( "Walks" );

    ....

    >
    >
    > #include <iostream>
    > #include <string>
    >
    > enum ACTIONTYPE {WALK, FLY, SWIM, FLOAT, COMBAT, GUARD};
    >
    > class Base
    > {
    > void Walk(void)
    > {
    > X += 5;
    > std::cout << "Walked to " << X << "\n";
    > }
    >
    > void Fly(void)
    > {
    > X += 7;
    > std::cout << "Flew to " << X << "\n";
    > }
    >
    > void Swim(void)
    > {
    > X += 3;
    > std::cout << "Swam to " << X << "\n";
    > }
    >
    > void Float(void)
    > {
    > X += 2;
    > std::cout << "Floated to " << X << "\n";
    > }
    >
    > void Combat(void)
    > {
    > X += 2;
    > std::cout << "Combating around " << X << "\n";
    > }
    >
    > void Guard(void)
    > {
    > X += 2;
    > std::cout << "Gruarding around " << X << "\n";
    > }
    >
    > public:
    >
    >
    > Base( int X, const ACTIONTYPE Action1 = WALK ): X(X),
    > Action( Action1 )
    > {
    > std::cout << "Action initialized to " << Action << "\n";
    > }
    >
    > //void Move() { if ( Move_ != NULL ) { Move_( *this ); } }
    > void doAction(void)
    > {
    > switch(Action) {
    > case WALK:
    > Walk(); break;
    > case FLY:
    > Fly(); break;
    > case SWIM:
    > Swim(); break;
    > case FLOAT:
    > Float(); break;
    > case COMBAT:
    > Combat(); break;
    > case GUARD:
    > Guard(); break;
    > }
    > }
    >
    > void changeAction(const ACTIONTYPE Action1)
    > {
    > Action = Action1;
    > std::cout << "Action changed to " << Action << "\n";
    > }
    >
    >
    >
    > private:
    > //void (*Move_)( Base& );
    > ACTIONTYPE Action;
    > int X;
    > };
    >
    >
    > int main()
    > {
    > Base MyBase( 10, WALK);
    >
    > MyBase.doAction();
    > MyBase.changeAction(FLY);
    > MyBase.doAction();
    > MyBase.changeAction(SWIM);
    > MyBase.doAction();
    >
    > std::string wait;
    > std::getline( std::cin, wait );
    > }
    >
    Gianni Mariani, Apr 12, 2007
    #11
  12. Jim Langston

    patelvijayp Guest

    Hello,

    Here is simple way to do that.

    #include <iostream>
    #include <string>

    enum ACTIONTYPE {WALK, FLY, SWIM, FLOAT, COMBAT, GUARD};

    class Base
    {
    void Walk(void)
    {
    X += 5;
    std::cout << "Walked to " << X << "\n";
    }

    void Fly(void)
    {
    X += 7;
    std::cout << "Flew to " << X << "\n";
    }

    void Swim(void)
    {
    X += 3;
    std::cout << "Swam to " << X << "\n";
    }

    void Float(void)
    {
    X += 2;
    std::cout << "Floated to " << X << "\n";
    }

    void Combat(void)
    {
    X += 2;
    std::cout << "Combating around " << X << "\n";
    }

    void Guard(void)
    {
    X += 2;
    std::cout << "Gruarding around " << X << "\n";
    }

    public:


    Base( int X, const ACTIONTYPE Action1 = WALK ): X(X),
    Action( Action1 )
    {
    std::cout << "Action initialized to " << Action << "\n";
    }

    //void Move() { if ( Move_ != NULL ) { Move_( *this ); } }
    void doAction(void)
    {
    switch(Action) {
    case WALK:
    Walk(); break;
    case FLY:
    Fly(); break;
    case SWIM:
    Swim(); break;
    case FLOAT:
    Float(); break;
    case COMBAT:
    Combat(); break;
    case GUARD:
    Guard(); break;
    }
    }

    void changeAction(const ACTIONTYPE Action1)
    {
    Action = Action1;
    std::cout << "Action changed to " << Action << "\n";
    }



    private:
    //void (*Move_)( Base& );
    ACTIONTYPE Action;
    int X;
    };


    int main()
    {
    Base MyBase( 10, WALK);

    MyBase.doAction();
    MyBase.changeAction(FLY);
    MyBase.doAction();
    MyBase.changeAction(SWIM);
    MyBase.doAction();

    std::string wait;
    std::getline( std::cin, wait );
    }
    patelvijayp, Apr 14, 2007
    #12
  13. Jim Langston

    patelvijayp Guest

    Oops,

    My 2nd post is unintentional. I realized later that its already
    present as No.7.

    Sorry.
    patelvijayp, Apr 14, 2007
    #13
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Riken

    OOPS concepts

    Riken, Jul 15, 2003, in forum: ASP .Net
    Replies:
    3
    Views:
    17,278
    bmundy
    Aug 6, 2003
  2. Anandraj

    OOPS in .NET

    Anandraj, Aug 4, 2003, in forum: ASP .Net
    Replies:
    4
    Views:
    18,008
    Terry
    Aug 29, 2003
  3. =?Utf-8?B?ZGhucml2ZXJzaWRl?=

    Oops.. how can I rebuild a RESX file?

    =?Utf-8?B?ZGhucml2ZXJzaWRl?=, Feb 2, 2005, in forum: ASP .Net
    Replies:
    6
    Views:
    8,073
    =?Utf-8?B?ZGhucml2ZXJzaWRl?=
    Feb 2, 2005
  4. Replies:
    2
    Views:
    398
    Joerg Jooss
    Aug 21, 2005
  5. oaksong

    oops...changed the SA password

    oaksong, Nov 2, 2005, in forum: ASP .Net
    Replies:
    8
    Views:
    566
    oaksong
    Nov 3, 2005
Loading...

Share This Page