Factory Method

Factory pattern

Another design pattern that separates creation of an object from its representation is Factory. The intent of the Factory is to separate creation from the decision of which object to create. Use it instead of if/else or switch blocks when deciding which class to instantiate.

“Define an interface for creating an object, but let subclasses decide which class to instantiate.”

Gang of Four

factory.jpg

Example breaking OCP

The method below uses a switch block to decide which class to instantiate. It breaks OCP. Why? Each time you need to add a new planet type (for example TerrestrialPlanet), you will have to modify this method!

        static IPlanet GetPlanet(string planetTypeName)
        {
            switch (planetTypeName)
            {
                case "IceGiant":
                    return new IceGiant();
                case "GasGiant":
                    return new GasGiant();
                case "TerrestrialPlanet":
                    return new TerrestrialPlanet();
                default:
                    return new NullPlanet();
            }
        }

Roles

The roles that take part in this pattern are:

  • FactoryBase
    – an abstract class or interface
    – contains the signature for the FactoryMethod which returns a ProductBase object
  • ConcreteFactory
    – concrete implementation of FactoryBase
    – overrides the FactoryMethod that returns a ConcreteProduct object
  • ProductBase:
    – a base class for ConcreteProducts or an interface
  • ConcreteProduct:
    – implementation of ProductBase
    – the objects created by factory methods

We have an abstract base class (or an interface) FactoryBase  at the highest level and derived ConcreteFactory classes.  The concrete factories provide ConcreteProducts back to the application.

UML_Factory_Method

Example

Let’s get rid of the switch block above using Factory method pattern. We have classes representing planet types of the Solar System: IceGiant, GasGiant and TerrestrialPlanet. Objects of these types will be our ConcreteProducts.

    public class GasGiant: IPlanet
    {
        public void RunAroudTheSun()
        {
            Console.WriteLine("The gas giant planet runs around the Sun");
        }

        public void Spin()
        {
            Console.WriteLine("The gas giant planet turns around its own
            axis.");
        }

        public override string ToString()
        {
            return "A gas giant is a giant planet composed mainly of hydrogen
            and helium. ";
        }
    }

We also have the IPlanet interface- the ProductBase. All the ConcreteProducts implement this interface.

    public interface IPlanet
    {
        void RunAroudTheSun();
        void Spin();
    }

Our FactoryBase will be the IPlanetFactory interface. It contains the signature for the CreatePlanet method which returns an IPlanet object.

    public interface IPlanetFactory
    {
        IPlanet CreatePlanet();
    }

Finally we create ConcreteFactories: GasGiantFactory, IceGiantFactory, etc. These classes implement the FactoryMethod called CreatePlanet().

    public class GasGiantFactory : IPlanetFactory
    {
        public IPlanet CreatePlanet()
        {
            return new GasGiant();
        }
    }

    public class IceGiantFactory : IPlanetFactory
    {
        public IPlanet CreatePlanet()
        {
           return new IceGiant();
        }
    }

factoryMethodSolutionExplorer

The decision which class is to be instantiated can be stored in Settings.

factory_settings
The client will retrieve the factory that is going to be used (GasGiantFactory, IceGiantFactory etc.) using LoadFactory method. Then it simply calls CreatePlanet method on this particular factory.

        static void Main(string[] args)
        {
            IPlanetFactory factory = LoadFactory();
            IPlanet planet = factory.CreatePlanet();

            planet.Spin();
            planet.RunAroudTheSun();
            Console.WriteLine(planet.ToString());
        }

        static IPlanetFactory LoadFactory()
        {
            string factoryName = Properties.Settings.Default.PlanetFactory;
            return Assembly.GetExecutingAssembly().CreateInstance(factoryName) as IPlanetFactory;
        }

The result of our program:

 planets

Summary

Factory pattern lets us add new classes without breaking the Open/Closed Principle. We can store which object to create somewhere outside of the program, for example in a database or in the configuration files.

The factory method pattern is a design pattern that allows for the creation of objects without specifying the type of object that is to be created in code. A factory class contains a method that allows determination of the created type at run-time.

References

Design Patterns Library – Pluralsight Online Training

https://www.codeproject.com/Articles/430590/Design-Patterns-of-Creational-Design-Patterns

Picture by Xenja Santarelli

Leave a comment