Wednesday, December 11, 2013

Refactoring to Decorator design pattern.

Hello

There was post about it (decorator-design-pattern), but lets do it better.

First of all i wish do it w/o using Design patterns, but using usual OOP principles.

So, i am a AM General company, witch making Hummer H1 army cars.

Lets do it!
I wish create car with two fields: weight and description

public interface IArmyCar
{
 int Weight { get; }
 string Description { get; }
}

public class Hummer : IArmyCar
{
 public Hummer() { }

 public int Weight
 {
  get { return 5000; }
 }

 public string Description
 {
  get { return "Hummer car"; }
 }
}

Great!
After some time, US army wish complete the car with some additions: Xenon lights. Lets do it:

public interface IArmyCar
{
 int Weight { get; }
 string Description { get; }
}

public class Hummer : IArmyCar
{
 public Hummer() { }

 public int Weight
 {
  get { return 5000; }
 }

 public string Description
 {
  get { return "Hummer car"; }
 }
}

public class CarExtraLights : IArmyCar
{
 public CarExtraLights() { }

 public int Weight
 {
  get { return 5020; }
 }

 public string Description
 {
  get { return "Hummer car + strong lights"; }
 }
}

Very simple. I just need remember how much kg in Hummer H1 when i do weight calculations and description of Hummer H1 when i return descriptions. But it does not gonna be changed FOR A NOW.

We sell very well cars to US army and do a lot of money.
After some time we get some new requirement. Texas state wish extra accumulator, because of Texas rangers like to play country music all the night in Hummer.
OK, no problem!

public interface IArmyCar
{
 int Weight { get; }
 string Description { get; }
}

public class Hummer : IArmyCar
{
 public Hummer() { }

 public int Weight
 {
  get { return 5000; }
 }

 public string Description
 {
  get { return "Hummer car"; }
 }
}

public class CarExtraLights : IArmyCar
{
 public CarExtraLights() { }

 public int Weight
 {
  get { return 5020; }
 }

 public string Description
 {
  get { return "Hummer car + strong lights"; }
 }
}

public class CarExtraAccumulator : IArmyCar
{
 public CarExtraAccumulator() { }

 public int Weight
 {
  get { return 5100; }
 }

 public string Description
 {
  get { return "Hummer car + big accumulator"; }
 }
}

Peace of cake!!! Just remember Hummer weight and description

Fine. US army, as all armies in the world wish equip cars with shield and gun! A lot of money for us! Lets do it:

public interface IArmyCar
{
 int Weight { get; }
 string Description { get; }
}

public class Hummer : IArmyCar
{
 public Hummer() { }

 public int Weight
 {
  get { return 5000; }
 }

 public string Description
 {
  get { return "Hummer car"; }
 }
}

public class CarShield : IArmyCar
{
 public CarShield() { }

 public int Weight
 {
  get { return 6000; }
 }

 public string Description
 {
  get { return "Hummer car + shield"; }
 }
}

public class CarArmor : IArmyCar
{
 public CarArmor() { }

 public int Weight
 {
  get { return 5200; }
 }

 public string Description
 {
  get { return "Hummer car + MAG"; }
 }
}

public class CarExtraLights : IArmyCar
{
 public CarExtraLights() { }

 public int Weight
 {
  get { return 5020; }
 }

 public string Description
 {
  get { return "Hummer car + strong lights"; }
 }
}

public class CarExtraAccumulator : IArmyCar
{
 public CarExtraAccumulator() { }

 public int Weight
 {
  get { return 5100; }
 }

 public string Description
 {
  get { return "Hummer car + big accumulator"; }
 }
}

Very good!

We are great company and we recruited new mechanical engineer, which make H1 stronger and lighter! Great success!!! But... now i need recalculate all Weight fields from all classes... OK, lets do it. Now H1 weight is 4500 kg:


public interface IArmyCar
{
 int Weight { get; }
 string Description { get; }
}

public class Hummer : IArmyCar
{
 public Hummer () { }

 public int Weight
 {
  get { return 4500; }
 }

 public string Description
 {
  get { return "Hummer car"; }
 }
}

public class CarShield : IArmyCar
{
 public CarShield() { }

 public int Weight
 {
  get { return 5500; }
 }

 public string Description
 {
  get { return "Hummer car + shield"; }
 }
}

public class CarArmor : IArmyCar
{
 public CarArmor() { }

 public int Weight
 {
  get { return 4700; }
 }

 public string Description
 {
  get { return "Hummer car + MAG"; }
 }
}

public class CarExtraLights : IArmyCar
{
 public CarExtraLights() { }

 public int Weight
 {
  get { return 4520; }
 }

 public string Description
 {
  get { return "Hummer car + strong lights"; }
 }
}

public class CarExtraAccumulator : IArmyCar
{
 public CarExtraAccumulator() { }

 public int Weight
 {
  get { return 4600; }
 }

 public string Description
 {
  get { return "Hummer car + big accumulator"; }
 }
}

Of course, we violate Open close principle, but for a now it is all changes.

After 3 month France army wish buy 10000 Hummers, equipped with extra accumulator and strong lights. OK... it is a lot of money!
Lets add CarExtraAccumulatorExtraLights class:

public interface IArmyCar
{
 int Weight { get; }
 string Description { get; }
}

public class Hummer : IArmyCar
{
 public Hummer () { }

 public int Weight
 {
  get { return 4500; }
 }

 public string Description
 {
  get { return "Hummer car"; }
 }
}

public class CarShield : IArmyCar
{
 public CarShield() { }

 public int Weight
 {
  get { return 5500; }
 }

 public string Description
 {
  get { return "Hummer car + shield"; }
 }
}

public class CarArmor : IArmyCar
{
 public CarArmor() { }

 public int Weight
 {
  get { return 4700; }
 }

 public string Description
 {
  get { return "Hummer car + MAG"; }
 }
}

public class CarExtraLights : IArmyCar
{
 public CarExtraLights() { }

 public int Weight
 {
  get { return 4520; }
 }

 public string Description
 {
  get { return "Hummer car + strong lights"; }
 }
}

public class CarExtraAccumulator : IArmyCar
{
 public CarExtraAccumulator() { }

 public int Weight
 {
  get { return 4600; }
 }

 public string Description
 {
  get { return "Hummer car + big accumulator"; }
 }
}

public class CarExtraAccumulatorExtraLights : IArmyCar
{
 public CarExtraAccumulator() { }

 public int Weight
 {
  get { return 4500 + 100 + 20; }
 }

 public string Description
 {
  get { return "Hummer car + big accumulator + strong lights"; }
 }
}

Looks no bad. But we need remember H1 weight, accumulator weight, lights weight.

After 2 weeks H1 weight changed, accumulator weight changed but lights weight do not. After month some country wish buy H1 + armor. After 2 months other country wish H1 + Lights+ armor.
After 1 year we have 1000 classes, because some countries wish 3 guns and 6 accumulators on H1!!!! Oh my god! And after 2 years we have 20000 classes...


And after all H1 weight changed again... 
I quit! No more! I started take anti-anxiety medication and it helps me.

But, fortunately i find time-machine !!!!

And i go back in the time to learn patterns and change H1 project!

From http://www.dofactory.com i read: "Decorators provide a flexible alternative to subclassing for extending functionality".

It is exactly what i need! Change behaviour of object on the fly!

After 2 hours of programming i got it!

public interface IArmyCar
{
 int Weight { get; }
 string Description { get; }
}

public class Hummer : IArmyCar
{
 public Hummer () { }

 public int Weight
 {
  get { return 5000; }
 }

 public string Description
 {
  get { return "Hummer car"; }
 }
}

public abstract class CarDecorator : IArmyCar
{
 protected int _weight;
 protected string _description;

 public int Weight
 {
  get { return _weight; }
 }

 public string Description
 {
  get { return _description; }
 }
}

public class CarShield : CarDecorator
{
 public CarShield(IArmyCar ac)
 {
  _weight = 1000 + ac.Weight;
  _description = ac.Description + " + shield";
 }
}

public class CarArmor : CarDecorator
{
 public CarArmor(IArmyCar ac)
 {
  _weight = 200 + ac.Weight;
  _description = ac.Description + " + MAG";
 }
}

public class CarExtraLights : CarDecorator
{
 public CarExtraLights(IArmyCar ac)
 {
  _weight = 20 + ac.Weight;
  _description = ac.Description + " + strong lights";
 }
}

public class CarExtraAccumulator : CarDecorator
{
 public CarExtraAccumulator(IArmyCar ac)
 {
  _weight = 100 + ac.Weight;
  _description = ac.Description + " + big accumulator";
 }
}

And using... We use it like build cabbage in our self: adding layer by layer, layer by layer.
Lets create H1with 3 accumulators and 3 guns:

IArmyCar ac = new Hummer();
Console.WriteLine("Weight: {0}, description: {1}", ac.Weight, ac.Description);

ac = new CarArmor(new CarArmor(new CarArmor(
 new CarExtraAccumulator(new CarExtraAccumulator(new CarExtraAccumulator(new Hummer()))))));
Console.WriteLine("Weight: {0}, description: {1}", ac.Weight, ac.Description);

Output:
Weight: 5000, description: Hummer car
Weight: 5900, description: Hummer car + big accumulator + big accumulator + big accumulator + MAG + MAG + MAG

Press any key to continue . . .

Moreover, i do not need care about changes in weight of other parts! If, for example, weight of accumulator changed, it not influence on other classes!!! All decorators are phlegmatic to each other!!!
That's all.


No comments:

Post a Comment