What are the main differences between these design patterns? In which situation can one pattern be better than the other?
What are the main differences between these design patterns? In which situation can one pattern be better than the other?
All patterns are often abused. They should only be used when it really is a solution to a real problem.
I've seen factories being used where they did not need them, where code could construct objects without intermediaries.
It's interesting when you're not sure what kind of object to use. Usually a factory class is created to manage this. In it would have a method that returns the object of the correct type according to some criterion. There are cases that this method can even be static and dispense instanciar something just to consume the factory. It even gives to do without a class. Of course, each case has both an advantage and a disadvantage.
A common way is to make the selection by a switch
, by looking for a string or something external. This is often an error when receiving an enumeration. If you know what to do in development time, then instantiate what you need right away. Of course there are legitimate cases for its use, for example the enumeration has been generated by an external data.
public class PizzaFactory {
public Pizza CreatePizza(String sabor) {
if (sabor == "Portuguesa")
return new Portuguesa();
if (sabor == "Calabreza")
return new Calabreza();
return null;
}
...
}
All flavors are derived from Pizza
. So where you need Pizza
would call it like this:
PizzaFactory fabricaDePizza = new PizzaFactory()
Pizza pizza = fabricaDePizza.createPizza(textBoxPizza);
The text can come from several places, the one I put is obvious, but probably inappropriate in real application.
Some people prefer to do static class, so you do not have to instantiate anything just to create what you really want.
Of course this has a maintenance problem. If you create a new flavor you have to tinker with the factory class to add it. This is not the end of the world in most applications. But it is in some.
The solution is to create a mechanism for registering the flavor classes in the factory class or to reflect on well-standardized cases. The first one would look something like this:
class static PizzaFactory {
private Dictionary<string, Pizza> pizzas = new Dictionary<string, Pizza>();
public static void registerPizza(String nome, Pizza pizza) {
pizzas[nome] = pizza;
}
public static Pizza createPizza(String nome){
return pizzas[nome].createPizza();
}
}
Then each taste class will register and have a way to instantiate. The factory class does not need to know each of the flavors.
class Portuguesa : Pizza {
...
static Pizza() {
PizzaFactory.registerPizza("Portuguesa", new Portuguesa());
}
public override Pizza createPizza() {
return new Portuguesa();
}
...
}
Obviously this is not the fullest and most robust code possible.
This solution is suitable in many cases.
This pattern is used in case you need to build instances in a specific way, do something more that the construction would normally do, ie you do not need to be aware of how to consume the object's construction and a specific form. Obviously you can have several different manufacturing lines for the same class, allowing for flexibility.
This is done through subclass. Note that each subclass can have its own implementation, it does not matter to the consumer of that pattern what the object is and how it was built, it just wants the result.
Just like the previous pattern is a way of being able to build new objects without knowing all the existing ones. A maintenance that creates a new type can be consumed without any part of the code other than the new type being written.
Note that the class to be fabricated also need not be aware that it will have a factory.
public abstract class PizzaFactory {
protected abstract Pizza Make();
public Pizza GetPizza() { //este é o método de fábrica
return this.Make();
}
}
public class PortuguesaFactory : PizzaFactory {
protected override Pizza Make() {
Portuguesa pizza = new Portuguesa();
pizza.GetHam();
pizza.GetPea();
pizza.GetOnion();
pizza.GetEgg();
return (Pizza)pizza;
}
}
In this way you can call a pizza that already makes you pick up the specific ingredients without having to worry about it. In another flavor the ingredients are others, but it is not your problem, the factory of each one knows what to do, just call the GetPizza()
that goes for all flavors. Something like this:
PizzaFactory fabrica = new PortuguesaFactory();
Pizza pizza = fabrica.getPizza();
There are a few different versions of how the right way to do it is. It is common for people to claim that the way they like it is the right one. Honestly I do not know which one is correct when there are so many sources. This is one way to implement that works.
Essentially it is a factory of factories. It is used when we have an array of items that relate to and need an abstract creation that meets any item. It allows you to create several settings with related items. Thus it is possible to mount new options independently without being aware of the other.
In this more complex example we create different types of pizzas:
public interface IPizzaIngredientFactory {
public Massa createMassa();
public Molho createMolho();
public Queijo createQueijo();
public Acessorios[] createAcessorios();
public Carne createCarne();
}
public class PremiumPizzaIngredientFactory : IPizzaIngredientFactory {
public Massa createMassa() {
return new MassaFinaCrocante();
}
public Molho createMolho() {
return new MolhoTomateEspecial();
}
public Queijo createQueijo() {
return new QueijoMussarela();
}
public Acessorios[] createAcessorios() {
return new Acessorios { new MiniAzeitona(), new CebolaRoxa() };
}
public Carne createCarne() {
return new CarneRalada();
}
}
public abstract class Pizza {
public string Nome;
protected IMassa Massa;
protected IMolho Molho;
protected IAcessorios[] Acessorios;
protected IQueijo Queijo;
protected ICarne Carne;
public abstract void Prepare();
}
public class Calabreza : Pizza {
IPizzaIngredientFactory ingredientFactory;
public CheesePizza(IPizzaIngredientFactory ingredientFactory) {
ingredientFactory = ingredientFactory;
}
public override void Prepare() {
Massa = ingredientFactory.CreateMassa();
Molho = ingredientFactory.CreateMolho();
Queijo = ingredientFactory.CreateQueijo();
Carne = ingredientFactory.CreateCarne();
}
}
public class PremiumPizza {
protected override Pizza CreatePizza(string sabor) {
Pizza pizza;
IPizzaIngredientFactory ingredientFactory = new PremiumPizzaIngredientFactory();
switch (sabor) {
case "Portuguesa":
pizza = new Portuguesa(ingredientFactory);
pizza.Nome = "Pizza Premium Portuguesa";
break;
case "Calabreza":
pizza = new Calabreza(ingredientFactory);
pizza.Nome = "Pizza Premium Calabreza";
break;
}
return pizza;
}
}
This answer is a junction of Vinícius Thiengo's articles on Simple Factory , the Factory Method and Abstract Factory .
In general, all Factory (Factory Factory, Factory Factory, and Factory Factory) encapsulate the creation of objects.
Simple Factory
Simple Factory allows interfaces to create objects without exposing logical creation to the client.
Simple Factory is a good starting point for separating object creation from its use, few classes are created, and the pattern structure is very simple. If your context allows you to isolate the way objects are created and you have to deal with only one object type, Simple Factory is a great way to solve the problem.
Although simple, there are situations where using the Simple Factory pattern does not help much. A very clear sign that the pattern is not being effective is when the factory class begins to grow and have several methods to create the same products in different ways. This may be a good time to apply other factory defaults.
Factory Method
Factory Method defines an interface to create an object, but lets subclasses decide which class to instantiate.
Factory Method can be used when a class can not anticipate the class of objects it creates or when the class wants its subclasses to specify the objects they create. It can also be used when classes delegate responsibility to one of several subclasses, and you want to find the knowledge of which auxiliary subclass is delegated.
Abstract Factory
Abstract Factory provides interface to create families of related or dependent objects without specifying their concrete classes.
Abstract Factory can be used when a system must be independent of how its products are created, composed or represented, and the system must be configured as a product of a multi-product family. It can also be used when a family of objects is designed to be used together, and you need to ensure this restriction and when you want to provide a class library and want to reveal only its interfaces, not its implementations.
Remember that there is not a better version of Factory, each one fits more optimally into specific software design issues. And that goes for any design pattern.
References: