C #: как решить эту круговую зависимость? - PullRequest
3 голосов
/ 19 апреля 2010

У меня круговая зависимость в моем коде, и я не уверен, как ее решить.

Я занимаюсь разработкой игры. У NPC есть три компонента, отвечающие за мышление, восприятие и действия. Этим компонентам необходим доступ к контроллеру NPC, чтобы получить доступ к его модели, но контроллеру нужны эти компоненты, чтобы что-либо делать. Таким образом, оба принимают друг друга в качестве аргументов в своих конструкторах.

ISenseNPC sense = new DefaultSenseNPC(controller, worldQueryEngine);
IThinkNPC think = new DefaultThinkNPC(sense);
IActNPC act = new DefaultActNPC(combatEngine, sense, controller);
controller = new ControllerNPC(act, think);

(В приведенном выше примере параметр немного упрощен.)

Без act и think, controller ничего не может сделать, поэтому я не хочу, чтобы его инициализировали без них. Обратное в основном также верно. Что мне делать?

ControllerNPC с использованием think и act для обновления своего состояния в мире:

public class ControllerNPC {
   // ...
           public override void Update(long tick)
        {
            // ...
            act.UpdateFromBehavior(CurrentBehavior, tick);

            CurrentBehavior = think.TransitionState(CurrentBehavior, tick);
        }
   // ...

}

DefaultSenseNPC с использованием controller, чтобы определить, сталкивается ли он с чем-либо:

 public class DefaultSenseNPC {
       // ...
            public bool IsCollidingWithTarget()
            {
                return worldQuery.IsColliding(controller, model.Target);
            }
       // ...
    }

Ответы [ 4 ]

4 голосов
/ 19 апреля 2010

Отделите модель контроллера от конкретной службы контроллера с помощью интерфейса.

Речь идет о ссылках на проекты в доменном дизайне, я написал небольшой блог об этой проблеме немного раньше:

http://www.mellekoning.nl/index.php/2010/03/11/project-references-in-ddd/

0 голосов
/ 25 января 2011

Насколько я понимаю, первое и главное: Контролер не должен знать о мышлении, ощущении, действии ...

Я вижу, у вас есть что-то вроде метода «Обновить» для контроллера, и (я полагаю) контроллер должен что-то делать в зависимости от текущего «мышления», «восприятия», «действия».

Для этого случая я бы добавил еще 3 компонента на уровне модели: «ThinkModel», «ActModel», «SenseModel». Они должны представлять состояние соответствующего процесса и ничего не знать о другом мире.

Ваш контроллер должен получать эту информацию от компонентов (мышление, поведение, восприятие) такими методами, как «DoAction», «ThinkingAbout», «FeelingSomething», и хранить ее внутри.

В то же время он должен иметь набор событий, таких как «ActionOccured», «ThinkingOccured», «SenseingOccured» (последний может быть сформулирован как «FeeledSomething»). Эти события должны быть:

  • срабатывает в случае изменения какого-либо состояния;
  • предоставить соответствующую объектную модель;
  • должны прослушиваться компонентами.

В результате у вас будет контроллер, который будет знать только о моделях, и каждый компонент будет ссылаться на все модели И контроллер. Компоненты должны ничего не знать друг о друге. Контроллер ничего не должен знать о компонентах. И вы сможете создать свой объект следующим образом:

IThinkModel modelThinkg = new ThinkModel();
IActModel modelAct = new ActModel();
ISenseModel modelSense = new SenseModel();

IController controller = new Controller(modelThinkg, modelAct, modelSense);

ISenseNPC sense = new DefaultSenseNPC(controller);
IThinkNPC think = new DefaultThinkNPC(sense);
IActNPC act = new DefaultActNPC(combatEngine, sense, controller);

Конструктор каждого компонента может выглядеть так:

class DefaultSenseNPC
{
    DefaultSenseNPC(IController controller)
    {
        _controller = controller;
        _contoller.ThinkingAbout += ContollerReceivedNewThinking;
    }

    private ContollerReceivedNewThinking(IModelThinking modelNewThink)
    {
        _modelNewThink = modelNewThink;// store it for further calculations.
    }
}

Надеюсь, это поможет.

P.S. В некотором смысле предлагаемая «архитектура» похожа на шаблон MVP, используемый в приложениях с пользовательским интерфейсом.

0 голосов
/ 19 апреля 2010

Можно ли использовать события для связи между объектами?

0 голосов
/ 19 апреля 2010

Используйте двухфазное построение, при котором объекты создаются с null ссылками на связанные с ними объекты, а затем вы вызываете set методы для установки ссылок:

ISenseNPC sense = new DefaultSenseNPC(worldQueryEngine);
IThinkNPC think = new DefaultThinkNPC();
IActNPC act = new DefaultActNPC(combatEngine);
controller = new ControllerNPC();

sense.setController(controller);
think.setSense(sense);
act.setSense(sense);
act.setController(controller);
controller.setAct(act);
controller.setThink(think);

// And now the objects are ready to use.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...