Type Creational
Purpose Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.

Description

The Factory Method pattern provides an interface for creating objects in a superclass, but allows subclasses to alter the type of objects that will be created. Essentially, it involves creating object instances without having to specify the exact class of the object that will be created.


Overview

Factory Method
Product This is the interface for the type of objects the factory method creates. It declares methods that all concrete products must implement.
ConcreteProduct This class implements the Product interface. It defines a product that is to be created by the corresponding concrete factory. It's the concrete product of the factory method.
Creator This is the abstract class that declares the factory method, which returns a Product type object. It may also define a default implementation of the factory method that returns a default ConcreteProduct object. Additionally, it can call the factory method to create a Product object.
ConcreteCreator This class implements the Creator's factory method and returns an instance of a ConcreteProduct.

Implementation

// Product

public interface IEntity
{
    string Name { get; }
}

// ConcreteProducts

public class Orc : IEntity
{
    public string Name => "Uglúk";
}

public class Goblin : IEntity
{
    public string Name => "Grishnákh";
}

public class Balrog : IEntity
{
    public string Name => "Durin's Bane";
}

public class Kobold : IEntity
{
    public string Name => "Snark";
}

public class Minotaur : IEntity
{
    public string Name => "Jemain";
}

// Creator

public abstract class RandomEncounter
{
    // Factory Method
    
    public abstract IEntity CreateEnemy();

    public void Show()
    {
        var enemy = CreateEnemy();
        Console.WriteLine($"\tYou have encountered {enemy.Name}!");
    }
}

// ConcreteCreators

public class DungeonEncounter : RandomEncounter
{
    public override IEntity CreateEnemy()
    {
        return new Random().Next(10) switch
        {
            < 3 => new Balrog(),
            < 7 => new Goblin(),
            _ => new Orc(),
        };
    }
}

public class RuinsEncounter : RandomEncounter
{
    public override IEntity CreateEnemy()
    {
        return new Random().Next(10) switch
        {
            < 4 => new Minotaur(),
            _ => new Kobold()
        };
    }
}

// Client

public static class FactoryMethodDemo
{
    public static void Execute()
    {
        Console.WriteLine("You are in the dungeon!");
        
        RandomEncounter encounter = new DungeonEncounter();
        
        encounter.Show();
        encounter.Show();
        encounter.Show();
        encounter.Show();
        encounter.Show();
        
        Console.WriteLine("You are in the ruined citadel!");
        
        encounter = new RuinsEncounter();
        
        encounter.Show();
        encounter.Show();
        encounter.Show();
        encounter.Show();
        encounter.Show();
    }
}

// outputs:

// You are in the dungeon!
//         You have encountered Grishnákh!
//         You have encountered Durin's Bane!
//         You have encountered Grishnákh!
//         You have encountered Durin's Bane!
//         You have encountered Uglúk!
// You are in the ruined citadel!
//         You have encountered Jemain!
//         You have encountered Jemain!
//         You have encountered Snark!
//         You have encountered Snark!
//         You have encountered Jemain!

Notes

Many implementations of the Factory Method pattern fold the Creator and ConcreteCreator into the same class. This is often simpler, but differs from the classic GoF structure in which object creation is delegated to subclasses. Which approach is appropriate depends on where variation is expected and on the needs of the application.

More generally, understanding the problem a design pattern is intended to solve makes it easier to apply it judiciously and in proportion to the needs of the system. Design patterns are best treated as flexible guidelines rather than rigid formulas.

Factory Method can also work naturally with encapsulated behaviour in the Creator. A ConcreteCreator can access the protected state of its Creator, while extending the Creator with behaviour specific to the particular Product it creates. This can make Factory Method useful when product creation needs to vary alongside specialised creator behaviour.

The example above was deliberately minimal for clarity, but in practice a ConcreteCreator could contain all the logic needed to manage a random encounter in a specific environment, taking into account factors such as available lighting, terrain, or the effectiveness of particular actions.


Summary

The Factory Method pattern is especially useful when a class cannot anticipate in advance which concrete objects it will need to create. Instead of hard-coding those choices, it delegates object creation to subclasses, allowing the specific product type to vary without changing the base class's general logic.

It is also valuable when a base class needs to rely on object creation without depending directly on concrete classes. By calling a factory method supplied by a subclass, the base class can retain its general business logic while allowing subclasses to decide which specific products to provide.

Factory Method also serves as a useful extension point. A base class can define the overall structure of an operation, while subclasses vary the products involved by overriding the factory method. This helps separate core behaviour from instantiation concerns, which can improve flexibility, maintainability, and adherence to principles such as Single Responsibility and Open/Closed.

Overall, Factory Method is a simple but powerful way to make code more adaptable to change. It is particularly common in frameworks and extensible systems, where objects must be created dynamically and their exact types may not be known until runtime.

For more information, please see here.