Factory Method Design Pattern

Factory Method Design Pattern

In today’s episode of Software Design Patterns you will learn everything about the Factory Method Design Pattern. The Factory Method is a creational pattern. That means that the pattern has something to do with object creation. There are different types of design patterns. The other types are structural and behavioral. Those types will have a dedicated blog post in the future.

For reference – the last article covered the Singleton Design Pattern – which is also a creational pattern. All the examples, source code and unit tests are in this GitHub repository.

What’s the purpose of the Factory Method Design Pattern?

The Factory Method is a very common pattern, but strongly misused. I have seen many examples where methods were named factory, but did something completely different. The quote below – taken from the Gang of Four – is very precise about the Factory Method.

The Factory Method Design Pattern defines an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses. [1]

Don’t fall into the trap and use it for everything. If you are not sure about when to use, you probably don’t need it anyway.

How does the Factory Method looks like?

In the picture below you find the Unified Modeling Language (UML) diagram.

Factory Method Design Pattern UML

As mentioned at the beginning you find all the code examples in a working project on GitHub.

public interface ICar
{
	string GetName();
	void Accelerate();
	void Break();
}
public class ElectricCar : ICar
{
	public int Speed { get; private set; }

	public string GetName()
	{
		return "Electric";
	}

	public void Accelerate()
	{
		Speed = Speed + 2;
	}

	public void Break()
	{
		if (Speed > 0)
		{
			Speed--;
		}
	}
}
public class PetrolCar : ICar
{
	public int Speed { get; private set; }

	public string GetName()
	{
		return "Petrol";
	}

	public void Accelerate()
	{
		Speed++;
	}

	public void Break()
	{
		if (Speed > 0)
		{
			Speed--;
		}
	}
}

In this example we have an interface ICar.cs, which provides basic car operations like accelerate and break. In addition to that we have an GetName() method, were the concrete implementation should return the actual name of the car.

public class CarFactory
{
	public ICar CreateCar(string type)
	{
		if (string.Equals(type, "Petrol", StringComparison.OrdinalIgnoreCase))
		{
			return new PetrolCar();
		}

		if (string.Equals(type, "Electric", StringComparison.OrdinalIgnoreCase))
		{
			return new ElectricCar();
		}

		throw new NotSupportedException($"Car Type '{type}' is not supported");
	}
}

Our Factory, called CarFactory.cs, is responsible for creating instances of concrete cars. We provide a dedicated CreateCar(string type) method, where the desired type can be passed as an argument. The method maps the type to a concrete implementation. This can be done via multiple if clauses or a switch statement. Anyway, if there is no matching type we throw an NotSupportedException to make it clear that the factory does not support the desired type.

To see the proper use of the CarFactory look into the test project.

public class CarFactoryTest
{
	[Theory]
	[InlineData("Electric")]
	[InlineData("electric")]
	[InlineData("eLeCtriC")]
	public void ShouldCreateElectricCarInstance(string type)
	{
		var carFactory = new CarFactory();
		var car = carFactory.CreateCar(type);

		car.Should().BeOfType<ElectricCar>();
		car.GetName().Should().Be("Electric");
	}

	[Theory]
	[InlineData("Petrol")]
	[InlineData("petrol")]
	[InlineData("pEtRoL")]
	public void ShouldCreatePetrolCarInstance(string type)
	{
		var carFactory = new CarFactory();
		var car = carFactory.CreateCar(type);

		car.Should().BeOfType<PetrolCar>();
		car.GetName().Should().Be("Petrol");
	}

	[Theory]
	[InlineData("hans")]
	[InlineData("car")]
	[InlineData("electricpetrol")]
	public void ShouldThrowNotSupportedExceptionWhenTypeIsInvalid(string type)
	{
		var carFactory = new CarFactory();
		Action getCar = () => carFactory.CreateCar(type);

		getCar.Should().ThrowExactly<NotSupportedException>();
	}
}

If you have any questions or comments feel free to leave them below.

That’s it. Happy coding as always :).

References

[1] -Design patterns, software engineering, object-oriented programming