我们已经把创建型模式中的“大魔王”们(单例、工厂、抽象工厂)聊完了,现在迎来创建型模式里的最后一位高手——建造者模式(Builder Pattern)

如果说工厂模式关注的是“选哪种产品来生产”,那么建造者模式关注的则是“如何一步一步组装一个极其复杂的产品”。

在 Unity 开发中,它是解决“构造函数爆炸”和“对象参数太多”的绝对神器!

💡 为什么要用建造者模式?

假设你正在做一款 RPG 游戏,需要动态生成各种各样的 NPC 或敌人。一个复杂的敌人可能包含以下属性:名字、血量、速度、攻击力、防御力、武器预制体、护甲类型、是否是 Boss、AI 脚本类型……

新手的灾难写法(构造函数爆炸):
你可能会在 C# 中写一个拥有十几个参数的构造函数:

public Enemy(string name, int hp, float speed, int atk, int def, GameObject weapon, bool isBoss, string aiType) { ... }

这会导致两个巨大的痛苦:

  1. 极难阅读: 传参时一长串数字和字符串 new Enemy("恶魔", 5000, 2.5f, 150, 80, swordPrefab, true, "Aggressive"),根本分不清哪个数字代表什么。
  2. 缺乏灵活性: 如果你只想创建一个普通的史莱姆,绝大多数参数都是默认值,你却不得不传一堆无意义的参数(或者写十几个不同版本的重载构造函数)。

建造者模式的优雅解法:
把复杂对象的构建过程抽离出来,允许你通过链式调用(Fluent API),像拼乐高积木一样,需要什么属性就加什么属性,最后大喊一声 Build() 搞定!


一、 🌟 Unity 中最实用的链式建造者 (Fluent Builder)

这是 C# 开发者最喜欢的写法,利用 return this; 实现丝滑的连写。

1. 复杂的产品:敌人数据类 (EnemyCharacter)

using UnityEngine;

public class EnemyCharacter
{
    // 拥有大量的属性
    public string Name { get; set; }
    public int Health { get; set; }
    public float MoveSpeed { get; set; }
    public int AttackPower { get; set; }
    public GameObject WeaponPrefab { get; set; }
    public bool IsBoss { get; set; }

    // 打印一下信息,验证组装结果
    public void Introduce()
    {
        Debug.Log($"生成成功 -> 名字: {Name}, 血量: {Health}, 速度: {MoveSpeed}, 攻击力: {AttackPower}, 是否是Boss: {IsBoss}");
    }
}

2. 核心:建造者类 (EnemyBuilder)

using UnityEngine;

public class EnemyBuilder
{
    // 内部持有一个正在构建的产品实例
    private EnemyCharacter _enemy = new EnemyCharacter();

    // 构造函数中可以设置一些默认值
    public EnemyBuilder()
    {
        _enemy.Health = 100;
        _enemy.MoveSpeed = 3.5f;
        _enemy.AttackPower = 10;
        _enemy.IsBoss = false;
    }

    // 关键点:每个设置属性的方法都返回 Builder 自己 (this)
    public EnemyBuilder SetName(string name)
    {
        _enemy.Name = name;
        return this; 
    }

    public EnemyBuilder SetHealth(int hp)
    {
        _enemy.Health = hp;
        return this;
    }

    public EnemyBuilder SetSpeed(float speed)
    {
        _enemy.MoveSpeed = speed;
        return this;
    }

    public EnemyBuilder SetAttack(int atk)
    {
        _enemy.AttackPower = atk;
        return this;
    }

    public EnemyBuilder SetWeapon(GameObject weapon)
    {
        _enemy.WeaponPrefab = weapon;
        return this;
    }

    public EnemyBuilder MarkAsBoss()
    {
        _enemy.IsBoss = true;
        _enemy.Health *= 5; // Boss 血量翻 5 倍
        return this;
    }

    // 最终的组装方法:把造好的产品交出去
    public EnemyCharacter Build()
    {
        // 可以在这里进行最后的合法性校验
        if (string.IsNullOrEmpty(_enemy.Name))
        {
            _enemy.Name = "未命名怪物";
        }
        
        return _enemy;
    }
}

3. 在 Unity 中使用(优雅得让人感动)

using UnityEngine;

public class EnemySpawner : MonoBehaviour
{
    public GameObject goldenSwordPrefab; // 在面板拖入的武器

    void Start()
    {
        // 场景 1:我们需要造一只普通的史莱姆(很多属性用默认的就行)
        EnemyCharacter slime = new EnemyBuilder()
            .SetName("小史莱姆")
            .SetSpeed(1.5f)
            .Build(); // 完工!
            
        slime.Introduce();


        // 场景 2:我们需要造一个极其强悍的终极大 Boss
        EnemyCharacter boss = new EnemyBuilder()
            .SetName("不灭魔王")
            .SetHealth(2000)
            .SetAttack(999)
            .SetWeapon(goldenSwordPrefab)
            .MarkAsBoss() // 触发 Boss 逻辑
            .Build(); // 完工!

        boss.Introduce();
    }
}


二、 进阶:引入“指挥者” (Director) —— 固定配方

在标准的 GoF 建造者模式中,还有一个可选的角色叫 指挥者(Director)。它的作用是封装常用的“组装配方”

如果你的游戏里经常需要批量生产同一种配置的怪物(比如“标准的远程弓箭手”),你不需要每次都自己去连一串 Set,你可以把这个配方交给 Director。

// 指挥者:负责规定建造的步骤和配方
public class EnemyDirector
{
    // 传入一个建造者,指挥它去按照特定配方建造
    public EnemyCharacter ConstructStandardGoblin(EnemyBuilder builder)
    {
        return builder
            .SetName("哥布林步兵")
            .SetHealth(80)
            .SetSpeed(4.5f)
            .SetAttack(15)
            .Build();
    }

    public EnemyCharacter ConstructEliteKnight(EnemyBuilder builder, GameObject coolSword)
    {
        return builder
            .SetName("精英圣骑士")
            .SetHealth(500)
            .SetSpeed(3.0f)
            .SetAttack(50)
            .SetWeapon(coolSword)
            .Build();
    }
}

使用 Director:

EnemyBuilder builder = new EnemyBuilder();
EnemyDirector director = new EnemyDirector();

// 直接问导演要一个标准哥布林
EnemyCharacter goblin = director.ConstructStandardGoblin(builder);


💡 建造者模式在 Unity 中的其他神级应用

除了用来配置复杂的数据,建造者模式在 Unity 的以下领域简直是绝对的主角:

  1. 关卡 / 地图随机生成 (Procedural Generation):
    当你需要用代码动态生成一个迷宫、地下城或星球表面时,由于工序繁多(先生成网格 -> 刷地形高度 -> 布置植被 -> 放置怪物 -> 烘焙灯光),使用一个 MapBuilder 能够把极其杂乱的工序管理得井井有条。
  2. 网络请求工具类封装:
    在 Unity 中向服务器发请求时,我们常常需要自定义 Header、Body、Token。用建造者模式可以写出超爽的代码:
    HttpBuilder.Create().SetUrl("api/login").AddParam("user", "123").SetTimeout(5f).Post();

🚨 避坑指南

  • 不要为了用而用: 如果你的类只有两三个属性,且基本固定不变,直接用 C# 的大括号初始化器 new Enemy { Name = "A", Health = 10 }; 就足够了,没必要为了这几个变量凭空多写一个 Builder 类。
  • 状态重置问题: 如果你的 Builder 实例在 Build() 完之后还要重复利用,别忘了在 Build() 的瞬间把内部的临时对象 new 一个新的,防止下一个怪物污染了上一个怪物的属性。