Hi I was tasked with refactoring a portion of code in order for it to corespond with the open/closed principle.I managed to do that applying the strategy pattern in two of the methods but in one I do not know how I should proceed.
public ProductPriceCalculator(Product product)
{
this.product = product;
buyerStrategy = new BuyerStrategy();
discountStrategy = new DiscountStrategy();
}
public Price CalculatePrice()
{
price = new Price();
decimal productPrice = product.BasePrice +
(product.BasePrice * product.Addition);
decimal TVA = CalculateTVA();
price.ProductPrice = productPrice*TVA;
decimal discount = CalculateDiscount(price.ProductPrice);
price.Discount = price.ProductPrice * discount;
price.ProductPriceWithDiscount = price.ProductPrice - price.Discount;
price.TransportPrice = product.Transport.GetTransportPrice();
price.TotalPrice = price.ProductPriceWithDiscount + price.TransportPrice;
return price;
}
In this case this method initialezes the object by using the methods in the class.As it stands this method is not closed for modification because if at some point I have to add another property to Price I will have to come back here to initialize it.
How can I structure this code right?
One possible solution is following:
public class ProductPriceCalculator
{
private readonly Product _product;
private readonly BuyerStrategy _buyerStrategy;
private readonly DiscountStrategy _discountStrategy;
private readonly Price _price;
public ProductPriceCalculator(Product product,BuyerStrategy buyerStrategy,DiscountStrategy discountStrategy,Price price)
{
_product = product;
_buyerStrategy = buyerStrategy;
_discountStrategy = discountStrategy;
_price = price;
}
public Price CalculatePrice()
{
decimal productPrice = _product.BasePrice + (_product.BasePrice * _product.Addition);
decimal TVA = CalculateTVA();
decimal discount = CalculateDiscount(productPrice * TVA);
decimal transportPrice = _product.Transport.GetTransportPrice();
return _price.CalculatePrice(productPrice*TVA,discount,transportPrice);
}
...
}
public class Price
{
...
public virtual Price CalculatePrice(decimal productPrice, decimal discount, decimal transportPrice)
{
Price price = new Price();
price.ProductPrice = productPrice;
price.Discount = ProductPrice * discount;
price.ProductPriceWithDiscount = ProductPrice - Discount;
price.TransportPrice = transportPrice;
price.TotalPrice = ProductPriceWithDiscount + TransportPrice;
return price;
}
...
}
It is good to put dependencies (such as buyerStrategy
,discountStrategy
,price
) in constructor and than fill them via IoC container or something else rather then creating them in constructor themselves.
After introducing _price
field you can delegate filling price's properties to Price
class itself. Method Price.CalculatePrice
can be called as Fabric method for Price
class.