Есть ли хороший способ избежать использования неиспользуемого параметра метода в некоторых подклассах при применении шаблона стратегии? - PullRequest
8 голосов
/ 31 июля 2010

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

public abstract class SalesStrategy
{
    public abstract double GetPrice(double basePrice, double saleAmount);
}
public class AmountOffSale : SalesStrategy
{
    public override double GetPrice(double basePrice, double salesAmount)
    {
        return basePrice - salesAmount;
    }
}
public class FixedPriceSale : SalesStrategy
{
    public override double GetPrice(double basePrice, double salesAmount)
    {
        return salesAmount;
    }
}

Ответы [ 6 ]

6 голосов
/ 31 июля 2010

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

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

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

Одним из преимуществ является то, что я могу добавить свойство в свой класс PricingInfoэто не было актуально в прошлом (как, например, systemDiscount), и влияние на систему в целом не слишком велико.

5 голосов
/ 31 июля 2010

Нет.Это не избыточный параметр;код, использующий SalesStrategy, не должен знать, какой конкретный класс он использует, поэтому сигнатура метода должна быть одинаковой во всех производных классах.

2 голосов
/ 31 июля 2010

Если вы используете c # 4.0, вы можете изменить параметры и сделать basePrice необязательным, например, так:

public abstract class SalesStrategy
{
    public abstract double GetPrice(double saleAmount, double basePrice = 0d);
}

public class AmountOffSale : SalesStrategy
{
    public override double GetPrice(double salesAmount, double basePrice)
    {
        return basePrice - salesAmount;
    }
}

public class FixedPriceSale : SalesStrategy
{
    public override double GetPrice(double salesAmount, double basePrice = 0d)
    {
        return salesAmount;
    }
}

То есть можно сделать следующее ...

FixedPriceSale fixedPrice = new FixedPriceSale();
...
fixedPrice.GetPrice(salesAmount);

Обратите внимание, что AmountOffSale 'basePrice параметр не необязательный, это означает, что следующее не будет компилироваться:

AmountOffSale amountOffSale = new AmountOffSale();
...
// No overload for method 'GetPrice' takes 1 arguments
amountOffSale.GetPrice(salesAmount); 
0 голосов
/ 31 июля 2010

Другой альтернативой является использование объекта параметров или Dictionary<string, object>.Таким образом, вы можете консолидировать количество параметров для каждого метода и оставить место для дополнительных параметров в случае изменения требований в будущем.

Единственным недостатком является то, что Dictionary<string, object> может усложнить отслеживание параметров.в вашем коде, где в качестве параметра объект просто будет иметь все свойства, которые вы можете просматривать в своем коде.

0 голосов
/ 31 июля 2010

Хороший способ удалить нерелевантные параметры из интерфейса - передать эти параметры в конструкторы из подклассов.Таким образом, альтернативой для вашего дизайна будет:

public interface SalesStrategy
    {
        double CalculatePrice(double basePrice);
    }

public class FixedPriceSale : SalesStrategy
    {
        public double CalculatePrice(double basePrice)
        {
            return basePrice;
        }
    }

public class AmountOffSale : SalesStrategy
    {
        public double SalesAmount { get; set; }

        public AmountOffSale(double salesAmount)
        {
            this.SalesAmount = salesAmount;
        }

        public double CalculatePrice(double basePrice)
        {
            return basePrice - SalesAmount;
        }
    }

В этой конструкции вы не загрязняете свой интерфейс конкретными данными из подклассов.

0 голосов
/ 31 июля 2010

Не очень, на мой взгляд. Я бы оставил все как есть. Существуют различные приемы, которые вы можете использовать, например params (иметь один параметр double [] priceData) или IDynamicObject. Но самое чистое - просто использовать некоторые стратегии, игнорирующие дополнительный параметр.

...