«вложенный» / комбинированный шаблон стратегии? - PullRequest
1 голос
/ 15 декабря 2011

Я знаю, что название странное, поэтому позвольте мне попытаться выполнить некоторые базовые настройки.

У меня есть объект с именем StyleBundle.На основе двух вещей, Duration of StyleBundle и «тип» StyleBundle (Unlimited или PerStyle), будет определять общую цену StyleBundle.Итак, вот быстрый фрагмент StyleBundle:

public class StyleBundle
{
    public decimal Price {get; set;}
    public StyleType Type {get; set;} //STyleType is a simple enum, either "Unlimited" or "PerStyle"
    public Duration Duration {get; set;}
}

Вот продолжительность.По сути, он имеет перечисление DurationType, значения которого могут быть такими, как DurationType.OneYear, DurationType.TwoYears и т. Д. *

public class Duration
{
    public Duration(TimeSpan timeSpan, string name, DurationType type)
    {
        this.TimeSpan = timeSpan;
        this.Name = name;
        this.Type = type;
    }

    public TimeSpan TimeSpan { get; set; }
    public string Name { get; set; }
    public DurationType Type { get; set; }
}

В своем классе StyleBundle я передаю Duration фабрике стратегий с именем StyleBundlePricingStrategy.,Вот этот класс:

public class StyleBundlePricingFactory
{
     public static IPricingStrategy GetPricing(Duration duration)
     {
         if (duration.Type == DurationType.OneYear) { return new OneYearPricingStrategy(); }
         if (duration.Type == DurationType.TwoYear) { return new TwoYearPricingStrategy(); }
         etc...
         etc...
     }
 }

возвращаемые классы реализуют интерфейс IPricingStrategy:

public interface IPricingStrategy
{
    decimal GetPriceFor(StyleBundle aStyleBundle);
}

, который получает цену для StyleBundle.Каждый класс стратегии инкапсулирует, как цена извлекается для данного типа DurationType.Вот пример класса OneYearPricingStrategy:

public class OneYearPricingStrategy : IPricingStrategy
{
    public decimal GetPriceFor(StyleBundle aStyleBundle)
    {
        if (aStyleBundle.StylePricingType == StylePricingType.PerStyle)
        {
            return aStyleBundle.Products.Count() * 2500m;
        }
        else
        {
            return  50000m;
        }
    }
}

Хорошо, так что довольно простая настройка стратегии.

То, что меня скушает, это то, что если вы посмотрите на эту строку кода в классе "OneYearPricingStrategy":

if (aStyleBundle.StylePricingType == StylePricingType.PerStyle)

вы увидите, что мне все еще нужно использовать условное выражениев классе Strategy для учета типа StyleBundle.Тип StyleBundle будет влиять на то, как рассчитывается цена.

Для меня это плохой дизайн, б / к для каждого класса стратегии я пишу "OneYearPricingStratety", "TwoYearPricingStrategy" и т. Д. ... условно StylePricingTypeКопируется и вставляется во все классы Стратегии.

Это не хорошо, что происходит, когда мне нужно добавить новый StylePricingType?Мне придется вернуться к каждому классу PricingStrategy и обновить код, чтобы в окне выводился весь SRP (наряду с другими вещами) ...

мне нужен способ реализации некоторого типапаттерна, который позволит мне объединить две «стратагии» (Duration с StyleBundleType) и позволить правилам жить в одном месте ... не разбросанным по коду.

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

Может быть, это неправильный шаблон?

Любые указатели будут более чем оценены.

Спасибо, Майк

РЕДАКТИРОВАТЬ:

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

Изначально свойство StyleBundle.Price выглядело так:

public decimal Price
{
    get
    {
        if (this.StylePricingType == StylePricingType.PerStyle)
        {
            if (this.Duration.Type == DurationType.ThreeDays)
            {
                _price = 1500m;
            }
            else if (this.Duration.Type == DurationType.OneYear)
            {
                _price = 2500m;
            }
            else if (this.Duration.Type == DurationType.TwoYears)
            {
                _price = 2000m;
            }
            else if (this.Duration.Type == DurationType.ThreeYears)
            {
                _price = 1650m;
            }
        }
        else if (this.StylePricingType == StylePricingType.Unlimited)
        {
            if (this.Duration.Type == DurationType.ThreeDays)
            {
                throw new Exception("You can not have a StyleBundle of type Unlimited for a Duration of three days.");
            }
            else if (this.Duration.Type == DurationType.OneYear)
            {
                _price = 50000m;
            }
            else if (this.Duration.Type == DurationType.TwoYears)
            {
                _price = 40000m;
            }
            else if (this.Duration.Type == DurationType.ThreeYears)
            {
                _price = 33500m;
            }
        }
        else
        {
            throw new Exception("Illegal StylePricingType passed to Product.");
        }
        return _price;
    }
    private set
    {
        _price = value;
    }
}

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

Теперь, с применением шаблона проектирования Стратегии к этой проблеме, мое свойство StyleBundle.Price выглядит следующим образом:

public decimal Price
    {
        get
        {
            return _pricingStrategy.GetPriceFor(this);
        }
        private set
        {
            _price = value;
        }
    }

, где _pricingStrategy - это IPricingStrategy, и решение о том, какой разработчик использовать для нового -решено путем вызова класса StyleBundlePricingFactory.GetPricing (duration) в конструкторе StyleBundle.

1 Ответ

4 голосов
/ 15 декабря 2011

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

Шаблон Стратегия обычно зарезервирован для случаев, когда вы хотите делегировать поведенческую логику для класса. Например, если у меня есть класс ChessPlayer, то Grandmaster implements ChessStrategy и Novice implements ChessStrategy будет хорошим способом изменить поведенческую стратегию моего ChessPlayer, тогда как интерфейс ChessPlayer, который перемещает фигуры, не будет иметь изменить.

В вашем случае у вас просто есть данные, а не сложная стратегия расчета цены, поэтому я бы искал подходящую структуру данных. Дважды вложенные HashMap поверх Durations X PricingStyles будут работать нормально. Пример:

Price calculatePrice(Duration d, PricingStyle s) {
    return map.get(d).get(s);
}

PS: когда дело доходит до хорошего дизайна, то, что требует меньше кода, обычно становится победителем.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...