Лучший дизайн для постоянно меняющегося свойства в классе Singleton с использованием IoC?

У меня есть следующий вспомогательный класс с открытым свойством _variableHandler. Свойство является общедоступным, так как изначально у меня было видение установки свойства из вызывающего кода, прежде чем использовать методы в классе XAMLHelper, но теперь я сомневаюсь, что это разумный подход. В основном потому, что мне нужно будет вызывать класс довольно часто и не всегда обновлять значение _variableHandler, а это означает, что все станет грязным.

public class XAMLHelper
{
    public IVariableTypeHandler _variableHandler;

    public XAMLHelper()
    {
    }
}

У меня также есть фабрика, которая используется для предоставления желаемого конкретного экземпляра VariableTypeHandler.

Я также использую контейнер IoC (Unity) для предоставления одного экземпляра класса XAMLHelper, как показано ниже.

container.RegisterInstance<XAMLHelper>(new XAMLHelper());

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

container.Resolve(new PropertyInjection(VariableHandlerFactory.GetInstance("Str")));

Я попытался добавить фрагменты в регистрацию контейнера, например

new InjectionProperty()

Но это, похоже, не обновляет свойство _variableHandler после того, как оно было создано в первый раз. Я пропустил что-то важное здесь? Или пытаетесь сделать что-то, что невозможно с контейнером IoC?


person unknownpresense    schedule 22.01.2019    source источник
comment
VariableHandlerFactory.GetInstance("Str") возвращает экземпляр IVariableTypeHandler? Также я думаю, что вам нужно аннотировать свойство атрибутом [Dependency]...   -  person Johnny    schedule 22.01.2019


Ответы (1)


Как правило, хороший дизайн предлагает делать классы helper\manipulator\operation без состояния. Здесь у вас явно есть вспомогательный класс с состоянием, поскольку вы можете установить состояние объекта через свойство _variableHandler (не реальное свойство, это больше похоже на поле).

Что вы можете сделать, так это заставить factory для IVariableHandler зарегистрировать это в IoC и внедрить его в XAMLHelper. Затем при вызове помощника вы просто указываете, какой обработчик использовать, и заново создаете его с помощью factory. Фабрика может быть немного умнее, чтобы повторно использовать уже созданный объект, используя какое-то кэширование.

public intefrace IVariableHandlerFactory
{
    IVariableHandler Get(string description);
}

public class VariableHandlerFactory : IVariableHandlerFactory
{
    private readonly IDictionary<string, IVariableHandler> _cache;

    public VariableHandlerFactory()
    {
        _cache = new Dictionary<string, IVariableHandler>();
    }

    public IVariableHandler Get(string description)
    {
        IVariableHandler handler; 

        if(_cache.TryGetValue(description, out handler))
        {
            return handler;         
        }

        handler = //create it...
        _cache[description] = handler;

        return handler;
    }
}

а затем используйте это в XAMLHelper

public class XAMLHelper
{
    private readonly IVariableHandlerFactory _factory;

    public XAMLHelper(IVariableHandlerFactory factory)
    {
        _factory = factory;
    }

    public void HelpMe(string description)
    {
        var handler = _factory.Get(description);

        //do actual work using handler
    }
}

Давайте отложим это в сторону и обсудим настоящую проблему здесь. Во-первых, вам не хватает аннотации вашей собственности:

[Dependency]
public IVariableTypeHandler VariableHandler { get; set; }

тогда вы можете зарегистрировать XAMLHelper, используя InjectionProperty

container.RegisterType<VariableTypeHandlerImpl, IVariableTypeHandler>();

using(var lifetime = new ContainerControlledLifetimeManager())
    container.RegisterType<XAMLHelper>(lifetime, new InjectionProperty("VariableHandler");

или, если у вас есть конкретный экземпляр IVariableTypeHandler, вы можете использовать:

var variableHandler = VariableHandlerFactory.GetInstance("Str");

using(var lifetime = new ContainerControlledLifetimeManager())
    container.RegisterType<XAMLHelper>(lifetime, new InjectionProperty("VariableHandler", variableHandler);
person Johnny    schedule 22.01.2019
comment
Действительно, VariableHandlerFactory.GetInstance("Str") возвращает экземпляр IVariableTypeHandler. Можете ли вы предложить простой способ сделать класс XAMLHelper без состояния, но при этом использовать VariableHandlers, созданные фабрикой? Я также добавил атрибут «Зависимость», как было предложено, а затем использовал этот код container.Resolve<XAMLHelper>(new PropertyOverride("VariableHandler", VariableHandlerFactory.GetInstance("Int"))); для обновления свойства из VariableHandler по умолчанию (как указано в регистрации контейнера), но, похоже, это не обновляет свойство. Какие-либо предложения? - person unknownpresense; 23.01.2019
comment
@unknownpresense Например, почему XAMLHelper должен быть синглтоном, не могли бы вы каждый раз создавать его и вводить IVariableTypeHandler? Сколько вариаций у IVariableTypeHandler, не могли бы вы зарегистрировать все его типы? - person Johnny; 23.01.2019
comment
Класс XAMLHelper вызывается около 100 раз за цикл, а процесс может выполнять до 30 циклов за один цикл. Свойство VariableHandler обновляется примерно 6 раз за эти 100 вызовов. Я подумал, что создавать новый экземпляр каждый раз было бы немного излишним. В настоящее время существует 20 вариантов IVariableHandler, но их число может легко увеличиться вдвое, поэтому я решил, что подойдет фабрика. Я пытаюсь кодировать в соответствии с высокими стандартами (после SOLID, DRY и т. д.), поэтому любые улучшения будут высоко оценены :) - person unknownpresense; 23.01.2019
comment
@unknownpresense Для IVariableHandler у вас может быть фабрика или сборщик, который подойдет, может быть, фабрика с базовым кешем, поэтому вы создаете только один раз и сохраняете, если это возможно. Затем внедрите эту фабрику в XAMLHelper и вызовите методы XAMLHelper, указав, какой IVariableHandler использовать... Возможно ли это? - person Johnny; 23.01.2019
comment
Это отличная идея! Бонусный вопрос здесь: как мне кэшировать ранее созданные IVariableHandler? Я подумал о том, чтобы сделать их все синглтонами, может ли это сработать? Также я считаю, что ваше предложение указать, какой обработчик использовать, решит мою проблему! Затем я могу просто проверить, что текущий обработчик на XAMLHelper совпадает с указанным, и обновить, если нет: D - person unknownpresense; 23.01.2019
comment
@unknownpresense Вы можете кэшировать их так же просто, как IDictionary<Type, IVariableHandler>. Тот, что в XAMLHelper, на самом деле не нужен, но может быть также кешем L1, L2 ... Должен ли я обернуть это также в ответ? - person Johnny; 23.01.2019
comment
Если вы не возражаете, это бы мне здорово помогло! Спасибо, спасибо за помощь :) - person unknownpresense; 23.01.2019