This is a creational pattern that provides a way to create an object without exposing the creation logic for the users of it. There are two well-known approaches that I will explain and show how and when you should use them.
Abstract Factory
This type of factory creates families of objects that are related to each other without specifying their concrete classes.
Example
Let’s say that we have a burger industry and we are able to create beef burgers and vegetarian burgers. The main idea is to have multiple clients that use my factory to create their burgers to sell them in their restaurants or markets.
The abstraction of BurgerFactory with two implementations
To create a beef burger they need to specify the fat percentage that will exist in the composition of the burger. For the vegetarian ones, they need to define the base vegetable that will be used to create them. These two requirements are part of our factory interface IBurgerFactory
.
The abstraction of Beef and Vegetarian Burger
We have our first client! A low cost burger company contact us to produce their burgers. The first thing is to define the specific classes that implement the previous interfaces.
public class LowCostVegetarianBurger : IVegetarianBurger
{
public string GetBaseVegetable() => "Chickpea";
}
public class LowCostBeefBurger : IBeefBurger
{
public int GetFatPercentage() => 5;
}
Now we need to create the concrete factory that implements the required interface.
public class LowCostBurgerFactory : IBurgerFactory
{
public LowCostBurgerFactory()
{
}
public IBeefBurger CreateBeefBurger()
{
return new LowCostBeefBurger();
}
public IVegetarianBurger CreateVegetarianBurger()
{
return new LowCostVegetarianBurger();
}
}
What if I don’t want some type of burger?
Just return null or an exception if you prefer. However, if this is a recurrent situation I advise you to create two different interfaces. One just for beef burger factories and another for vegetarian burgers factories. You will have IBeefBurgerFactory
and IVegetarianBurgerFactory
instead of IBurgerFactory.
How can use it?
IBurgerFactory lowCostFactory = new LowCostBurgerFactory();
_logger.LogInformation($"Beef Burger fat percentage: {lowCostFactory.CreateBeefBurger().GetFatPercentage()}");
_logger.LogInformation($"Vegetarian Burger base: {lowCostFactory.CreateVegetarianBurger().GetBaseVegetable()}");
This is a simple example to understand the purpose of this factory approach. You will certainly add more complexity to the creation of your objects.
Factory Method
This type of factory gives an interface to create objects in a superclass. However, allows that the subclasses decide the type of objects that will be created.
Example
We received a request from a mobile application that manages orders to restaurants. This app needs to show the restaurant menu to its users.
Let’s create a factory to be inherited by any restaurant in order to create their menus.
The most important thing to do is to define an abstract class that has a GetMenu()
method. This will be our factory method.
The abstract class MenuFactory with two implementations
Every restaurant factory must extend this class and implement the abstract method. Having a main abstract factory allows us to provide other methods to be used by any child class, the method GetPrice()
has that purpose.
public abstract class MenuFactory
{
public abstract Menu GetMenu();
public float GetPrice(string itemName)
{
Menu menu = GetMenu();
MenuItem menuItem = menu.Items.SingleOrDefault(m => m.Name == itemName);
if (menuItem != null)
return menuItem.Price;
return 0;
}
}
The following code block shows an example of a restaurant factory.
public class BurgerRestaurantFactory : MenuFactory
{
private readonly string _restaurantName;
public BurgerRestaurantFactory()
{
_restaurantName = "Burger Shot";
}
public override Menu GetMenu()
{
List<MenuItem> menuItems = new List<MenuItem>()
{
new MenuItem("Smash Burger", 3),
new MenuItem("Cheese Burger", 4),
new MenuItem("Bacon Cheese Burger", 5.49f),
new MenuItem("Smash Burger Combo", 5.99f)
};
return new Menu(_restaurantName, menuItems);
}
}
This is a simple example, this burger restaurant doesn’t add anything besides the menu and its name. However, we know that this class can be, and probably will, more complex than this.
How can use it?
MenuFactory burgerRestaurantFactory = new BurgerRestaurantFactory();
Menu burgerMenu = burgerRestaurantFactory.GetMenu();string itemName = "Bacon Cheese Burger";
float baconCheesePrice = burgerRestaurantFactory.GetPrice(itemName);
_logger.LogInformation($"The restaurant {burgerMenu.RestaurantName} has a {itemName} for {baconCheesePrice}.");
You can get the menu from that restaurant and use the method to get the price of some specific item.
Conclusion
This pattern is really good to have in your hand when you want to develop any type of system. Its focus is on the creation of more complex objects where you have to create multiple other objects that compose it. The two approaches that were presented are equally good options, you just need to understand the differences and choose the one that better fits your necessities.
Besides all that was said and explained I just need to add one thing: Yes I love burgers.