Hi,
I am back with a new pattern today.
My requirement is to create a game which simulates with a pizza store.Right now the store will support two type of Pizzas "Cheese" and "DoubleCheese". So i start off with the design, i will have a class called "Pizza" which has the cost and description of the Pizza.Now we will have to have two more classes "Cheese pizza" and "doubleCheesePizza". both of them extend the "Pizza" class. this will work fine- no problem. but after all this , now i feel that my pizza store has to have some more pizzas. so I have to introduce new classes for the new pizzas. so the problem is something called "Class Explosion" - having a lot of classes which finally become unmanageable. Some of the classes will be
Cottage garden, cottage garden with extra Cheese, papperoni, papperoni with extra cheese so on.
but now if i want to introduce pizzas with thin crust? we are all lost, we have to make such a lot of changes in the design. My pizza store will be limited in terms of the pizza it offers. competitors will offer a lot of variety and one fine day i will have to close my store.
this is the piece of code (This is wrong design)
public class Pizza
{
double _Cost;
string _Description;
public double Cost
{
set
{
_Cost = value;
}
}
public string Description
{
set
{
_Description = value;
}
}
public void GetCost()
{
Console.WriteLine("The Cost is " + _Cost.ToString());
}
public void GetDescription()
{
Console.WriteLine("The Description is " + _Description);
}
public Pizza()
{
}
}
public class CheesePizza : Pizza
{
public CheesePizza()
{
}
}
public class DoubleCheesePizza : Pizza
{
public DoubleCheesePizza()
{
}
}
public class CottageGarden : Pizza
{
public CottageGarden()
{
}
}
Method of instantiation
Pizza obj = new Pizza();
obj.Cost = 150.00;
obj.Description = "pizza base";
obj.GetCost();
obj.GetDescription();
Pizza obj1 = new CheesePizza();
obj1.Cost = 190.00;
obj1.Description = "pizza base with cheese";
obj1.GetCost();
obj1.GetDescription();
But I feel that there is another nice manner in which we can implement the same. how? lets start with the "design principles" -
1) identify what changes and what remains static - here the crust (i sometimes use the term "type of pizza" for the same) and the topping (flavor of pizza) change, so we need to differentiate them.
2) code to interface not to an implementation (so we need some abstract classes or interfaces here)- we will have a common abstract class for pizza crust called "Pizza", why abstract class? because we have the properties - cost and description. interfaces cannot have properties. we have two functions :getCost() and GetDescription() which are abstract methods. We have two classes - "ThickCrust" and "ThinCrust" implements "Pizza". we now focus on the topping - we have a abstract class topping which extends pizza ( why extend pizza? topping will not be used by itself, it will always be a part of pizza an addon to pizza.). now we have some topping (cheese ) to add flavor to the crust. we implement the methods of "Pizza" in these classes. also these class will have an object of type "Pizza". why?reason is that we are adding something more to the basepizza. we have a pizza crust, we add one topping to that crust, then we can add one more topping to the pizza which we have now. that is the reason why we have pizza composed as a part of topping (cheese and double cheese).
example
thin crust is a pizza.
this if contained with in a cheese object will be (thin crust + cheese) which is again a pizza.
if (thin crust + cheese) is in another cheese object (it is again a pizza - a double cheese).
so now we don't need the double cheese class itself. can e see the difference between our old approach and our new approach-- yes we can drastically reduce the number of extra classes.
public abstract class Pizza
{
public double Cost;
public string Description;
public abstract Double getCost();
public abstract string GetDescription();
}
public class thinCrust : Pizza
{
public thinCrust()
{
this.Cost = 160;
this.Description = "thin Crust ";
}
public override double getCost()
{
return this.Cost;
}
public override string GetDescription()
{
return this.Description;
}
}
public class thickCrust : Pizza
{
public thickCrust()
{
this.Cost = 140;
this.Description = "thick Crust ";
}
public override double getCost()
{
return this.Cost;
}
public override string GetDescription()
{
return this.Description;
}
}
public abstract class Topping : Pizza
{
}
public class Cheese : Topping
{
Pizza PizzaObject;
public Cheese(Pizza pPizza)
{
PizzaObject = pPizza;
}
public override double getCost()
{
this.Cost = PizzaObject.Cost + 20;
return PizzaObject.Cost +20;
}
public override string GetDescription()
{
this.Description = PizzaObject.Description + " , Cheese topping";
return PizzaObject.Description +" , Cheese topping";
}
}
To instantiate
create a crust (thin or thick) object first,
wrap it in the topping object (cheese as of now). this will create a cheese pizza. now we can wrap this cheese pizza in another cheese object and the result will be a double cheese pizza.
Pizza objPIzza = new thickCrust();
Pizza Objcheese = new Cheese(objPIzza);
Console.WriteLine(Objcheese.getCost());
Console.WriteLine(Objcheese.GetDescription());
//implementing double cheese
Pizza DoubleCheese = new Cheese(Objcheese);
Console.WriteLine(DoubleCheese.getCost());
Console.WriteLine(DoubleCheese.GetDescription());
Output
160
thick Crust , Cheese topping
180
thick Crust , Cheese topping , Cheese topping
can you see how flexible our design is now?
if i want my store to introduce a new type of pizza ( say a new crust -- UltraThinCrust) and a new topping (called "Extravaganza"), i have to create a new class "UltraThinCrust" which implements "Pizza" and a new class called "Extravaganza" which implements "Topping" and then i can create an instance of "UltraThinCrust" and pass it to an instance of Extravaganza.
so we get a new pizza "UltraThinCrust Extravaganza".
no class explosion, no unmanageable classes.
its all easy by just wrapping objects.
Happy Coding !!!
No comments:
Post a Comment