Unity: условное разрешение

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

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

// In Bootstrapper class
resolver.RegisterType<IDataType1, DataType1>();
resolver.RegisterType<IDataType2, DataType2>();
resolver.RegisterType<IDataType3, DataType3>();


// Main flow...outwith bootstrapper
switch (dataRecordType)
{
    case DataRecordType.dataType1:
        DataType1 dt1 = new DataType1();
        dt1.ProcessData();
        break;

    case DataRecordType.dataType2:
        DataType2 dt2 = new DataType2();
        dt2.ProcessData();
        break;

    case DataRecordType.dataType3:
        DataType3 dt3 = new DataType3();
        dt3.ProcessData();
        break;

    default:
        break;
}

person Chris    schedule 28.10.2014    source источник


Ответы (1)


Здесь вам не хватает нескольких абстракций. Вам не хватает общей абстракции над вашими типами данных и абстракции для создания реализаций этих типов данных:

// In your core layer
public interface IDataType {
    void ProcessData();
}

public interface IDataTypeFactory {
    IDataType Create(DataRecordType dataRecordType);
}

// In Bootstrapper class
resolver.RegisterInstance<IDataTypeFactory>(new DataTypeFactory(resolver));
resolver.RegisterType<IDataType1, DataType1>();
resolver.RegisterType<IDataType2, DataType2>();
resolver.RegisterType<IDataType3, DataType3>();

private sealed class DataTypeFactory : IDataTypeFactory {
    private readonly IUnityContainer container;
    public DataTypeFactory(IUnityContainer container) {
        this.container = container;
    }

    public IDataType Create(DataRecordType dataRecordType) {
        switch (dataRecordType) {
            case DataRecordType.dataType1:
                return this.container.Resolve<IDataType1>();
            case DataRecordType.dataType2:
                return this.container.Resolve<IDataType2>();
            case DataRecordType.dataType3:
                return this.container.Resolve<IDataType3>();
            default:
                throw new InvalidEnumArgumentException();
        }
    }
}

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

// Main flow...outwith bootstrapper
IDataType dt = this.dataTypeFactory.Create(dataRecordType);
dt.ProcessData();

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

// In Bootstrapper class
resolver.RegisterInstance<IDataTypeFactory>(new DataTypeFactory(resolver));
resolver.RegisterType<DataType1>();
resolver.RegisterType<DataType2>();
resolver.RegisterType<DataType3>();

private sealed class DataTypeFactory : IDataTypeFactory {
    private readonly IUnityContainer container;
    public DataTypeFactory(IUnityContainer container) {
        this.container = container;
    }

    public IDataType Create(DataRecordType dataRecordType) {
        switch (dataRecordType) {
            case DataRecordType.dataType1:
                return this.container.Resolve<DataType1>();
            case DataRecordType.dataType2:
                return this.container.Resolve<DataType2>();
            case DataRecordType.dataType3:
                return this.container.Resolve<DataType3>();
            default:
                throw new InvalidEnumArgumentException();
        }
    }
}
person Steven    schedule 28.10.2014
comment
+1 Стоит отметить, что контейнер IOC не должен проскальзывать из корня композиции (поскольку ОП кажется новичком). Также есть ли способ создать фабрику, не связывая код с container.Resolve? - person Sriram Sakthivel; 28.10.2014
comment
@SriramSakthivel: Спасибо, что заметили. Я согласен, на контейнер нельзя ссылаться нигде, кроме корня композиции. Можно отделить фабрику от контейнера, но поскольку эта реализация фабрики может быть частью корня композиции, нет проблем, что фабрика ссылается на сам контейнер. Но другой вариант — внедрить классы DataTypeX в фабрику вместо контейнера. - person Steven; 28.10.2014
comment
Я полагаю, вы имеете в виду ввести Func<DataTypeX> в Factory? в противном случае нам нужно несколько экземпляров фабрики для получения нового экземпляра DataTypeX, не так ли? - person Sriram Sakthivel; 28.10.2014
comment
@SriramSakthivel: Нет. Я имею в виду наличие фабрики со следующим конструктором: DataTypeFactory(DataType1 dt1, DataType2 dt2, DataType3 dt3). - person Steven; 28.10.2014
comment
Да, но тогда ваша фабрика всегда будет возвращать один и тот же экземпляр. Это меняет поведение, я прав? Чтобы добиться текущего поведения (переходного), нам нужно DataTypeFactory(Func<DataType1> dt1, Func<DataType2> dt2, ...) Поправьте меня, если я ошибаюсь. Я пользователь autofac, время жизни по умолчанию - это экземпляр для каждой зависимости, поэтому я задаю этот вопрос :) - person Sriram Sakthivel; 28.10.2014
comment
@SriramSakthivel: Нет, в этом случае фабрику можно просто зарегистрировать как временную, и в этом случае она будет работать. К сожалению, Unity не предупредит вас, если вы случайно зарегистрируете фабрику как singleton, как это делают некоторые другие контейнеры. - person Steven; 28.10.2014