Showing posts with label OOP. Show all posts
Showing posts with label OOP. Show all posts

Dec 6, 2015

Applying the Strategy Pattern

One of my favorite patterns, is the Strategy pattern. It is so simple and so useful and can be applied in many contexts.

In this post, I am going to show you where I have applied the Strategy pattern. I've seen many example on the net, mostly academic examples, but in this post, my example will be based on a real world example. I am using Borland Developer Studio 2006 and Windows Vista.

If you not are familiar with the Strategy pattern, there are a  lot of good information out on the net. I will not do any in depth explanation of the pattern here. However, if you are familiar with it, but just don't remember it at the moment, here is the intent of the Strategy pattern according to GoF.

"Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it"

Well, let's start looking into the example I want to show.

Let's say we are working with a program, which is managing items in a stock for a store. The program must handle the items article number, name, weight, price etc.

Now let's say we have some legacy code of a class describing an item.

#ifndef itemH
#define itemH

#include <System.hpp>

class Item
{
public:
 Item(const String &name, const String &artNo,
   float costPriceEuro, float weightKg)
 : name_(name), artNo_(artNo),
   costPriceEuro_(costPriceEuro), weightKg_(weightKg)
 {}

 float GetCostPrice() const { return costPriceEuro_; }

private:
 String name_;
 String artNo_;
 float costPriceEuro_;
 float weightKg_;
};
#endif

Let's say we have a client, which is using the Cost prices. The Cost price can easily be retrieved by calling the items GetCostPrice() method.

Item item("Jeans", "J100-A12", 20.00, 0.450);
float costPrice = item.GetCostPrice();
 
The Cost price is the price the retailer pays for the item. Here we are passing the Cost price to the item via the constructor. The Cost price may have been retrieved from a database or similar.

Now, the new requirements tells us that we must be able to handle other kind of prices. More exactly, it must handle the Gross price and the Net price as well.

Before proceeding, just some explanations of the price terms above. I'm not an economist, so don't be too hard on me. Here are some definitions.

The Gross price is the price after the retailer has added the markup margin and the Net price, is the final price you as customer pays.

 The formulas for the prices can be written according to;

Gross price = Cost price + Cost price x MarkupPercent/100;
Net price = Gross price - Gross price x DiscountPercent/100;

Now it is time to add the functionality to our Item class. The item must know its markup and discount, so we add those two arguments to the parameter list and also adding methods, so the client can get the prices and percentages.

#ifndef itemH
#define itemH

#include <System.hpp>

class Item
{
public:
 Item(const String &name, const String &artNo,
   float costPriceEuro, float weightKg,
   float markupPercent, float discountPercent)
 : name_(name), artNo_(artNo),
   costPriceEuro_(costPriceEuro), weightKg_(weightKg),
   markupPercent_(markupPercent), discountPercent_(discountPercent)
 {}

 float GetCostPrice() const { return costPriceEuro_; }
 float GetGrossPrice() const;
 float GetNetPrice() const;
 float GetMarkupPercent() const { return markupPercent_; }
 float GetDiscountPercent() const { return discountPercent_; }

private:
 String name_;
 String artNo_;
 float costPriceEuro_;
 float weightKg_;
 float markupPercent_;
 float discountPercent_;
};
#endif
 
The details for the metods GetGrossPrice and GetNetPrice is given in the implementation file for the item, see below.

#include "item.h"

float Item::GetGrossPrice() const
{
   return GetCostPrice() + GetCostPrice()*GetMarkupPercent()/100;
}


float Item::GetNetPrice() const
{
   return GetGrossPrice() - GetGrossPrice()*GetDiscountPercent()/100;
}
 
The client then needs to call the methods like below.

Item item("Jeans", "J100-A12", 20.00, 0.450, 110, 25);
float costPrice = item.GetCostPrice();
float grossPrice = item.GetGrossPrice();
float netPrice = item. GetNetPrice();

We now have a situation, which is clearly applicable for the Strategy pattern.

By using the Strategy pattern, we can encapsulate the price calculation in a class, see the definitions below.

#ifndef strategyH
#define strategyH

class Item;

class Price
{
public:
 virtual float Calc(const Item &item) const = 0;
};


class CostPrice : public Price
{
public:
 virtual float Calc(const Item &item) const;
};


class GrossPrice : public Price
{
public:
 virtual float Calc(const Item &item) const;
};


class NetPrice : public Price
{
public:
 virtual float Calc(const Item &item) const;
};
#endif

The implementation file for the concrete strategies, looks like below.

#include "strategy.h"
#include "item.h"

float CostPrice::Calc(const Item &item) const
{
 return item.GetCostPrice();
}


float GrossPrice::Calc(const Item &item) const
{
 float costPrice = CostPrice().Calc(item);
 return costPrice + costPrice*item.GetMarkupPercent()/100;
}


float NetPrice::Calc(const Item &item) const
{
 float grossPrice = GrossPrice().Calc(item);
 return grossPrice - grossPrice*item.GetDiscountPercent()/100;
}
 
Now we have written our abstract base class and the concrete strategies, so let's use them in the Item implementation. First, let's rewrite the Item class definition.
#ifndef itemH
#define itemH

#include <System.hpp>

class Price;

class Item
{
public:
 Item(const String &name, const String &artNo,
   float costPriceEuro, float weightKg,
   float markupPercent, float discountPercent)
 : name_(name), artNo_(artNo),
   costPriceEuro_(costPriceEuro), weightKg_(weightKg),
   markupPercent_(markupPercent), discountPercent_(discountPercent)
 {}

 float GetCostPrice() const { return costPriceEuro_; }
 float GetPrice(const Price &price) const;
 float GetMarkupPercent() const { return markupPercent_; }
 float GetDiscountPercent() const { return discountPercent_; }

private:
 String name_;
 String artNo_;
 float costPriceEuro_;
 float weightKg_;
 float markupPercent_;
 float discountPercent_;
};
#endif

Above, I've written a general GetPrice function, which takes the strategy as a parameter. The corresponding implementation file now looks like below.

#include "item.h"
#include "strategy.h"

float Item::GetPrice(const Price &price) const
{
   return price.Calc(*this);
}

Finally, the client code can be updated according to below.

Item item("Jeans", "J100-A12", 20.00, 0.450, 110, 25);
float costPrice = item.GetPrice(CostPrice());
float grossPrice = item.GetPrice(GrossPrice());
float netPrice = item.GetPrice(NetPrice());

In the future, there may come a new requirement, to implement a new kind of price calculation. In this case, we don't need to modify anything in the Item class, but we need to inherit a new concrete strategy from the abstract base class; Price.

Furthermore, another advantage of using strategies, is showed below. Let's say you need to summarize all GrossPrices and NetPrices of items in a container. Using the first approach with item.GetGrossPrice() and item.GetNetPrice(), may result in two functions, see below.

float GetGrossPriceSum()
{
 float sum = 0;
 //for each item
  sum += item.GetGrossPrice();
 //end loop

 return sum;
}

float GetNetPriceSum()
{
 float sum = 0;
 //for each item
  sum += item.GetNetPrice();
 //end loop

 return sum;
}

These functions are doing the exact same things, except for the function name. We can easily simplify this to one function, using the concrete strategy as an input parameter.

float GetPriceSum(const Price &price)
{
 float sum = 0;
 //for each item
  sum += item.GetPrice(price);
 //end loop

 return sum;
}

I would like to summarize this post with a simple quote from Alan Shalloway/James R. Trott in their book "Design Patterns explained", p. 123.



"Find what varies and encapsulate it"


This is indeed what we are doing in the Strategy pattern. This is something I repeat for myself, when I am designing a new system of my own. Very good to keep in mind.

You are welcome to leave comments, complaints or questions!


Jan 7, 2015

SOLID

I bet you have encountered some code smells during your programming career.

Maybe you have made a change (corrected a bug) in a module, which caused a change (introduced a new bug) in another unforseen module?

Maybe you have experienced that a very simple change in your module will force you to do changes in multiple modules?

Even worse, code duplicates you wasn't aware of (you fixed a bug in a function in a module, but the bug was not fully solved due an exact code duplicate of the function in another module)?

There are more code smells around out there, you tell me!

However, using SOLID will help you to prevent some of the code smells. SOLID is a set of five basic OOP principles, that will help you to write quality software.

The SOLID acronym:

S : Single responsibility principle (SRP)
   There should never be more than one reason for a class to change

O : Opened/closed principle (OCP)
   Software entities should be open for extension, but closed for modification

L : Liskov substitution principle (LSP)
   Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it

I : Interface segregation principle (ISP)
   Clients should not be forced to depend upon interfaces that they do not use

D: Dependency inversion principle (DIP)
   A. High level modules should not depend upon low level modules. Both should depend upon abstractions.
B. Abstractions should not depend upon details. Details should depend upon abstractions


There is a lot of good articles on the net. I have no intention to explain them here. This post is more of a reminder that there are basic principles to follow.

My personal experienced is that you will need some training to actually understand when to use them. Further you will need some time to make it a habit to always consider SOLID when you are writing software.

Generally, I think it is easier to use SOLID when writing new software. To apply SOLID in existing code demands more effort (and time). Of course, it depends on the code you start from.

You are welcome to leave comments, complaints or questions!