Это хорошее использование шаблона состояния для поддержания текущего выбранного объекта?

Типичный сценарий паттерна состояний включает в себя состояния, которые в основном разные, такие как closed_connection_state или open_connection_state. В моем случае все состояния по существу одинаковы, но операция должна применяться к текущему выбранному объекту.

Обычно что-то подобное делается с использованием индексной переменной, которая указывает на текущий выбранный объект, но использует шаблон состояния. Лучшая реализация для этого, как в примере ниже?

class Object
{
    std::string _name;
public:
    Object(std::string name) : _name(name)
    {

    }

    void Perform()
    {
        std::cout << _name << " Perform called\r\n";
    }
};

class CurrentObject
{
    Object* _a;
    Object* _b;
    Object* _current;

public:

    CurrentObject(Object* a, Object* b) : _a(a), _b(b)
    {
        _current = a;
    }

    void Perform()
    {
        _current->Perform();

        // Update _current logic goes here, lets just switch
        // the state whenever `Perform` is called.
        if (_current == _a)
            _current = _b;
        else
            _current = _a;
    };

};

int main()
{
    Object a("a"); // program can be in a state when `a` is the current object.
    Object b("b"); // or b can become the current object as result of an operation on current object

    CurrentObject current(&a, &b); // it assigns the defaults

    // assume Perform() does its thing but it also needs to change the current selected object.
    // In this example, we assumes the current selection object is always swapped.
    current.Perform(); // operates on `a`, the default
    current.Perform(); // operates on `b` due state changed in above line
    current.Perform(); // operates on `a` doe to state changed in above line again
}
0
0
82
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Это определенно разумный поступок, если ваши состояния умножаются (как это обычно бывает), это может стать немного трудным в обслуживании, но на самом деле это очень хорошая реализация конечного автомата в стиле объектно-ориентированного программирования.

Вероятно, вы хотите, чтобы ваши состояния (a и b) расширяли общее абстрактное состояние, чтобы, когда функциональность одинакова для всех состояний, вам не нужно было реализовывать ее в каждом отдельном объекте.

Для расширения вы, вероятно, также захотите «назвать» свои состояния и поместить их в хэш-таблицу, как только она масштабируется (помните, в программировании у вас есть 1 или несколько), добавление нового состояния не приведет к изменению кода в вашем конечном автомате - но я предполагаю, что у вас уже есть что-то подобное, и вы просто уменьшили масштаб для этого вопроса.

Также обратите внимание, что для переключения состояний, которые вы не хотите делать напрямую (в качестве примера), вам, вероятно, понадобится метод (setState), который изменяет состояние при возврате метода выполнения, а не в самом методе выполнения или во время его работы. Фактически, вы можете выполнить возврат строки, указывающей на следующее желаемое состояние.

Редактировать из комментариев:

Я имел в виду, называя ваши состояния, а не:

class CurrentObject
{
    Object* _a;
    Object* _b;
    Object* _current;
    ...

У вас может быть что-то вроде (извините за мой java-синтаксис, C# не является моим основным языком, но я знаю, что он очень похож по функциональности)

class CurrentObject
{
    Hashtable states=new Hashtable();
    Object* _current;

    public addState(String stateName, Object* state)
    {
        states.put(stateName, state)
    }

    public void Perform()
    {
        String nextState = _current->Perform();
        if(nextState != null)
            setState(nextState);
    }
    public void setState(String stateName)
    {
        _current = states.get(stateName);
    }

}

Ваш код вызова будет делать что-то вроде:

currentObject = new CurrentObject()
currentObject.addState("state a", _a);
currentObject.addState("state b", _b); 
currentObject.setState("state a");
currentObject.perform();
...

Я игнорирую МНОГОЕ инициализацию и проверку ошибок.

В настоящее время ваш конечный автомат имеет только одно событие: «Perform ()». Вы можете обнаружить, что вам нужны другие события, которые немного усложнят ситуацию (в java я, возможно, использовал бы отражение или аннотации для решения ЭТОЙ проблемы, не знаю, как это сделает C#).

Спасибо, не могли бы вы уточнить, что именно вы имеете в виду, называя состояния и помещая их в хеш-таблицу. Это может быть удобно, поскольку мне не нужно менять код конечного автомата.

zar 13.09.2018 20:58

@zar с двумя состояниями работает с двумя переменными (_a и _b). С десятью состояниями это становится обременительным. Кроме того, при использовании именованных идентификаторов ваш код не так универсален, как мог бы быть. Храните состояния в контейнере, и вы можете легко поддерживать любое количество состояний. Используйте хеш-таблицу, и у вас будет высокоскоростной поиск состояний в контейнере, который не зависит от порядка, в котором состояния добавлены к машине, или от какой-либо другой сложной в управлении глубокой магии. У вас может быть код, похожий на _current = states["OFF"];, легко читаемый и достаточно быстрый.

user4581301 13.09.2018 21:32

Добавлены примеры и другие примечания по реализации в конце исходного сообщения. @ user4581301 (или любой другой, кто знает C# лучше меня), не стесняйтесь редактировать мой javaesque-код на C#, если хотите.

Bill K 13.09.2018 21:40

Немного подозрительно относится к этому, Билл. Задающий вопрос ориентирован на C++, и это немного больше отрывает ваш пример, чем нужно C#. Если вы не возражаете против бойни, я могу это сделать.

user4581301 13.09.2018 22:05

@ user4581301 Ух, я думал, что видел C#, а не C++. Вы правы, концепции все равно должны работать, но реализация будет совсем другой. В прошлый раз, когда я использовал C++, я не думаю, что у него даже была встроенная реализация Hashmap :) (по крайней мере, ее не было в stdlib!)

Bill K 13.09.2018 22:09

Несвязанный: std::unordered_map. Появился в C++ 11. До этого у нас был std::map, который обычно был красно-черным деревом. Не уверен, что на самом деле было бы лучше использовать здесь, поскольку std::string не кэширует хеш-ключ.

user4581301 13.09.2018 22:17

Спасибо вам обоим @ user4581301, это было очень полезно. У меня есть несколько вариантов использования, где у меня есть 2 фиксированных состояния, но переменные состояния в другом месте, поэтому подход хеш-таблицы будет весьма полезным.

zar 14.09.2018 20:10

Другие вопросы по теме