Is there a good way to avoid an unused method parameter in some subclasses when applying a strategy template?

I have the following scenario when I have different types of sales algorithms for calculating the selling price. FixedSaleStrategy does not need the basePrice parameter, while all other strategy implementations need this. Is there a good way to avoid this redundant parameter?

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; } } 
+7
c # oop design-patterns strategy-pattern
source share
6 answers

The strategy template is based on the idea that the calling code does not know the called implementation.

If you change the parameters used for each implementation, you will find that you are not getting the full benefit of this template: the caller needs to know which implementation should be used and what to call it.

What I usually do is pass a class containing a super-set of information (something like PricingInfo) that always populates the same way (ideally centralized in the code), and the only difference is the implementation of the strategy.

One of the advantages is that I can add a property to my PricingInfo class, which was not relevant in the past (for example, systemDiscount), and the impact on the system as a whole is not too great.

+6
source share

Not. This is not a redundant parameter; the code that SalesStrategy uses does not need to know which particular class it uses, so the method signature must be the same in all derived classes.

+5
source share

If you are using C # 4.0, you can change the parameters and make basePrice optional:

 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; } } 

The value can be performed as follows:

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

Note that the AmountOffSale basePrice parameter is optional, which means the following will not compile:

 AmountOffSale amountOffSale = new AmountOffSale(); ... // No overload for method 'GetPrice' takes 1 arguments amountOffSale.GetPrice(salesAmount); 
+2
source share

Not good, in my opinion. I would keep it as it is. There are various tricks that you could use, for example params (have one double [] priceData parameter) or IDynamicObject . But the purest thing is simply that some strategies ignore an additional parameter.

0
source share

A good way to remove irrelevant parameters from an interface is to pass these parameters in constructors from subclasses. So, an alternative to your design would be:

 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; } } 

In this construct, you do not pollute your interface with specific data from subclasses.

0
source share

Another alternative is to use a parameter object or Dictionary<string, object> . Thus, you can consolidate the number of parameters for each method and leave room for additional parameters if there is a change in requirements in the future.

The only drawback is that Dictionary<string, object> can track parameters in your code, where as a parameter object there will simply be all the properties that you can view in your code.

0
source share

All Articles