← All patterns
Creational

Factory

Hide which concrete class gets created behind a single method that returns an interface.

What Is It?

A Factory centralises object creation. Clients ask for "a Goblin" through a method like Create(EnemyType.Goblin) and receive an IEnemy back; they never write new Goblin() or reference the Goblin prefab themselves.

Because the client only knows the interface, you can add a Dragon, swap the Orc prefab or route creation through an object pool without touching any spawner, wave controller or debug tool that calls the factory.

In Unity the factory usually owns a lookup of prefabs — often a ScriptableObject with a list of (EnemyType, prefab) entries — and calls Instantiate plus any post-setup (stats, team, difficulty scaling) in one place.

When Is It Used?

Use it when several systems create the same family of objects: wave spawners, a level editor, cheat menus and save-game loading all need "make me enemy X".

It is valuable when creation involves more than Instantiate — injecting dependencies, applying difficulty multipliers, registering with a manager — and you do not want that logic copy-pasted.

Do not bother for one-off objects only ever created in one spot. A serialized prefab reference and Instantiate is clearer.

Interactive Demo

Click the controls and watch the objects collaborate. The console mirrors what the C# code below would log.

Code

Assets / Scripts / Creational/ Factory ›EnemySpawner.cs
using System.Collections.Generic;
using UnityEngine;

namespace Patterns.Creational.Factory
{
    /// <summary>
    /// Client. It knows enemy *types* and the factory, but never
    /// references Goblin, Orc or Dragon classes or prefabs.
    /// </summary>
    public class EnemySpawner : MonoBehaviour
    {
        [SerializeField] private EnemyFactory factory;
        [SerializeField] private Transform[] spawnPoints;

        private readonly List<IEnemy> alive = new();

        private void Update()
        {
            if (Input.GetKeyDown(KeyCode.Alpha1)) Spawn(EnemyType.Goblin);
            if (Input.GetKeyDown(KeyCode.Alpha2)) Spawn(EnemyType.Orc);
            if (Input.GetKeyDown(KeyCode.Alpha3)) Spawn(EnemyType.Dragon);

            if (Input.GetKeyDown(KeyCode.Space))
                foreach (IEnemy enemy in alive)
                    enemy.Attack();
        }

        private void Spawn(EnemyType type)
        {
            Transform point = spawnPoints[Random.Range(0, spawnPoints.Length)];
            IEnemy enemy = factory.Create(type, point.position, transform);

            if (enemy != null)
                alive.Add(enemy);
        }
    }
}
4 files · namespace Patterns.Creational.FactoryC# · UTF-8 · LF

Advantages & Disadvantages

+ Advantages

  • Clients depend on IEnemy, not concrete classes — low coupling.
  • One place to add new types, change prefabs or plug in pooling.
  • Data-driven: designers can register new prefabs in a ScriptableObject without code changes.

− Disadvantages

  • Adds an indirection layer; jumping to "where is this created?" takes an extra hop.
  • A giant switch in the factory can become its own maintenance hotspot if not data-driven.
  • Type keys (enums or strings) can drift out of sync with registered prefabs and fail at runtime.

Tips

  1. 01Back the factory with a Dictionary<EnemyType, Enemy> built once in Awake from a serialized list — lookups stay O(1) and Inspector-editable.
  2. 02Have Create return the interface (IEnemy) so callers cannot reach into concrete-type details.
  3. 03Combine with Object Pooling: the factory can fetch from a UnityEngine.Pool.ObjectPool<T> instead of instantiating each time.
  4. 04Log a clear error (and return null) when a key has no prefab, rather than throwing deep inside a spawner.
  5. 05For families of related objects (e.g. forest vs desert enemy sets) graduate to an Abstract Factory with one factory per theme.