LINUX.ORG.RU

Доступ к Context в State pattern

 


0

1

Пытаюсь применить State pattern, но мне также нужно, чтобы из ConcreteStateA и ConcreteStateB я мог вызывать методы объекта Context, причём нужно сделать так, чтобы эти методы можно было вызывать только из наследников State, т.е. просто сделать эти методы public нельзя.

Получается, если я сделаю нужные методы класса Context приватными, то придётся всех наследников класса State записать во friend'ы класса Context. Это неподходящее решение, поскольку я не смогу создавать новые состояния без модификации класса Context.

Для обхода этой проблемы я создал класс Accessor. Класс Accessor — это friend класса Context, по сути класс Accessor предоставляет интерфейс к нужным приватным методам класса Context, при этом объекты состояний имеют ссылку на Accessor, таким образом, могут вызывать нужные приватные методы класса Context. Конструктор класса Accessor приватный, чтобы кто угодно не мог создать его и получить доступ к приватным методам класса Context.

Вот набросал примерный код этого всего:

#include <iostream>

class Accessor;

class State
{
public:
        State(Accessor *accessor) : m_accessor(accessor) {}
        virtual ~State() {}

        virtual void event1() = 0;
        virtual void event2() = 0;

protected:
        Accessor *accessor() const { return m_accessor; }

private:
        Accessor *m_accessor;
};

class ConcreteStateA : public State
{
public:
        ConcreteStateA(Accessor *accessor) : State(accessor) {}

        virtual void event1() override;
        virtual void event2() override;
};

class ConcreteStateB : public State
{
public:
        ConcreteStateB(Accessor *accessor) : State(accessor) {}

        virtual void event1() override;
        virtual void event2() override;
};

class Context
{
public:
        Context();
        ~Context();

        void event1() { m_state->event1(); }
        void event2() { m_state->event2(); }

private:
        Accessor *m_accessor;
        State *m_state;

        void setState(State *state) { delete m_state; m_state = state; }
        void action1();
        void action2();

        friend class Accessor;
};

class Accessor
{
private:
        Accessor(Context *context) : m_context(context) {}

public:
        void setState(State *state) { m_context->setState(state); }
        void action1() { m_context->action1(); }
        void action2() { m_context->action2(); }

private:
        Context *m_context;

        friend class Context;
};

Context::Context() :
        m_accessor(new Accessor(this)),
        m_state(new ConcreteStateA(m_accessor))
{}

Context::~Context()
{
        delete m_state;
        delete m_accessor;
}

void Context::action1()
{
        std::cout << "Context::action1()" << std::endl;
}

void Context::action2()
{
        std::cout << "Context::action2()" << std::endl;
}

void ConcreteStateA::event1()
{
        std::cout << "ConcreteStateA::event1()" << std::endl;
        accessor()->action1();
}

void ConcreteStateA::event2()
{
        std::cout << "ConcreteStateA::event2()" << std::endl;
        accessor()->action2();
        accessor()->setState(new ConcreteStateB(accessor()));
}

void ConcreteStateB::event1()
{
        std::cout << "ConcreteStateB::event1()" << std::endl;
        accessor()->action1();
        accessor()->setState(new ConcreteStateA(accessor()));
}

void ConcreteStateB::event2()
{
        std::cout << "ConcreteStateB::event2()" << std::endl;
        accessor()->action2();
}

int main()
{
        Context context;
        context.event1();
        context.event2();
        context.event2();
        context.event1();
        context.event1();
        return 0;
}

Если посмотреть на код, вся суть должна быть понятна. Вопрос такой: насколько хорошо такое решение? Может, есть решение лучше? Может быть, есть какие-то недостатки, которые я пока что не заметил? Может, это вообще какой-то известный антипаттерн? :D

Это что за Enteprise Level Design Pattern?

тобы эти методы можно было вызывать только из наследников State

У тебя State абстрактный, как ты из него можешь вызвать хоть что-либо?

Rubbiroid ()
Ответ на: комментарий от Rubbiroid

из наследников State

У тебя State абстрактный, как ты из него можешь вызвать хоть что-либо?

1. Не из State, а из наследников State.

2. Ничего не мешает мне вызывать что-то из абстрактного класса, вот только в моём коде такого нет.

gentoo_root ★★★★★ ()

Это ты изобрёл Dependency Injection и это самый эффективный путь в ситуации. Всё правильно сделал.

E ★★★ ()
Ответ на: комментарий от E

Хм, почитал на википедии про Dependency Injection, если я правильно понял, то это немножко другой паттерн, чем у меня.

У меня делается упор на то, что есть класс Accessor, с помощью которого объект класса Context предоставляет некоторой группе объектов (наследникам State) доступ к некоторым своим приватным методам. То есть я в объектах состояний храню ссылку не на сам Context, а на промежуточный Accessor. Насколько хорошо/плохо так делать?

gentoo_root ★★★★★ ()
Ответ на: комментарий от gentoo_root

Насколько хорошо/плохо так делать?

Хранить ссылку? Не очень хорошо, лишняя связь. Проще будет передавать этот Accessor в методы event*(), а-ля Visitor.

Вообще дизайн мутный. Context должен только управлять состояниями, если там есть дополнительный функционал - его лучше вынести в третий класс, создать его экземпляр приватно в Context и передавать этот объект в создаваемые/вызываемые State'ы. По сути, тот же Accessor, только роль меняется и теперь он полноценный Provider, никому не видимый, кроме этой компании. Так будут более разделены обязанности, отлаживать/тестировать проще.

E ★★★ ()
Ответ на: комментарий от E

если там есть дополнительный функционал - его лучше вынести в третий класс, создать его экземпляр приватно в Context и передавать этот объект в создаваемые/вызываемые State'ы.

Точно, вот это уже идея получше, чем у меня. Спасибо, так и сделаю.

gentoo_root ★★★★★ ()
Ответ на: комментарий от gentoo_root

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

А чтобы не вызывать Context из других мест, то можно этого просто не делать, не?

Rubbiroid ()
Ответ на: комментарий от Rubbiroid

А чтобы не вызывать Context из других мест, то можно этого просто не делать, не?

Я хотел ограничить доступ к некоторым методам класса Context, чтобы их могли вызывать потомки State, но не мог никто другой.

Вообще мне кажется, что в ООП зачастую не хватает более продвинутой системы разграничения прав доступа, чем public/protected/private. Может, мне хочется странного, но когда начинаются какие-то связи между объектами, мне часто хочется, чтобы другим классам были доступны лишь некоторые методы моего класса, причём разным классам разные.

gentoo_root ★★★★★ ()
Ответ на: комментарий от gentoo_root

Так раздай разным классам разные интерфейсы. Понятное дело, с ACL это будет иметь мало общего, но лучше так, чем никак.

one_more_hokum ★★★ ()
Ответ на: комментарий от one_more_hokum

Так раздай разным классам разные интерфейсы.

Я думал о таком, но я ведь смогу скастовать указатель на интерфейс к указателю на настоящий объект и получить полный доступ. Хотя не знаю, стоит ли об этом так беспокоиться...

gentoo_root ★★★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.