The factory design pattern solves one big issue – your clients having to know about all the different concrete class types that they may need to instantiate. Or to put it another way your clients just tell the factory what they need built and the factory returns an instance of the required class. We explain this in more detail below and/or you can watch this video.
The Problem We're Solving Say you have a client application or class. In our example it's a simple console application. This console app is responsible for taking input from the user and then instantiating another class based on that input. The input provided creates an instance of ONE class from a range of similar classes. This simple console application looks like this:
All it does is… – accepts user input In this example we have a number of similar related classes that are makes of car. Each represented and created by a different (but similar) class. The logic for your client application could be something like this….
Then you create your car instances from classes like the one that follows. There would of course be an instance of this class for each of the different car types (e.g. Ford and Volvo).
And just for completeness you'd have the interfaced defined as follows…
Now, lets say we add a new car class. We add Saab. When you add a new car class then you update your client code with a new piece of logic.
Which is fine. Except, really, it's not fine. Why? Well what happens when you start adding multiple clients? Say you have multiple clients using this same logic. You implement additional clients that need to create instances of these car classes…. – API client – Web App client Now when add a new class you have to update all the client classes with this same logic.
All the clients end up with the same logic and you're updating that logic in three places now. Not only that but we're duplicating code. Also, this doesn't conform to one of the SOLID design principles that your… > “design should always be open for extension but closed for modifications“ The root of this problem is that all our clients have to know about all the classes. That's the problem the Factory Pattern solves.
What Is the Factory Pattern? The Factory Pattern is a design pattern that provides the following: – Allows us to choose different classes to instantiate at run time (dependent on the input from various clients) – Avoids having all clients having to know about all classes (so we don't have to update all our clients when things change) – Allows us to centralise the class creation code (so we don't end up with duplicated code) In short the factory pattern provides a solution that _encapsulates_ the object creation. With the factory class we get this layer of abstraction between the client and the concrete classes:
None of the clients are aware of the concrete classes. They just know that there is a factory that creates classes. All the clients need to do is feed in details about what they want and the factory class worries about everything else. User input, requests from an API or even an entry on a web page. These different clients just pass through a parameter that says what kind of class the object needs to be created from. The Factory class takes that parameter and then creates the object on behalf of the client.
How Does The Factory Pattern Work From a code point of view this makes life far simpler for our clients. A typical client can now do away with all that construction logic and so with something as simple as this.
Notice there that this client knows nothing about the different types of classes. The client just passes the parameter to the Factory Class and expects an object to be returned. All the complexity goes in the Factory Class. The factory class expects the 'parameter' from the client and then creates an instance of the class with the following:
In this example our CarFactory class has a static method, GetCar(), that is called with a string parameter Type. That string parameter is the type of car that the client needs the factory to create. This factory class is then responsible for taking that parameter and creating an instance of the required class. The created instance then passed back to the client with the 'return' statement. To do all of this the Factory Class has to know about all the different classes available. That's why all the conditional logic now resides here. > Note: The GetCar() method is static. The difference between a static method and a non-static method is that a static method belongs to the class itself. That is, you don't need an instance of the class in order to call this static method. Now look what happens when we want to add a new class. Say we add the new concrete class Toyota.
Once we've added that we need to update the conditional logic in our Factory class. So we extend that conditional logic with..
Sample TextNote that we didn't touch the clients. The clients have absolutely no knowledge of the concrete classes that are available. The client don't actually even know that the factory class now supports this new concrete class. No updating in multiple places. No duplicate code. When we added a new concrete class, we just updated the logic in one place: in the Factory.
Why Do We Need The Factory Pattern? When we started out we explained that without the Factory Pattern all our clients need to know about all the classes. This ultimately means we end up duplicating code and putting lots of logic in the clients. In a solution we needed these three things: When we place a Factory Class between our clients and our concrete classes we can extend our design by adding new concrete classes. We update our design by just updating the factory class. And we avoid having to modify the clients. All of this means we confirm to the age old principle that – our “design should be open for extension but closed for modification". Oh! And it’s called the Factory Pattern because…. well I’m hoping you can work that out on your own now that I’ve explained everything else 😉
– works out which type of class is required
– creates an instance of that class
class MainProgram
{
static void Main(string[] args)
{
// Client that gets data direct from console
String? inputFromConsole = null;
Console.WriteLine("Enter car type");
inputFromConsole = Console.ReadLine();
// Create new car based on client input
ICar MyNewCar = null;
if (inputFromConsole.ToLower().Equals("ford"))
{
MyNewCar = new CarFord();
}
else if (inputFromConsole.ToLower().Equals("volvo"))
{
MyNewCar = new CarVolvo();
}
Console.WriteLine("Car make is " + MyNewCar.GetCarModel() );
Console.WriteLine("Car has a top speed of " + MyNewCar.GetTopSpeed() );
Console.ReadKey();
}
}
internal class CarFord : ICar
{
private readonly int Speed;
public CarFord()
{
this.Speed = 90;
}
public int GetTopSpeed()
{
return this.Speed;
}
public string GetCarModel()
{
return "Ford";
}
}
internal interface ICar
{
String GetCarModel();
int GetTopSpeed();
}
if (inputFromConsole.ToLower().Equals("ford"))
{
MyNewCar = new CarFord();
}
else if (inputFromConsole.ToLower().Equals("volvo"))
{
MyNewCar = new CarVolvo();
}
else if (inputFromConsole.ToLower().Equals("saab")) // new condition added
{
MyNewCar = new CarSaab();
}
class MainProgram
{
static void Main(string[] args)
{
String? inputFromConsole = null;
Console.WriteLine("Enter car type");
inputFromConsole = Console.ReadLine();
ICar MyCar = CarFactory.GetCar(inputFromConsole);
Console.WriteLine("Car make is " + MyCar.CarModel());
Console.WriteLine("Car has a top speed of " + MyCar.TopSpeed() );
Console.ReadKey();
}
}
internal class CarFactory
{
public static ICar GetCar(String Type)
{
ICar ObjectType = null;
if (Type.ToLower().Equals("ford"))
{
ObjectType = new CarFord();
}
else if (Type.ToLower().Equals("volvo"))
{
ObjectType = new CarVolvo();
}
else if (Type.ToLower().Equals("saab"))
{
ObjectType = new CarSaab();
}
return ObjectType;
}
}
internal class CarToyota : ICar
{
private readonly int Speed;
public CarToyota()
{
this.Speed = 120;
}
public int TopSpeed()
{
return this.Speed;
}
public string CarModel()
{
return "Toyota";
}
}
if (Type.ToLower().Equals("ford"))
{
ObjectType = new CarFord();
}
else if (Type.ToLower().Equals("volvo"))
{
ObjectType = new CarVolvo();
}
else if (Type.ToLower().Equals("saab"))
{
ObjectType = new CarSaab();
}
else if (Type.ToLower().Equals("toyota")) // new condition added
{
ObjectType = new CarToyota();
}