prestokeys
prestokeys

Reputation: 4849

State Pattern when derived classes have extra states

The language I'm using is C++, but the problem is language independent. Let's say we have

class State {};

enum StateTypes {MOVEMENT, AFFECTED_BY_SPELL, ..., ANGER, N,
    CARRYING_WEAPON, CASTING_SPELL};

class Base {
    array<stack<State*>, N> states;  // MOVEMENT, AFFECTED_BY_SPELL, ..., ANGER
};

class Derived : public Base {
//    array<stack<State*>, 2> states;  // ??? CARRYING_WEAPON, CASTING_SPELL
};

and Derived has more possible states than Base does. I need all the states stacks in an array so that I can iterate through them. But Derived has all the possible states that Base has plus some that Base does not, as indicated above--Derived shall have states involving carrying a weapon or casting a spell, which do not apply to base. So naturally, for Derived I want an array of states stacks of length N+2 (in this example), but at the same time, I want Derived to inherit the 'states' data member in Base and use that. How do I design this so that when Base iterates though all of its states it iterates from MOVEMENT to ANGER, while Derived iterates from MOVEMENT to CASTING_SPELL? Is the only solution to give Derived a separate array, N+2> states data member and have its Base::states totally ignored? Or let Base have all N+2 states, even though some of those state categories make no sense in Base (only Derived can carry a weapon or cast a spell)? There has to be a better way.

This question is actually a more generic one: how can a derived class have a superset container data member of a base class container data member with all operations on both containers being carried out in the same way, apart from moving the superset onto Base, or having Derived's superset used exclusively while ignoring Base's container data member completely? Is there a special pattern to handle this?

Something like create a container data member for Derived that has only the elements not present in Base's container data member and then have operations go through Bases' container and then continue into Derived's container somehow???

class State {};

enum StateTypes {MOVEMENT, AFFECTED_BY_SPELL, ..., ANGER, N,
    CARRYING_WEAPON, CASTING_SPELL};

struct Base {
    array<stack<State*>, N> states;  // MOVEMENT, AFFECTED_BY_SPELL, ..., ANGER
    virtual void foo() {for (State* x : states) {foo_helper();} }
    void foo_helper();
    virtual const array<stack<State*>, N>& getStates() const {return states;}
};

struct Derived : public Base {
    array<stack<State*>, 2> states;  // CARRYING_WEAPON, CASTING_SPELL
    virtual void foo() override {
        const array<stack<State*>, N+2> allStates = Base::states + states merged;
        for (State* x : allStates) {foo_helper();}  // Good idea?
    }
    virtual const array<stack<State*>, N+2>& getStates() const override {
        return Base::states + states merged;  // won't work because of different return type
    }
};

Same problem if Derived has its own derived classes with their own unique states, and so forth...

Upvotes: 0

Views: 90

Answers (1)

Basilevs
Basilevs

Reputation: 23896

You fail to encapsulate state management of Base class and its descendants. Consider never giving out direct reference to states container and manage states internally.

After all, users of the class will only need to know only last state of every stack (if I get your idea).

To hold them, use vector:

class Base {
    vector<stack<State*>> states;  // MOVEMENT, AFFECTED_BY_SPELL, ..., ANGER
    public:
    Base() {
       states.reserve(N);
       states.push_back(....)
    }
    vector<State*> getStates() const {
      vector<State*> rv;
      rv.reserve(N);
      for(stack<State*> & stack: states) {
        rv.push_back(stack.top());
      }
      return rv;
    }
};

struct Derived : public Base {
    Derived() {
       states.reserve(N+2);
       states.push_back(....)
    }
}

Upvotes: 1

Related Questions