У меня есть следующий сценарий, где у меня есть различные виды алгоритмов продаж для вычисления продажной цены. 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;
}
}
В основе шаблона стратегии лежит идея о том, что вызывающий код не знает вызываемую реализацию.
Если бы вы изменили параметры, используемые для каждой реализации, вы бы обнаружили, что не получаете всех преимуществ этого шаблона: вызывающему нужно было бы знать, какая реализация будет использоваться и как ее вызвать.
Я обычно передаю класс, содержащий супернабор информации (что-то вроде PricingInfo), который всегда заполняется одинаково (в идеале централизовано в коде), и единственная разница заключается в реализации стратегии. .
Одним из преимуществ является то, что я могу добавить свойство к моему классу PricingInfo, которое не было актуальным в прошлом (например, systemDiscount), и влияние на систему в целом не слишком велико.
На мой взгляд, не очень хороший. Я бы оставил это как есть. Вы можете использовать различные уловки, например params (с одним params double [] priceData) или IDynamicObject
. Но лучше всего, если некоторые стратегии игнорируют дополнительный параметр.
Другой альтернативой является использование объекта параметров или Dictionary
. Таким образом, вы можете объединить количество параметров для каждого метода и оставить место для дополнительных параметров, если в будущем произойдут изменения требований.
Единственным недостатком является то, что Dictionary
может усложнить отслеживание параметров в вашем коде, тогда как объект параметров просто будет иметь все свойства, которые вы можете просматривать в вашем коде.
Нет. Это не лишний параметр; код, использующий SalesStrategy, не должен знать, какой конкретный класс он использует, поэтому сигнатура метода должна быть идентичной во всех производных классах.
Если вы используете 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);
Хороший способ удалить нерелевантные параметры из интерфейса - передать эти параметры в конструкторы из подклассов. Итак, альтернативой для вашего дизайна может быть:
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;
}
}
В этой конструкции вы не загрязняете свой интерфейс конкретными данными из подклассов.