Избегание связи с образцом стратегии - PullRequest
5 голосов
/ 05 мая 2011

Я пытаюсь применить шаблон «Стратегия» к конкретной ситуации, но у меня возникла проблема с тем, как избежать привязки каждой конкретной стратегии к объекту контекста, предоставляющему данные для нее. Ниже приведен упрощенный случай шаблона, который происходит несколькими различными способами, но должен обрабатываться аналогичным образом.

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

Итак, представьте следующие (очень упрощенные) классы:

class Acquisition
{
    public Int32 IntegrationTime { get; set; }
    public Double Battery { get; set; }
    public Double Signal { get; set; }
}

interface IAnalogOutputter
{
    double getVoltage(Acquisition acq);
}

class BatteryAnalogOutputter : IAnalogOutputter
{
    double getVoltage(Acquisition acq)
    {
        return acq.Battery;
    }
}

Теперь каждый конкретный класс стратегии должен быть связан с моим классом Acquisition, который также является одним из наиболее вероятных классов для изменения, поскольку он является ядром для нашего приложения. Это все еще улучшение по сравнению со старым дизайном, который был гигантским оператором switch внутри класса Acquisition. У каждого типа данных может быть свой метод преобразования (хотя «Батарея» - простой переход, другие не так уж просты), поэтому я считаю, что стратегия или аналогичный метод должны быть подходящим способом.

Также отмечу, что в окончательной реализации IAnalogOutputter будет абстрактным классом, а не интерфейсом. Эти классы будут в списке, который настраивается пользователем и сериализуется в файл XML. Список должен редактироваться во время выполнения и запоминаться, поэтому Serializable должен быть частью нашего окончательного решения. В случае, если это имеет значение.

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

Ответы [ 3 ]

2 голосов
/ 05 мая 2011

Strategy Pattern инкапсулирует - обычно сложную - операцию / расчет.

Напряжение, которое вы хотите вернуть, зависит от

  • штук конфигурации
  • Некоторые данных сбора

Так что я бы поместил их в другой класс и передал разработчикам стратегий.

Также с точки зрения сериализации у вас нет сериализации классов стратегии, возможно, только их имя или имя типа.


UPDATE

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

0 голосов
/ 17 мая 2011

Хорошо, я ненавижу не отдавать кому-то честь здесь, но я нашел гибридное решение, которое очень хорошо подходит для моих целей.Он отлично сериализуется и значительно упрощает добавление новых типов вывода.Ключ был единственным интерфейсом, IOutputValueProvider.Также обратите внимание, как легко этот шаблон обрабатывает извлечение различных способов хранения данных (например, словарь вместо параметра).

interface IOutputValueProvider
{
    Double GetBattery();
    Double GetSignal();
    Int32 GetIntegrationTime();
    Double GetDictionaryValue(String key);
}

interface IAnalogOutputter
{
    double getVoltage(IOutputValueProvider provider);
}

class BatteryAnalogOutputter : IAnalogOutputter
{
    double getVoltage(IOutputValueProvider provider)
    {
        return provider.GetBattery();
    }
}

class DictionaryValueOutputter : IAnalogOutputter
{
    public String DictionaryKey { get; set; }
    public double getVoltage(IOutputValueProvider provider)
    {
        return provider.GetDictionaryValue(DictionaryKey);
    }
}

Итак, мне просто нужно убедиться, что Acquisition реализует интерфейс:

class Acquisition : IOutputValueProvider
{
    public Int32 IntegrationTime { get; set; }
    public Double Battery { get; set; }
    public Double Signal { get; set; }
    public Dictionary<String, Double> DictionaryValues;

    public double GetBattery() { return Battery;}
    public double GetSignal() { return Signal; }
    public int GetIntegrationTime() { return IntegrationTime; }
    public double GetDictionaryValue(String key) 
    {
        Double d = 0.0;
        return DictionaryValues.TryGetValue(key, out d) ? d : 0.0;
    }
}

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

0 голосов
/ 05 мая 2011

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

public class OutputterFactory
{
    public static IAnalogOutputter CreateBatteryAnalogOutputter(Acquisition acq)
    {
        return new BatteryANalogOutputter(acq.Battery);
    }



}
...