If you have read my previous blog about why we should use design patterns, you should already know how important they are. The design pattern I will be discussing is the simple factory and factory method patterns which belong to the creational design pattern category.

Simple Factory

This design pattern works by creating products from one "concrete" factory so that we don't have to use a new keyword whenever we want to create a certain object. The factory creates new products dynamically in the runtime without specifying what product to generate.

It solves a lot of problems by encapsulating product creation. We can dynamically create different products without knowing the concrete definitions. This reduces coupling in clients' code. The design for a simple factory is as follows:

Simple Factory Design

Example of a Simple Factory

Let's see an example of how a simple factory works:

  1. First, we create an abstract product. In my example below, let's call it IPlayer.
  2. Concrete products inherit from the abstract product. In my case, Knight, Mage, GrandMage, Lancer inherits from IPlayer.
  3. Next, we create a concrete factory PlayerFactory that creates different products according to the type provided. In my case, it creates the PlayerType.
  4. We now create new products from PlayerFactory rather than using new keywords everywhere.

The example using the pattern described is shown below.

using System;

namespace DesignPattern.Patterns.FactoryPattern.SimpleFactory
{
    public interface IPlayer
    {
        void StartAttacking();
    }
    
    public class Knight : IPlayer
    {
        public void StartAttacking()
        {
            Console.WriteLine("Young knight is using his sword to slash enemies");
        }
    }
    
    public class Mage : IPlayer
    {
        public void StartAttacking()
        {
            Console.WriteLine("Ice mage is using her ice attack to freeze enemies");
        }
    }
    
    public class GrandMage : IPlayer
    {
        public void StartAttacking()
        {
            Console.WriteLine("I have mastered the power of fire");
        }
    }
    
    public class Lancer : IPlayer
    {
        public void StartAttacking()
        {
            Console.WriteLine("I have very long spear");
        }
    }
}

namespace DesignPattern.Patterns.FactoryPattern.SimpleFactory
{
    public static class PlayerFactory
    {
        public static IPlayer CreatePlayer(PlayerType playerType)
        {
            switch (playerType)
            {
                case PlayerType.Knight:
                    return new Knight();
                case PlayerType.Mage:
                    return new Mage();
                default:
                    return null;
            }
        }
    }
    public enum PlayerType
    {
        Knight , Mage 
    }
}

namespace DesignPattern.Patterns.FactoryPattern.SimpleFactory
{
    public class SimpleFactoryDemo
    {
        public void Run()
        {
            IPlayer player = PlayerFactory.CreatePlayer(PlayerType.Mage);
            player.StartAttacking();

            player = PlayerFactory.CreatePlayer(PlayerType.Knight);
            player.StartAttacking();
            
        }
        
    }
}

namespace Patterns
{
    internal class Program
    {
        public static void Main(string[] args)
        {
            SimpleFactoryDemo simpleFactoryDemo = new SimpleFactoryDemo();
            simpleFactoryDemo.Run();
        }
    }
}

// Above program outputs following
// Ice mage is using her ice attack to freeze enemies
// Young knight is using his sword to slash enemies
Example of Simple Factory

Factory Method

This design pattern works by creating products from a factory without defining what object type to create. The main difference between this pattern and a simple factory is that we create an abstract factory rather than a concrete implementation of the factory in this pattern.

The concrete factory will then override the creation process and creates a product with its own implementation. The client doesn't have to worry about the actual implementation of how products are created. The design for the Factory Method is as follows:

Factory Method Design

Example

Let's see an example of a factory method design implementation:

  1. Create an abstract product. In my example below, let's call it IAbility.
  2. Make products inherited from IAbility. I have implemented FireAbility, IceAbility,SlashAbility, and ThrustAbility.
  3. Make an abstract factory. In my case, I'm using IAbilityFactory. This is where the factory method differs from the simple factory. Instead of the concrete implementation of a factory, we now depend on IAbilityFactory.
  4. Create concrete factories that implement IAbilityFactory. Here, these are called ElementalAbilityFactory and PhysicalAbilityFactory.
  5. Now the client can just pass abilityType and get different products from different factories.

The example using the pattern described is shown below.

using System;

namespace DesignPattern.Patterns.FactoryPattern.Factory_Method
{
    public interface IAbilityFactory
    {
        IAbility CreateAbility(AbilityType abilityType);
    }
    
    public class ElementalAbilityFactory : IAbilityFactory
    {
        public IAbility CreateAbility(AbilityType abilityType)
        {
            switch (abilityType)
            {
                case AbilityType.fire:
                    return new FireAbility();
                case AbilityType.ice:
                    return new IceAbility();
                default:
                    return null;
            }
        }
    }
    
    public class PhysicalAbilityFactory : IAbilityFactory
    {
        public IAbility CreateAbility(AbilityType abilityType)
        {
            switch (abilityType)
            {
                case AbilityType.slash:
                    return new SlashAbility();
                case AbilityType.thrust:
                    return new ThrustAbility();
                default:
                    return null;
            }
        }
    }
}

namespace DesignPattern.Patterns.FactoryPattern.Factory_Method
{
    public class FactoryMethodDemo
    {
        public void Run()
        {
            IAbilityFactory abilityFactory = new ElementalAbilityFactory();

            IAbility ability = abilityFactory.CreateAbility(AbilityType.fire);
            ability.UseAbility();
            
            ability = abilityFactory.CreateAbility(AbilityType.ice);
            ability.UseAbility();

            abilityFactory = new PhysicalAbilityFactory();
            ability = abilityFactory.CreateAbility(AbilityType.thrust);
            ability.UseAbility();
            
            ability = abilityFactory.CreateAbility(AbilityType.slash);
            ability.UseAbility();
        }
    }
}


using System;

namespace DesignPattern.Patterns.FactoryPattern.Factory_Method
{
    public interface IAbility
    {
         void UseAbility();
    }
    
    public enum AbilityType
    {
        fire , ice, slash , thrust
    }
    

    public class IceAbility :IAbility
    {
        public void UseAbility()
        {
            Console.WriteLine("Ice ability used");
        }
    }
    public class FireAbility :IAbility
    {
        public void UseAbility()
        {
            Console.WriteLine("Fire ability used");
        }
    }
    public class SlashAbility :IAbility
    {
        public void UseAbility()
        {
            Console.WriteLine("slash attack used");
        }
    }
    
    public class ThrustAbility : IAbility
    {
        public void UseAbility()
        {
            Console.WriteLine("thrust attack used");
        }
    }
}

namespace Patterns
{
    internal class Program
    {
        public static void Main(string[] args)
        {

            FactoryMethodDemo factoryMethodDemo = new FactoryMethodDemo();
            factoryMethodDemo.Run();
        }
    }
}

// Above program outputs following
// Fire ability used
// Ice ability used
// thrust attack used
// slash attack used
Factory Method Example

Abstract Factory

This design pattern works by creating multiple products that belong to the same families using a factory. A single factory is responsible for creating families of the related object without specifying their concrete subclass. One factory should produce at least 2 products that are somehow related to each other. The Design diagram for the abstract factory is shown below.

Abstract Factory Design

Example

Let's see an example of a factory method design implementation:

  1. We need to first create at least 2 abstract products that are somehow connected. In my case, they are IPlayer and IAbility.
  2. We then create new concrete products from IPlayer and IAbility. I have fire, ice, slash, and thrust that inherit from IAbility, and Mage, GrandMage, Knight, Lancer that inherit from IPlayer.
  3. Since abstract factory deals with families of objects, it needs to be able to create at least 2 products. In my case, it creates player and ability.
  4. We need to make a concrete implementation of the factory. I have MagicClassFactory and MeleeClassFactory that inherits from ICharacterClassFactory.
  5. Finally, we create ability and player by passing abilityType as a parameter when creating these products.

The example using the pattern described is shown below.

using System;
using DesignPattern.Patterns.FactoryPattern.Factory_Method;
using DesignPattern.Patterns.FactoryPattern.SimpleFactory;

namespace DesignPattern.Patterns.FactoryPattern.Abstract_Factory
{
    public interface ICharacterClassFactory
    {
        IPlayer CreatePlayer(AbilityType type);
        IAbility CreateAbility(AbilityType type);
    }

    public class MagicClassFactory : ICharacterClassFactory
    {
        public  IPlayer CreatePlayer(AbilityType ability)
        {
            switch (ability)
            {
                case AbilityType.ice:
                    return new Mage();
                case AbilityType.fire:
                    return new GrandMage();
                default:
                    return null;
            }
        }

        public IAbility CreateAbility(AbilityType abilityType)
        {
            switch (abilityType)
            {
                case AbilityType.fire:
                    return new FireAbility();
                case AbilityType.ice:
                    return new IceAbility();
                default:
                    return null;
            }
        }
    }

    public class MeleeClassFactory : ICharacterClassFactory
    {
        public  IPlayer CreatePlayer(AbilityType ability)
        {
            switch (ability)
            {
                case AbilityType.slash:
                    return new Knight();
                case AbilityType.thrust:
                    return new Lancer();
                default:
                    return null;
            }
        }

        public IAbility CreateAbility(AbilityType abilityType)
        {
            switch (abilityType)
            {
                case AbilityType.slash:
                    return new SlashAbility();
                case AbilityType.thrust:
                    return new ThrustAbility();
                default:
                    return null;
            }
        }
    }
}

using DesignPattern.Patterns.FactoryPattern.Factory_Method;
using DesignPattern.Patterns.FactoryPattern.SimpleFactory;

namespace DesignPattern.Patterns.FactoryPattern.Abstract_Factory
{
    public class AbstractFactoryDemo
    {
        public void Run()
        {
            ICharacterClassFactory characterClassFactory = new MeleeClassFactory();

            IPlayer player = null;
            IAbility ability = null;
            
            player = characterClassFactory.CreatePlayer(AbilityType.thrust);
            ability = characterClassFactory.CreateAbility(AbilityType.thrust);
            player.StartAttacking();
            ability.UseAbility();
            
            player = characterClassFactory.CreatePlayer(AbilityType.slash);
            ability = characterClassFactory.CreateAbility(AbilityType.slash);
            player.StartAttacking();
            ability.UseAbility();

            characterClassFactory = new MagicClassFactory();
            
            
            player =characterClassFactory.CreatePlayer(AbilityType.fire);
            ability = characterClassFactory.CreateAbility(AbilityType.fire);
            player.StartAttacking();
            ability.UseAbility();
            
            player = characterClassFactory.CreatePlayer(AbilityType.ice);
            ability = characterClassFactory.CreateAbility(AbilityType.ice);
            player.StartAttacking();
            ability.UseAbility();
            
            
        }
    }
}

namespace Patterns
{
    internal class Program
    {
        public static void Main(string[] args)
        {
            AbstractFactoryDemo abstractFactoryDemo = new AbstractFactoryDemo();
            abstractFactoryDemo.Run();
        }
    }
}

// Above program outputs following
// I have very long spear
// thrust attack used
// Young knight is using his sword to slash enemies
// slash attack used
// I have mastered the power of fire
// Fire ability used
// Ice mage is using her ice attack to freeze enemies
// Ice ability used
Abstract Factory Example
ℹ️
All factories created in my example above always return a product by creating a new object. This is not always true, as you can also use object pooling and provide a product from the pool.

All the above patterns are categorized under creation patterns. These design patterns deal with creating objects.

Hope you learned something new from this. I will be coming up with more design patterns soon.