当前位置: 首页 > news >正文

设计模式——结构型模式(下)

外观模式(Facade):为复杂子系统提供 “统一的简化接口”,降低调用者复杂度。

外观模式相关具体代码如下所示:

using UnityEngine;
using System;// 子系统1:音频系统
public class AudioSystem
{public void PlayBackgroundMusic(string trackName){Debug.Log($"播放背景音乐: {trackName}");}public void StopBackgroundMusic(){Debug.Log("停止背景音乐");}public void PlaySoundEffect(string effectName, Vector3 position){Debug.Log($"在位置 {position} 播放音效: {effectName}");}
}// 子系统2:UI系统
public class UISystem
{public void ShowMainMenu(){Debug.Log("显示主菜单");}public void HideMainMenu(){Debug.Log("隐藏主菜单");}public void ShowHUD(){Debug.Log("显示游戏HUD界面");}public void UpdateScore(int score){Debug.Log($"更新分数显示: {score}");}
}// 子系统3:场景管理系统
public class SceneSystem
{public void LoadGameScene(string sceneName){Debug.Log($"加载游戏场景: {sceneName}");// 实际项目中会调用SceneManager.LoadScene()}public void UnloadCurrentScene(){Debug.Log("卸载当前场景");}public void RestartCurrentScene(){Debug.Log("重启当前场景");}
}// 子系统4:存档系统
public class SaveSystem
{public void SaveGame(string saveName){Debug.Log($"保存游戏到存档: {saveName}");}public void LoadGame(string saveName){Debug.Log($"从存档加载游戏: {saveName}");}public bool HasSaveData(){return true; // 简化示例,始终返回有存档}
}// 外观类:提供统一接口封装子系统
public class GameManagerFacade : MonoBehaviour
{// 子系统实例private AudioSystem audioSystem;private UISystem uiSystem;private SceneSystem sceneSystem;private SaveSystem saveSystem;private void Awake(){// 初始化子系统audioSystem = new AudioSystem();uiSystem = new UISystem();sceneSystem = new SceneSystem();saveSystem = new SaveSystem();}// 封装的开始新游戏流程public void StartNewGame(){Debug.Log("\n=== 开始新游戏 ===");uiSystem.HideMainMenu();sceneSystem.LoadGameScene("Level1");audioSystem.PlayBackgroundMusic("BattleTheme");uiSystem.ShowHUD();uiSystem.UpdateScore(0);}// 封装的继续游戏流程public void ContinueGame(){if (saveSystem.HasSaveData()){Debug.Log("\n=== 继续游戏 ===");uiSystem.HideMainMenu();saveSystem.LoadGame("AutoSave");audioSystem.PlayBackgroundMusic("BattleTheme");uiSystem.ShowHUD();}else{Debug.Log("\n没有可用存档,开始新游戏");StartNewGame();}}// 封装的游戏结束流程public void GameOver(){Debug.Log("\n=== 游戏结束 ===");audioSystem.StopBackgroundMusic();audioSystem.PlaySoundEffect("GameOver", Vector3.zero);uiSystem.ShowMainMenu();sceneSystem.UnloadCurrentScene();}// 封装的保存游戏流程public void SaveCurrentGame(){Debug.Log("\n=== 保存游戏 ===");saveSystem.SaveGame("AutoSave");audioSystem.PlaySoundEffect("SaveSuccess", Vector3.zero);}// 封装的更新分数流程public void AddScore(int points){// 内部可能涉及多个子系统协作uiSystem.UpdateScore(points);audioSystem.PlaySoundEffect("ScoreUp", Vector3.zero);}
}// 客户端:只与外观类交互
public class GameClient : MonoBehaviour
{public GameManagerFacade gameManager;private void Start(){if (gameManager == null){gameManager = FindObjectOfType<GameManagerFacade>();}// 显示主菜单(初始状态)// 注意:客户端不需要知道具体哪个子系统处理这个请求// 模拟用户输入Invoke("UserStartNewGame", 1f);Invoke("UserAddScore", 2f);Invoke("UserSaveGame", 3f);Invoke("UserGameOver", 4f);}private void UserStartNewGame(){gameManager.StartNewGame();}private void UserAddScore(){gameManager.AddScore(150);}private void UserSaveGame(){gameManager.SaveCurrentGame();}private void UserGameOver(){gameManager.GameOver();}
}

外观模式的优势:

简化了客户端的使用,将复杂的子系统交互封装为简单接口;降低了客户端与子系统之间的耦合度,子系统的变化不会影响客户端;隐藏了系统的内部复杂性,使代码更易于维护和扩展;可以有选择地暴露子系统功能,控制访问权限。在 Unity 中,外观模式非常适合实现GameManager这类核心管理器,将音频、UI、场景、存档等分散的系统整合起来,为其他脚本提供简洁的接口。

享元模式(Flyweight):复用大量 “相同 / 相似属性” 的对象,减少内存消耗

享元模式相关具体代码如下所示:

using UnityEngine;
using System.Collections.Generic;// 享元对象:存储可共享的精灵数据
public class SpriteFlyweight
{public Sprite Sprite;       // 共享的精灵图public string SpriteName;   // 精灵名称public Vector2 Pivot;       // 精灵锚点public Vector2 Size;        // 精灵尺寸public SpriteFlyweight(Sprite sprite){Sprite = sprite;SpriteName = sprite.name;Pivot = sprite.pivot;Size = sprite.rect.size;}
}// 享元工厂:管理和提供享元对象
public class SpriteFlyweightFactory
{private Dictionary<string, SpriteFlyweight> flyweights = new Dictionary<string, SpriteFlyweight>();// 获取或创建享元对象public SpriteFlyweight GetFlyweight(Sprite sprite){if (sprite == null) return null;string key = sprite.name;// 如果已存在则返回共享实例,否则创建新的享元对象if (!flyweights.ContainsKey(key)){flyweights[key] = new SpriteFlyweight(sprite);Debug.Log($"创建新的享元对象: {key}");}else{Debug.Log($"复用享元对象: {key}");}return flyweights[key];}// 获取当前缓存的享元对象数量public int GetFlyweightCount(){return flyweights.Count;}
}// 具体游戏对象:包含不可共享的状态和共享的享元对象
public class GameSpriteObject : MonoBehaviour
{private SpriteFlyweight flyweight;  // 共享的享元部分private Vector3 position;           // 不可共享的位置private Quaternion rotation;        // 不可共享的旋转private Color color;                // 不可共享的颜色// 初始化方法:组合享元对象和特有状态public void Initialize(SpriteFlyweight flyweight, Vector3 pos, Quaternion rot, Color col){this.flyweight = flyweight;this.position = pos;this.rotation = rot;this.color = col;// 应用精灵和属性到游戏对象ApplySprite();}private void ApplySprite(){if (flyweight?.Sprite != null){// 添加SpriteRenderer并设置共享的精灵SpriteRenderer renderer = GetComponent<SpriteRenderer>();if (renderer == null)renderer = gameObject.AddComponent<SpriteRenderer>();renderer.sprite = flyweight.Sprite;transform.position = position;transform.rotation = rotation;renderer.color = color;gameObject.name = $"{flyweight.SpriteName}_Instance";}}
}// 客户端:使用享元模式创建大量游戏对象
public class FlyweightClient : MonoBehaviour
{public Sprite[] sprites;  // 可在Inspector中赋值的精灵数组public int objectsPerType = 50;  // 每种精灵创建的实例数量private SpriteFlyweightFactory factory;private List<GameSpriteObject> gameObjects = new List<GameSpriteObject>();private void Start(){// 初始化享元工厂factory = new SpriteFlyweightFactory();// 创建大量游戏对象CreateMultipleObjects();// 显示统计信息Debug.Log($"创建了 {gameObjects.Count} 个游戏对象");Debug.Log($"但只使用了 {factory.GetFlyweightCount()} 个共享的享元对象");}private void CreateMultipleObjects(){foreach (Sprite sprite in sprites){if (sprite == null) continue;// 获取共享的享元对象SpriteFlyweight flyweight = factory.GetFlyweight(sprite);// 创建多个实例,共享同一个享元对象但拥有不同的位置和颜色for (int i = 0; i < objectsPerType; i++){// 随机位置和颜色(特有状态)Vector3 randomPos = new Vector3(Random.Range(-10f, 10f),Random.Range(-5f, 5f),0);Color randomColor = new Color(Random.Range(0.8f, 1f),Random.Range(0.8f, 1f),Random.Range(0.8f, 1f));// 创建游戏对象并初始化GameObject go = new GameObject();GameSpriteObject gameSprite = go.AddComponent<GameSpriteObject>();gameSprite.Initialize(flyweight, randomPos, Quaternion.identity, randomColor);gameObjects.Add(gameSprite);}}}
}

享元模式的优势:

显著减少内存占用,尤其在需要创建大量相似对象时;降低了系统资源消耗,提高性能;将对象的共享数据和特有数据分离,清晰管理不同类型的状态;适合处理纹理、字体、配置数据等重复使用的资源。在 Unity 中,享元模式特别适合 2D 游戏中的大量精灵实例、粒子系统配置、地形瓦片等场景,能够有效优化内存使用和加载性能。

代理模式(Proxy):为对象提供 “代理类”,控制对原对象的访问(如权限、缓存、远程调用)。

代理模式的相关具体代码如下所示:

using UnityEngine;
using System;// 主题接口:定义真实对象和代理的共同行为
public interface IHeavyResource
{void Load();void Unload();bool IsLoaded { get; }string GetResourceName();
}// 真实主题:需要被代理的重量级资源(如大型模型、关卡数据等)
public class HeavyResource : IHeavyResource
{private string resourcePath;private bool isLoaded;public HeavyResource(string path){resourcePath = path;isLoaded = false;}public void Load(){if (isLoaded){Debug.Log($"{GetResourceName()} 已加载,无需重复加载");return;}// 模拟加载重量级资源的耗时操作Debug.Log($"开始加载重量级资源: {resourcePath}");// 实际项目中可能是 Resources.Load() 或 Addressables.LoadAssetAsync()System.Threading.Thread.Sleep(1000); // 模拟加载延迟isLoaded = true;Debug.Log($"{GetResourceName()} 加载完成");}public void Unload(){if (!isLoaded){Debug.Log($"{GetResourceName()} 未加载,无需卸载");return;}// 模拟卸载资源Debug.Log($"卸载重量级资源: {resourcePath}");// 实际项目中可能是 Resources.UnloadAsset() 或 Addressables.Release()isLoaded = false;}public bool IsLoaded => isLoaded;public string GetResourceName(){return System.IO.Path.GetFileName(resourcePath);}
}/// <summary>/// 代理类(ResourceProxy):实现主题接口,内部持有真实资源的引用,提供以下额外功能:/// 延迟初始化:只有在需要时才创建真实资源对象/// 权限控制:检查访问者是否有权限操作资源/// 访问日志:记录所有对资源的操作/// 安全访问:即使真实对象未初始化也能安全处理请求/// </summary>
// 代理类:控制对真实资源的访问
public class ResourceProxy : IHeavyResource
{private HeavyResource realResource; // 真实资源对象(延迟初始化)private string resourcePath;private int accessCount = 0; // 记录访问次数private string userRole; // 访问者角色(用于权限控制)public ResourceProxy(string path, string role){resourcePath = path;userRole = role;}// 代理的加载方法:包含权限检查、延迟初始化和日志记录public void Load(){accessCount++;LogAccess("Load");// 权限检查if (!HasPermission()){Debug.LogError($"权限不足!{userRole} 无法加载 {GetResourceName()}");return;}// 延迟初始化:只有在真正需要时才创建真实对象if (realResource == null){realResource = new HeavyResource(resourcePath);}realResource.Load();}// 代理的卸载方法public void Unload(){accessCount++;LogAccess("Unload");if (!HasPermission()){Debug.LogError($"权限不足!{userRole} 无法卸载 {GetResourceName()}");return;}if (realResource != null){realResource.Unload();}else{Debug.Log($"{GetResourceName()} 尚未加载,无法卸载");}}public bool IsLoaded{get{// 即使真实对象未初始化,也能安全返回状态return realResource != null && realResource.IsLoaded;}}public string GetResourceName(){return System.IO.Path.GetFileName(resourcePath);}// 代理特有的功能:权限检查private bool HasPermission(){// 只有管理员和开发者可以加载特殊资源if (resourcePath.Contains("secret") && userRole != "Admin" && userRole != "Developer"){return false;}return true;}// 代理特有的功能:访问日志记录private void LogAccess(string operation){Debug.Log($"[{DateTime.Now:HH:mm:ss}] {userRole} 执行 {operation} 操作,资源: {GetResourceName()},累计访问: {accessCount}次");}
}// 客户端:只与代理交互,不直接访问真实对象
public class ResourceManager : MonoBehaviour
{private void Start(){// 创建代理(客户端不知道真实对象的存在)IHeavyResource normalResource = new ResourceProxy("models/character.prefab", "Player");IHeavyResource secretResource = new ResourceProxy("models/secret_boss.prefab", "Player");IHeavyResource adminResource = new ResourceProxy("models/secret_boss.prefab", "Admin");// 通过代理访问资源Debug.Log("=== 玩家访问普通资源 ===");normalResource.Load();normalResource.Load(); // 测试重复加载Debug.Log($"普通资源加载状态: {normalResource.IsLoaded}");normalResource.Unload();Debug.Log("\n=== 玩家访问机密资源(权限不足) ===");secretResource.Load(); // 会被拒绝Debug.Log("\n=== 管理员访问机密资源 ===");adminResource.Load();Debug.Log($"机密资源加载状态: {adminResource.IsLoaded}");adminResource.Unload();}
}

代理模式的优势:

控制对真实对象的访问,可实现权限管理、访问限制;实现延迟加载,提高系统启动速度和内存使用效率;可以在不修改真实对象的情况下添加额外功能(如日志、缓存);隔离了客户端与真实对象,降低了耦合度。在 Unity 中,代理模式适合用于资源管理、网络请求、权限控制等场景,尤其适合处理那些创建成本高、加载耗时的资源或服务。

 

http://www.dtcms.com/a/395824.html

相关文章:

  • CANoe中封装SeedKey安全解锁函数的完整指南
  • Vue树选择
  • opencv人脸识别
  • 怿星科技桂林子公司乔迁新址,于山水画中开启研发新篇章
  • 创建者模式:工厂方法模式
  • 【 C/C++ 算法】入门动态规划-----路径问题(以练代学式)
  • 三.上网行为安全
  • k个一组翻转链表
  • Super分区和动态分区
  • 2026华清远见新品发布会:聚焦人工智能嵌入式物联网,打造“虚实融合•软硬协同“智能化教育新生态!
  • 09 - spring security加载流程
  • 【大前端】Android:读取剪切板与禁用剪切板复制功能(完整指南)
  • 第18讲 机器学习与深度学习
  • 数据结构 05(线性:栈和队列)
  • RAG系统嵌入模型怎么选?选型策略和踩坑指南
  • 机器学习 vs. 动力学模型,Ai2 最新研究:仅需 2 分钟,ACE2 可完成一次 4 个月季节预报
  • RNN循环神经网络详解
  • 【Nginx开荒攻略】深入解析Nginx进程管理与信号控制:从原理到实战
  • MySQL与Redis面试问题详解
  • 鸿蒙Next IPC Kit详解:构建高效进程间通信的完整指南
  • 【开题答辩全过程】以 基于springboot的高校疫情防控系统为例,包含答辩的问题和答案
  • Centos7 命令行使用nmcli重置网络配置
  • 如何计算sequence粒度的负载均衡损失
  • 学财税大数据应用,需要考CPA/税务师吗?
  • RAG全栈技术——文档加载器
  • 如何理解Service Mesh(服务网格)
  • android9适配camera gc02m1
  • 【十天成长计划】BoostKit初级班 开班啦!——陪伴式学习,阶梯式成长
  • 【图文详解】强化学习核心框架、数学基础、分类、应用场景
  • Rust简介