I didn’t write about .Net for a long time and it’s always
excited to be back.
I design and write
software solution for a living, and I do that with different technologies(PHP
Symfony, Python, Javascript…) but regardless of the technology I always try to
respect the S.O.L.I.D principals(http://en.wikipedia.org/wiki/SOLID_(object-oriented_design))
because violating one of those cause a great collateral damage in your code.
In this article I wanted to share one of my method to write
a respectful code (respectful to me, you can have another opinion) using .Net
technology.
Let us start with a case study:
The Straw hats pirates (the pirate crew in the one piece
anime) contact my company and they want to write a piece of software to
automate pizza preparation for them.
Sanji(the cook of the pirate crew) explained to me that in
order to prepare pizza we need to
1)
Prepare the Pizza Dough
2)
Prepare the sauce
3)
Add pizza sauce and pizza
ingredients to the pizza dough
4)
Put the pizza in the oven
My company assigned this job to me and I wrote this class.
namespace PizzaExample
{
public class PizzaMaker
{
public void
preparePizza()
{
this.MakeDough();
this.PrepareSauce();
this.AddIngredientsAndSauce();
this.PutInOven();
}
private void
PutInOven()
{
Console.WriteLine("Pizza
in Oven");
}
private void
AddIngredientsAndSauce()
{
Console.WriteLine("Adding
sauce, cheese,olives and meat");
}
private void
PrepareSauce()
{
Console.WriteLine("Making
sauce");
}
private void
MakeDough()
{
Console.WriteLine("Making
daugh");
}
}
}
I was happy about my work but only when Sanji come back to
me and told that not all of his crew members like the same ingredients and he
claimed that Nami is vegetarian and she don’t like to have meat, Chopper
another crew member don’t like olives. So I had to rewrite the method AddIngredientsAndSauce to full fit my costumers needs, I refactor it
into two methods and it look like this
now:
private void
AddIngredientsAndSauce(String membername="Any")
{
if(membername=="Nami")
Console.WriteLine("Adding cheese and olives");
else if(membername=="Chpper")
Console.WriteLine("Adding cheese and meat");
else
Console.WriteLine("Adding cheese,olives and meat");
}
private void
addSauce()
{
Console.WriteLine("Adding
sauce");
}
Things are good for sometimes, but after a while Sanji come
back to me claiming that the Mugiwara(straw hats) pirates have made an alliance
with some new big pirate named Law and this pirate want to have sea king fish(a
great imaginary fish in one piece) in his pizza.
So I have to add this into my code, the simple thing to do
is to go and add a new if statement in the method AddIngredients and it will
look like this:
private void
AddIngredients(String membername="Any")
{
if(membername=="Nami")
Console.WriteLine("Adding cheese and olives");
else if(membername=="Chpper")
Console.WriteLine("Adding cheese and meat");
else if(membername=="Chpper")
Console.WriteLine("Adding cheese,olives, meat and see king fish");
else
Console.WriteLine("Adding cheese,olives and meat");
}
But this a huge violation to the open close principal, the
open close principal suggest that a class should be open for extension and
close for changes(http://en.wikipedia.org/wiki/Open/closed_principle).
This is why we should use the power of object oriented
paradigm to overcome this problem. For that I’m going to use abstraction,
inheritance and polymorphism to come up with a good solution.
First thing to come to our mind is to create a class
responsible for making pizza for each member; each of those classes will
inherit from the mother class PizzaMaker. The PizzaMaker class will implement
the general implementation for the AddIngredient method and the each of the
other child class will override this method to add the suitable ingredients for
the member. So we have this new class diagram
This is the new AddIngredientPizza of the Pizzamaker class
protected virtual void AddIngredients()
{
Console.WriteLine("Adding cheese,olives and meat");
}
This is the NamiPizzaMaker class:
namespace PizzaExample
{
public class NamiPizzaMaker:PizzaMaker
{
protected override void AddIngredients()
{
Console.WriteLine("Adding
cheese and olives");
}
}
}
Now the question is how and where to know which class to
instantiate (PizzaMaker, NamiPizzaMaker, ChopperPizzaMaker or LawPizzaMaker).
Well the first option is to give that responsibility to higher layer (UI layer)
but this way you will create a dependency between layers and violate the
Dependency injection principal. So the best way is to go and create a factory
class that will be responsible for knowing how to instantiate the class.
The factory class
will look like that :
namespace PizzaExample
{
public class PizzaFactory
{
public static PizzaMaker getPizzaMaker(String
member)
{
if (member == "Nami")
return new
NamiPizzaMaker();
if (member == "Chopper")
return new
ChopperPizzaMaker();
if (member == "Law")
return new
LawPizzaMaker();
else
return new
PizzaMaker();
}
}
}
Well now we protected our AddIngredient Method from
violating the open close principals, also we reduce the dependency by adding
the factory class that will be responsible for creating the suitable class.
But still if we analyze the static method getPizzaMaker of
the PizzaFactory class we will notice couple of bad design smell
1)
We have to deal with
hardcoded strings “Nami”,”Low” and ”Chopper”(hardcoded strings are the root of
huge bugs and design problems)
2)
Each time we add new child
class of PizzaMaker we need to modify the getPizzaMaker method again that
violate the Open/Close principal
3)
The method is static(I will
explain why this is bad in another article about testing)
Well my solution to 1 and 2 problem is to create an xml
configuration file that knows for each member what class to instantiate.This
XML structure will hold xml elements, each element will have two properties,
one is the name of the member and two the name of the class to instantiate.
Example for the team member Nami we have the class
NamiPizzaMaker class that know how to make pizza for Nami(Single responsibility
principal), for the factory class to able to return an instance of the
NamiPizzaMaker class we have to add this xml element
<add member=”Nami”
classname=”NamiPizzaMaker”/>
The getPizzaMaker method of the PizzaFactory class will look
into the xml file, get all the elements, iterate them and when the name of the
member equals the “member” property in the xml element the method will get the “classname”
property from that same xml element as string and create an instance of the
class from string using the Activator.CreateInstance(“nameoftheclass”).
So each time we have a new pizza maker all you have to do is
to create a new class that is a subclass of PizzaMaker class and add a new xml
element to the configuration file, like that <add member=”new member”
classname=”NewSubsclassofPizzaMaker”/>.
To create this xml configuration file you have two
solutions, one solution will creating a new xml file called pizzamakers.xml and
all your xml elements in it, but with this solution will have at least 3 or 4 xml files that
should be maintained and this is not very productive.
Second solution will be using App.config or web.config, .Net
offer many build in classes in the namespace System.Configuration that helps
you get access to this configuration file. So I will use this file to put my
custom configuration and in the next section I will describe how to do it.
NB: All the previous concepts are general concepts that are
valid with any technology that use object oriented architecture, but the next
section is .Net specific section because we going to use .Net built in
features.
First step is to add a new configuration file to your
project and you should get a new file called App.config .
Second Step: Create a custom section
.Net framework has a
built in classes to access configuration file all those classes are under the
namespace System.Configuration.
And with the ConfigurationManager class you can access the elements
of any section in your configuration file including your custom sections using
the GetSection method.
ConfigurationManager.GetSection("nameofthesection");
But before add any custom sections to the xml config file and in
order for the ConfigurationManager class to be able to recognize your custom section you
need to create a subclass of the class ConfigurationSection.
I won’t get in the details of how to create a custom configuration
section, you can look into it in the internet.
We need this class in order to get a custom section called
PizzaMakerSection
namespace PizzaExample
{
public class PizzaMakersSection:ConfigurationSection
{
[ConfigurationProperty("",
IsRequired=true, IsDefaultCollection=true)]
public PizzaMakerElementCollection
PizzaMaker
{
get
{
return (PizzaMakerElementCollection)this[""];
}
set
{
this[""]
= value; }
}
}
public class PizzaMakerElementCollection: ConfigurationElementCollection{
protected override ConfigurationElement CreateNewElement()
{
return new PizzMakerElement();
}
protected override object
GetElementKey(ConfigurationElement
element)
{
return ((PizzMakerElement)element).className;
}
}
public class PizzMakerElement : ConfigurationElement
{
[ConfigurationProperty("member",IsRequired=true)]
public String
Memeber { get { return
(string)this["member"]; } set
{ this["member"]
= value; } }
[ConfigurationProperty("classname",IsRequired=true)]
public string
className
{
get
{
return (string)this["classname"];
}
set
{
this["classname"]
= value;
}
}
}
}
and the App.config file will look something like that:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section
name="pizzaMakerSection"
type="PizzaExample.PizzaMakersSection,PizzaExample"
/>
</configSections>
<pizzaMakerSection>
<add member="Nami" classname="PizzaExample.NamiPizzaMaker"/>
<add member="Chopper" classname="PizzaExample.ChopperPizzaMaker"/>
<add member="Law" classname="PizzaExample.LawPizzaMaker"/>
<add member="Any" classname="PizzaExample.PizzaMaker"/>
</pizzaMakerSection>
</configuration>
Third Step: Modify the factory to be able to read from App.config
file and instantiate the appropriate class
namespace PizzaExample
{
public class PizzaFactory
{
public static PizzaMaker getPizzaMaker(String
member)
{
PizzaMakersSection config = (PizzaMakersSection)ConfigurationManager.GetSection("pizzaMakerSection");
foreach (PizzMakerElement
element in config.PizzaMaker)
{
if(element.Memeber.Equals(member))
{
try
{
var type = Type.GetType(element.className);
PizzaMaker instance = (PizzaMaker)Activator.CreateInstance(type);
return instance;
}
catch (Exception
ex)
{
//log exception
return null;
}
}
}
return null;
}
}
}
Note that you can add PizzaMaker subclasses for each new
member without having to change anything in the code of the method
getPizzaMaker of the PizzaFactory class and without violating the open/close
principal, all you have to do is
1)create a subclass of the PizzaMaker class
2) Add a new xml element to the pizzaMakerSection in the App.config file that have the
member property and the classname property.
Conclusion
This is my
method to implement the factory pattern in .Net, I hope that was helping and if
you have any better methods or advices please share it with me.