Какова возможная альтернатива этим статическим переменным? - PullRequest
4 голосов
/ 23 января 2011

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

class MyGame
{
    class InputEngine
    {
        internal void DoInput()
        {
            if (Keys["F1"].IsPressed)
            {
                // Create a window using the gui engine
            }
        }
    }
    class GuiEngine
    {
        internal void Update() { }
        internal void Draw() { }
    }
    private GuiEngine engine;
    private InputEngine input;

    internal MyGame()
    {
        this.input = new InputEngine();
        this.engine = new GuiEngine();
    }

    internal void Update()
    {
        this.engine.Update();
        this.input.DoInput();
    }
    internal void Draw()
    {
        this.engine.Draw();
    }
}

Как я могу получить доступ к экземпляру движка графического интерфейса не только из входного примера, но и из десятков других мест, не делая его статичным.(Я действительно предпочел бы не передавать его в качестве параметра).

Ответы [ 9 ]

2 голосов
/ 23 января 2011

Решением вашей проблемы может быть редизайн.Что-то не так, когда вам нужно

получить доступ к экземпляру движка графического интерфейса не только из примера ввода, но и из десятков других мест

Это указывает на множество зависимостей, которыеможет быть просто неправильно.

2 голосов
/ 23 января 2011

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

Для Ninject синтаксис:

var kernel = new Ninject.StandardKernel(settings);
kernel.Bind<ISomeInterface>().To<MyConcreteObject>().InSingletonScope();

Для Unity синтаксис:

UnityContainer container = new UnityContainer();
container.RegisterType<ISomeInterface, MyConcreteObject>(new ContainerControlledLifetimeManager());

Так что ваши занятия могут быть примерно такими:

public class MyThing
{
    ISomeInterface _mySingletonObject;

    public MyThing(ISomeInterface mySingletonObject)
    {
        _mySingletonObject = mySingletonObject;
    }
}

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

Опять же, для Ninject:

var singletopnObject = kernel.Get<ISomeInterface>();

И единство (думаю по памяти)

var singletopnObject = container.Resolve<ISomeInterface>();

И все другие контейнеры IoC предлагают одни и те же функции по-разному.

P.S. Статика не зло. Они быстрые и очень полезные при правильном использовании.

1 голос
/ 23 января 2011

У вас есть только три реальных варианта прохождения -

  • Статика
  • Параметризация (cctor, метод и т. Д.)
  • Делегаты / Замыкания / Lambda

Такие методы, как инвертирование контейнеров управления, также могут помочь в создании, управлении жизненным циклом и обнаружении экземпляров.При добавлении зависимостей можно полностью автоматизировать весь жизненный цикл с минимальной атрибуцией / кодированием.

Из трех методов: статика и параметризация успешно проходят «тест» на обслуживание программного обеспечения с точки зрения очевидности и простоты.IoC довольно читабелен.Тем не менее, кодовые базы DI обычно больше похожи на черную магию с неявным поведением для времени жизни, местоположения и привязки.

Делегаты, замыкания и т. Д. Могут работать очень хорошо, с сохранением контекста при создании, однако отладка можетнемного кошмара, и часто поддержание / чтение кода закрытия кажется беспорядочным, так как он редко выполняется в контексте того, где он написан.

Статики, как вы указываете, вероятно, наиболее грубые, их труднее высмеивать, заменять и т. д...

1 голос
/ 23 января 2011

Возможно, вы могли бы реализовать шаблон синглтона в своем типе игрового движка?Если вы никогда не создаете более 1 экземпляра этого объекта, я думаю, что это идеальный выбор.

В противном случае вы можете попробовать использовать решение IoC , такое как Unity от Mincrosoft.

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

Теперь первое, что статика - это зло, ИМХО не то, что статическая функция, одиночные классы сами по себе являются злом. Но если бы вы хотели создать несколько автоматических тестов для классов, которые используют ваш статический контекст, это было бы практически невозможно.

Так что если вы хотите полностью игнорировать любые надежды на создание автоматических тестов, тогда сделайте класс статичным со статическими данными.

Однако, если вы хотите оставить эту опцию открытой, я бы посоветовал вам взглянуть на такие вещи, как контейнеры IOC ( структурная карта моя любимая) У вас может быть (ahem) статический контейнер IOC, который вы используете для создания экземпляров ваших зависимостей. Зависимости будут принимать экземпляр, например, GuiEngine, как аргумент конструктора. И ваш контейнер IOC будет гарантировать, что все зависимости получат один и тот же экземпляр, при условии, что вы настроили его правильно.

Как указывает Тим, было бы неплохо извлечь интерфейс для классов вашего движка, поскольку он позволит вам создавать альтернативные реализации для указанных тестов, например, макеты, заглушки, шпионы, фиктивные объекты и т. д.

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

Вы можете передать его как параметр конструктора, а не как параметр метода, например:

public class InputEngine
{
    public InputEngine(IGuiEngine guiEngine)
    {
         // set private member to store guiEngine
    }
}

Примечание. Я также изменил GuiEngine на интерфейс - таким образом вы можете использовать контейнер внедрения зависимостейнапример, StructureMap для автоматической передачи движка графического интерфейса вашим экземплярам по мере необходимости.Помогает и в тестировании.

В качестве альтернативы, вы можете использовать DI-контейнер или фабричный класс для предоставления одноэлементного экземпляра GUI Engine.

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

Техника называется инъекцией зависимостей.По сути, вы «внедряете» «статическую переменную» в экземпляр.Что касается экземпляра, то это обычная переменная экземпляра.Вы просто передаете свой экземпляр графического интерфейса в конструктор.

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

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

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

Передайте экземпляр Game в конструктор других классов и сохраните его в локальной переменной.Таким образом, вы можете получить доступ к Game и всем его членам практически из любого места, но все же можете иметь несколько экземпляров игры.

Вы можете автоматизировать это с помощью Dependency Injection.

В некоторых ситуациях переменные со статическими переменными являются альтернативой, но я не думаю, что ваша - одна из них.

...