Что делать с параметрами времени выполнения при использовании области действия?

Предупреждение, впереди длинный пост.

Я много думал об этом в последнее время и изо всех сил пытаюсь найти здесь удовлетворительное решение. В примерах я буду использовать C # и autofac.

Проблема

IoC отлично подходит для построения больших деревьев служб без сохранения состояния.Я разрешаю службы и передаю данные только вызовам методов. Здорово.

Иногда мне нужно передать параметр данных в конструктор службы. Вот для чего нужны фабрики. Вместо разрешения службы я разрешаю ее фабрику и вызываю метод create с параметром, чтобы получить свою службу. Еще немного работы, но все в порядке.

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

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

Решения

1. Метод инициализации

Вместо передачи данных в конструктор просто передайте их методу Initialize .

Интерфейс:

interface IMyService
{
    void Initialize(Data data);
    void DoStuff();
}

Класс:

class MyService : IMyService
{
    private Data mData;
    public void Initialize(Data data)
    {
        mData = data;
    }

    public void DoStuff()
    {
        //...
    }
}

Регистрация:

builder.RegisterType<MyService>().As<IMyService>().InstancePerLifetimeScope();

Использование:

var myService = context.Resolve<IMyService>();
myService.Init(data);

// somewhere else
var myService = context.Resolve<IMyService>();

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

2. Шаблон держателя

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

Интерфейс:

interface IMyService
{
    void DoStuff();
}

Класс:

class MyService : IMyService
{
    private Data mData;
    public MyService(IDataHolder dataHolder)
    {
        mData = dataHolder.Data;
    }

    public void DoStuff()
    {
        //...
    }
}

Регистрация:

builder.RegisterType<MyService>().As<IMyService>();
builder.RegisterType<DataHolder>().As<IDataHolder>().InstancePerLifetimeScope();

Использование:

var holder = context.Resolve<IDataHolder>();
holder.Data = data;

// somewhere else
var myService = context.Resolve<IMyService>();

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

3. Пусть контейнер содержит экземпляр

Интерфейс:

interface IMyService
{
    void DoStuff();
}

Класс:

class MyService : IMyService
{
    private Data mData;
    public MyService(Data data)
    {
        mData = dataHolder.Data;
    }

    public void DoStuff()
    {
        //...
    }
}

Регистрация:

builder.RegisterType<MyService>().As<IMyService>().InstancePerLifetimeScope();

Использование:

var myServiceFactory = context.Resolve<Func<Data, IMyService>>();
myServiceFactory(data);

// somewhere else
var myService = context.Resolve<IMyService>();

Верно. Я нигде не храню результат заводского вызова, потому что autofac хранит его для меня. Это довольно удивительно для любого, кто прочитает код. Я не уверен, что autofac вообще предназначался для такого использования. Приятно то, что мне не нужен ни дополнительный метод инициализации, ни дополнительный класс для хранения экземпляра.

Вопрос

Что вы думаете об этом? Как вы справляетесь с ситуацией с параметрами данных времени выполнения и областью действия? Мне не хватает лучшего подхода?

31
задан Nathan Tuggy 21 May 2015 в 00:52
поделиться