外观模式

在Unity中使用C#实现外观模式(Facade Pattern),核心是为复杂的子系统集群提供一个统一的“门面”接口,简化客户端调用。结合游戏开发典型场景——游戏开局流程(需依次调用资源加载、角色初始化、音效播放、UI显示等多个子系统),能直观体现外观模式“隐藏复杂细节、简化调用”的核心特性。

一、核心思路(外观模式)

角色 对应实现类 作用
子系统类 ResourceSystem/CharacterSystem/AudioSystem/UISystem 复杂功能的具体实现者,各自负责独立的子功能(加载资源、创建角色等)
外观类 GameStartFacade(游戏开局外观类) 封装所有子系统的调用逻辑,对外提供统一的简化接口(如StartGame()
客户端 GameManager(挂载到Unity物体) 仅调用外观类的接口,无需直接操作子系统

二、完整代码实现

1. 子系统类(复杂功能的具体实现)

1.1 资源加载子系统:ResourceSystem
using UnityEngine;

/// <summary>
/// 子系统1:资源加载(场景、预制体、配置表等)
/// </summary>
public class ResourceSystem
{
    /// <summary>
    /// 加载游戏核心资源(模拟异步加载)
    /// </summary>
    public void LoadCoreResources()
    {
        Debug.Log("[资源子系统] 开始加载场景资源...");
        // Unity实际开发中可调用Resources.Load/Addressables.LoadAsync
        // 模拟加载延迟
        Debug.Log("[资源子系统] 角色预制体加载完成!");
        Debug.Log("[资源子系统] 关卡配置表加载完成!");
        Debug.Log("[资源子系统] 所有核心资源加载完毕✅");
    }

    /// <summary>
    /// 释放无用资源(子系统内部方法,客户端无需关注)
    /// </summary>
    private void UnloadUnusedResources()
    {
        Resources.UnloadUnusedAssets();
        Debug.Log("[资源子系统] 已释放无用资源");
    }
}
1.2 角色初始化子系统:CharacterSystem
/// <summary>
/// 子系统2:角色初始化(创建玩家、加载存档、初始化属性)
/// </summary>
public class CharacterSystem
{
    /// <summary>
    /// 初始化玩家角色
    /// </summary>
    /// <param name="playerName">玩家名称</param>
    public void InitPlayer(string playerName)
    {
        Debug.Log($"[角色子系统] 开始初始化玩家:{playerName}");
        // 模拟加载玩家存档、初始化属性
        Debug.Log($"[角色子系统] 玩家{playerName}存档加载完成!");
        Debug.Log($"[角色子系统] 玩家{playerName}属性初始化完成(HP=1000,攻击=200)✅");
    }
}
1.3 音效播放子系统:AudioSystem
/// <summary>
/// 子系统3:音效管理(背景音乐、音效播放)
/// </summary>
public class AudioSystem
{
    /// <summary>
    /// 播放开局背景音乐
    /// </summary>
    public void PlayBGM()
    {
        Debug.Log("[音效子系统] 开始播放开局背景音乐《勇者出征》✅");
        // Unity实际开发中可调用AudioSource.Play()
    }

    /// <summary>
    /// 停止所有音效(子系统内部方法)
    /// </summary>
    private void StopAllAudio()
    {
        Debug.Log("[音效子系统] 已停止所有音效");
    }
}
1.4 UI显示子系统:UISystem
/// <summary>
/// 子系统4:UI管理(显示开局界面、隐藏加载界面)
/// </summary>
public class UISystem
{
    /// <summary>
    /// 显示游戏主界面
    /// </summary>
    public void ShowMainUI()
    {
        Debug.Log("[UI子系统] 隐藏加载界面,显示游戏主界面✅");
        // Unity实际开发中可控制Canvas/Panel的显示隐藏
    }

    /// <summary>
    /// 显示加载进度(子系统内部方法)
    /// </summary>
    /// <param name="progress">进度0-100</param>
    private void ShowLoadingProgress(int progress)
    {
        Debug.Log($"[UI子系统] 加载进度:{progress}%");
    }
}

2. 外观类:GameStartFacade(统一门面接口)

/// <summary>
/// 外观类:游戏开局门面,封装所有子系统的复杂调用逻辑
/// </summary>
public class GameStartFacade
{
    // 持有所有子系统的引用
    private readonly ResourceSystem _resourceSystem;
    private readonly CharacterSystem _characterSystem;
    private readonly AudioSystem _audioSystem;
    private readonly UISystem _uiSystem;

    // 初始化所有子系统
    public GameStartFacade()
    {
        _resourceSystem = new ResourceSystem();
        _characterSystem = new CharacterSystem();
        _audioSystem = new AudioSystem();
        _uiSystem = new UISystem();
    }

    /// <summary>
    /// 对外暴露的简化接口:一键启动游戏
    /// </summary>
    /// <param name="playerName">玩家名称</param>
    public void StartGame(string playerName)
    {
        Debug.Log("========== 开始游戏开局流程 ==========\n");
        
        // 按顺序调用子系统(复杂逻辑封装在外观类内部)
        _resourceSystem.LoadCoreResources();
        Debug.Log(""); // 空行分隔
        _characterSystem.InitPlayer(playerName);
        Debug.Log("");
        _audioSystem.PlayBGM();
        Debug.Log("");
        _uiSystem.ShowMainUI();
        
        Debug.Log("\n========== 游戏开局流程完成 ==========");
    }

    /// <summary>
    /// 可选:对外暴露的简化接口——退出游戏
    /// </summary>
    public void ExitGame()
    {
        Debug.Log("\n========== 开始退出游戏流程 ==========");
        // 调用子系统的清理逻辑(即使子系统方法是private,外观类可通过public方法间接调用)
        Debug.Log("[资源子系统] 释放所有资源");
        Debug.Log("[音效子系统] 停止背景音乐");
        Debug.Log("[UI子系统] 显示退出确认界面");
        Debug.Log("========== 退出流程准备完成 ==========");
    }
}

3. 客户端调用:GameManager(Unity场景挂载)

using UnityEngine;

/// <summary>
/// 游戏管理器(客户端):仅调用外观类的简化接口,无需关注子系统细节
/// </summary>
public class GameManager : MonoBehaviour
{
    private GameStartFacade _gameStartFacade;

    private void Awake()
    {
        // 初始化外观类
        _gameStartFacade = new GameStartFacade();
    }

    private void Start()
    {
        // 一键启动游戏(仅需调用外观类的一个方法,无需操作任何子系统)
        _gameStartFacade.StartGame("勇者一号");

        // 模拟5秒后退出游戏(测试退出接口)
        Invoke(nameof(ExitGameTest), 5f);
    }

    private void ExitGameTest()
    {
        _gameStartFacade.ExitGame();
    }
}

三、Unity运行效果

GameManager挂载到场景任意物体(如MainCamera),运行后控制台输出:

========== 开始游戏开局流程 ==========

[资源子系统] 开始加载场景资源...
[资源子系统] 角色预制体加载完成!
[资源子系统] 关卡配置表加载完成!
[资源子系统] 所有核心资源加载完毕✅

[角色子系统] 开始初始化玩家:勇者一号
[角色子系统] 玩家勇者一号存档加载完成!
[角色子系统] 玩家勇者一号属性初始化完成(HP=1000,攻击=200)✅

[音效子系统] 开始播放开局背景音乐《勇者出征》✅

[UI子系统] 隐藏加载界面,显示游戏主界面✅

========== 游戏开局流程完成 ==========

// 5秒后输出:
========== 开始退出游戏流程 ==========
[资源子系统] 释放所有资源
[音效子系统] 停止背景音乐
[UI子系统] 显示退出确认界面
========== 退出流程准备完成 ==========

四、Unity场景扩展(贴合实际开发)

1. 结合Unity异步加载(Addressables)

实际项目中,资源加载是异步的,可在外观类中封装异步逻辑,客户端只需等待回调:

/// <summary>
/// 外观类中封装异步加载逻辑
/// </summary>
public class GameStartFacade
{
    // ... 原有子系统引用

    /// <summary>
    /// 异步启动游戏(带回调)
    /// </summary>
    /// <param name="playerName">玩家名称</param>
    /// <param name="onComplete">完成回调</param>
    public async void StartGameAsync(string playerName, Action onComplete)
    {
        Debug.Log("========== 异步开始游戏开局流程 ==========\n");
        
        // 异步加载资源(Unity Addressables示例)
        // var handle = Addressables.LoadAssetsAsync<GameObject>("Character", null);
        // await handle.Task;
        Debug.Log("[资源子系统] 异步加载资源完成!");
        
        _characterSystem.InitPlayer(playerName);
        _audioSystem.PlayBGM();
        _uiSystem.ShowMainUI();
        
        Debug.Log("\n========== 异步开局流程完成 ==========");
        onComplete?.Invoke();
    }
}

// 客户端调用异步接口
private void Start()
{
    _gameStartFacade.StartGameAsync("勇者一号", () =>
    {
        Debug.Log("【客户端】游戏已成功启动,可开始交互!");
    });
}

2. 子系统单例化(Unity常用优化)

将子系统改为单例模式,避免重复创建实例,符合Unity开发习惯:

/// <summary>
/// 单例化的资源子系统
/// </summary>
public class ResourceSystem
{
    // 单例实例
    private static ResourceSystem _instance;
    public static ResourceSystem Instance => 
        _instance ?? (_instance = new ResourceSystem());

    // 私有构造器,禁止外部new
    private ResourceSystem() { }

    // ... 原有LoadCoreResources方法
}

// 外观类中使用单例
public class GameStartFacade
{
    private readonly ResourceSystem _resourceSystem = ResourceSystem.Instance;
    // ... 其他子系统同理
}

3. 动态扩展子系统(符合开闭原则)

新增子系统(如SaveSystem存档子系统)时,仅需修改外观类,客户端调用逻辑完全不变:

/// <summary>
/// 新增子系统:存档管理
/// </summary>
public class SaveSystem
{
    public void LoadSaveData(string playerName)
    {
        Debug.Log($"[存档子系统] 加载玩家{playerName}的存档数据✅");
    }
}

// 外观类中集成新子系统
public class GameStartFacade
{
    private readonly SaveSystem _saveSystem = new SaveSystem();

    public void StartGame(string playerName)
    {
        Debug.Log("========== 开始游戏开局流程 ==========\n");
        
        _resourceSystem.LoadCoreResources();
        Debug.Log("");
        _saveSystem.LoadSaveData(playerName); // 新增调用
        Debug.Log("");
        _characterSystem.InitPlayer(playerName);
        // ... 原有其他子系统调用
    }
}

// 客户端调用不变,仍只需一行代码
_gameStartFacade.StartGame("勇者一号");

4. 错误处理封装(简化客户端异常处理)

在外观类中统一处理子系统的异常,客户端无需关注具体错误类型:

public void StartGame(string playerName)
{
    try
    {
        _resourceSystem.LoadCoreResources();
        _characterSystem.InitPlayer(playerName);
        _audioSystem.PlayBGM();
        _uiSystem.ShowMainUI();
    }
    catch (Exception ex)
    {
        // 统一错误处理(显示错误UI、记录日志)
        Debug.LogError($"[外观类] 开局流程出错:{ex.Message}");
        _uiSystem.ShowErrorUI("游戏启动失败,请重试!");
    }
}

五、外观模式核心优势(Unity场景)

  1. 简化调用:客户端仅需调用外观类的1-2个方法,即可完成复杂的多子系统联动(比如开局流程从“调用4个子系统的8个方法”简化为“调用1个StartGame方法”);
  2. 解耦客户端与子系统:客户端完全不依赖子系统,子系统的修改(如资源加载方式从Resources改为Addressables)无需修改客户端代码;
  3. 统一管理复杂逻辑:多子系统的调用顺序、依赖关系都封装在外观类中,便于维护(比如开局必须先加载资源,再初始化角色);
  4. 降低学习成本:新开发者无需了解所有子系统的细节,只需调用外观类的接口即可完成核心功能。

六、外观模式 vs 其他模式(Unity场景对比)

模式 核心差异 Unity适用场景
外观模式 封装多个子系统,提供统一接口 复杂流程调用(开局/结算/退出游戏、下单支付流程)
工厂模式 关注“创建对象” 单个对象的创建(角色、道具)
建造者模式 关注“组装对象”,分步定制 复杂对象的组件组装(角色定制、UI面板组装)

外观模式是Unity中开发“复杂流程型功能”的首选模式,比如游戏开局、关卡结算、商城下单、玩家退出等需要多系统联动的场景,能极大简化客户端代码,降低维护成本。