0

Suppose, that I have an abstract base State class and at least two derived classes AnimalState and PlantState(also abstract). Also, I have many derived classes from AnimalState and PlantState.

class State{} // abstract
class AnimalState: public State{} // abstract
class PlantState: public State{} // abstract
//maybe few more of such classes here

class AnimalStateSpecific1: public AnimalState{}
class AnimalStateSpecific2: public AnimalState{}
... //many of them
class PlantStateSpecific1: public PlantState{}
class PlantStateSpecific2: public PlantState{}
... //many of them

Now suppose, that I use them in some kind of method that operates on base State pointers. Those pointers are replaced over time with other pointers to different class from the State hierarchy. It happens by some rule, specifically within the predefined state graph.

Now to the question part. In order to determine the next state, I need to know the previous one. But since I have only base State pointers, I can not efficiently tell what type of state I have, without doing dynamic_cast to every derived class in the hierarchy that is not good. I can have some enum with all kinds of states that I have, but I do not really like that because I do not want to mix information from two hierarchy branches, as it is really different. Also, I do not like different enums for every branch in the hierarchy such as AnimalStateEnum, PlantStateEnum etc.

What is the best solution for this problem? Maybe my design is not good from the start? I want to keep it as generic as possible and work only with base class objects, if possible.

OrenIshShalom
  • 5,974
  • 9
  • 37
  • 87
Mykola Niemtsov
  • 540
  • 1
  • 7
  • 23
  • 3
    If you really need to know the actual type of `State*` you probably miss something in your interface there. – πάντα ῥεῖ Jul 27 '18 at 16:12
  • 4
    You could use [double-dispatch](https://stackoverflow.com/a/12582114/1460794) or generate an index for the type with [std::type_index](https://en.cppreference.com/w/cpp/types/type_index). – wally Jul 27 '18 at 16:12
  • 1
    You might be interested in the design models I've been using for [STTCL](https://github.com/makulik/sttcl), you can also use that as a framework to model your state machine from a Harel (UML) state diagram. – πάντα ῥεῖ Jul 27 '18 at 16:15

3 Answers3

2

Now to the question part. In order to determine the next state, I need to know the previous one.

Simplest solution based on limited information we have - object, which knows it's own state creates next state object:

class State{
public:
    ...
    virtual std::unique_ptr<State> transform( some data ) = 0;
};

then you implement it in each derived from State class which can change it's state and knows where it can move to. What data you need to pass is not a simple question - it depends on your task and may have various options, but you need to define something that can be used by all derived classes, as signature is defined on the base class and shared on all derived ones.

What is the best solution for this problem? Maybe my design is not good from the start?

This question is not trivial and only can be answered having pretty deep knowledge on your task. If you are unsure - implement a prototype and check if solution fits your problem well. Unfortunately the only way to learn how to create a good design is your own experience (except trivial cases of course).

Slava
  • 43,454
  • 1
  • 47
  • 90
1

You could simply have a virtual method next() inside the state class hierarchy, and then do something similar to the following example:

State *globalState = nullptr;
void foo(State *s)
{
    globalState = s->next();
}

Where each derived class will implement next() to its own meaning:

PlantStateSpecific1 *AnimalStateSpecific1::next(){ return new PlantStateSpecific1; }
AnimalStateSpecific1 *PlantStateSpecific1::next(){ return new AnimalStateSpecific1; }

This is more OOP than having an enum / integer descriptor of the derived class.

OrenIshShalom
  • 5,974
  • 9
  • 37
  • 87
0

What you can have is an integer inside the base state class that every class below it will set in its constructor. Then you can either use a sereis of constants, a list of possible states with the id corresponding to the state type index, or use an enumerator. The id is more flexible as you can create state types with relative ease and add handling to them without too much difficulty, aswell as if you want to create a new state from the id type.

Just one of the ways iv done this before, but there are probably many others.