Unity 设计模式 之 结构型模式 -【装饰者模式】【外观模式】【享元模式】【代理模式】
目录
Unity 设计模式 之 结构型模式 -【装饰者模式】【外观模式】【享元模式】【代理模式】
4.2 添加武器功能的装饰者 WeaponDecorator
1.1 子系统 1:3D 对象管理(ObjectManager)
1.2 子系统 2:对象移动管理(MovementManager)
一、简单介绍
设计模式 是指在软件开发中为解决常见问题而总结出的一套 可复用的解决方案。这些模式是经过长期实践证明有效的 编程经验总结,并可以在不同的项目中复用。设计模式并不是代码片段,而是对常见问题的 抽象解决方案,它提供了代码结构和模块间交互的一种设计思路,帮助开发者解决特定的设计问题。
二、装饰者模式(Decorator Pattern)
装饰者模式(Decorator Pattern) 是一种结构型设计模式,允许动态地为对象添加新的功能,而不改变其原有的结构。它通过创建一个装饰类,包装原始对象,以扩展对象的功能。这种模式非常灵活,因为可以在不修改现有类的情况下动态地添加功能。
装饰者模式的核心思想是将功能封装到独立的类中,通过这些类来为对象提供额外的行为。它避免了继承带来的类爆炸问题,因为不需要创建大量的子类去实现不同的功能组合。
装饰者模式的组成部分
1、什么时候使用装饰者模式
2、使用装饰者模式的好处
3、使用装饰者模式时的注意事项
三、在 Unity 中使用 装饰者模式
在 Unity 中,我们可以使用装饰者模式来动态为 3D 游戏对象添加不同的功能,比如给角色添加不同的外观组件或效果(例如盔甲、武器、特殊能力等)。通过装饰者模式,我们可以在不修改原始对象的情况下,灵活地给游戏对象添加功能,保持代码的简洁性和扩展性。
装饰者模式示例:动态装饰 3D 游戏角色
这个示例展示如何使用装饰者模式为一个简单的 3D 游戏角色添加盔甲和武器。装饰者模式允许我们动态地为对象叠加功能,而无需修改对象本身。
参考类图如下:
1、定义组件接口 ICharacter
首先,定义一个 ICharacter
接口,包含一个 Display
方法用于展示角色。
这个接口规定了角色的基本行为,即 Display
方法,表示角色将如何在场景中展示。
public interface ICharacter
{
void Display();
}
2、实现具体角色类 PlayerCharacter
接下来,我们实现一个具体的角色类 PlayerCharacter
,它是一个简单的角色,用 Unity 的 PrimitiveType.Capsule
作为视觉表示。
这个类实现了 ICharacter
接口,并渲染一个基本的 3D 角色(胶囊体)。
using UnityEngine;
public class PlayerCharacter : ICharacter
{
public void Display()
{
Debug.Log("Displaying Player Character");
GameObject.CreatePrimitive(PrimitiveType.Capsule); // 使用胶囊体来表示角色
}
}
3、实现装饰者基类 CharacterDecorator
装饰者基类 CharacterDecorator
,它持有一个 ICharacter
对象,并通过它来扩展角色的功能。
这个装饰者基类继承了 ICharacter
,并持有一个 ICharacter
类型的对象,Display
方法会调用原始对象的 Display
方法。
public abstract class CharacterDecorator : ICharacter
{
protected ICharacter decoratedCharacter;
public CharacterDecorator(ICharacter character)
{
decoratedCharacter = character;
}
public virtual void Display()
{
decoratedCharacter.Display();
}
}
4、实现具体的装饰者类
4.1 添加盔甲功能的装饰者 ArmorDecorator
using UnityEngine;
public class ArmorDecorator : CharacterDecorator
{
public ArmorDecorator(ICharacter character) : base(character) { }
public override void Display()
{
base.Display(); // 调用原始角色的 Display 方法
AddArmor(); // 添加盔甲
}
private void AddArmor()
{
Debug.Log("Adding Armor to Character");
// 使用立方体来表示盔甲
GameObject armor = GameObject.CreatePrimitive(PrimitiveType.Cube);
armor.transform.position = new Vector3(0, 1, 0); // 将盔甲放在角色的顶部
}
}
4.2 添加武器功能的装饰者 WeaponDecorator
using UnityEngine;
public class WeaponDecorator : CharacterDecorator
{
public WeaponDecorator(ICharacter character) : base(character) { }
public override void Display()
{
base.Display(); // 调用原始角色的 Display 方法
AddWeapon(); // 添加武器
}
private void AddWeapon()
{
Debug.Log("Adding Weapon to Character");
// 使用圆柱体来表示武器
GameObject weapon = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
weapon.transform.position = new Vector3(0, 2, 0); // 将武器放在角色的手上
}
}
这两个装饰者类分别为角色添加盔甲和武器的功能。通过调用 base.Display()
方法,装饰者可以先调用原始对象的行为,再添加新的功能。
5、在 Unity 中使用装饰者模式
在 Unity 中,我们可以将这些装饰者组合起来,为一个角色动态地添加盔甲和武器。以下代码展示了如何在 Unity 场景中使用装饰者模式。
using UnityEngine;
public class DecoratorPatternExample : MonoBehaviour
{
void Start()
{
// 创建一个基本的玩家角色
ICharacter player = new PlayerCharacter();
// 为玩家角色添加盔甲装饰
ICharacter armoredPlayer = new ArmorDecorator(player);
// 为玩家角色添加武器装饰
ICharacter fullyEquippedPlayer = new WeaponDecorator(armoredPlayer);
// 渲染角色,并添加盔甲和武器
fullyEquippedPlayer.Display();
}
}
6、运行结果分析
-
在控制台中,你将看到以下输出:
Displaying Player Character Adding Armor to Character Adding Weapon to Character
-
在 Unity 场景中,将会渲染出一个胶囊体(表示玩家角色),并在其上方附加一个立方体(表示盔甲),以及一个圆柱体(表示武器)。
通过这个示例,你可以看到如何在 Unity 中使用装饰者模式动态地为 3D 游戏角色添加不同的功能。这种设计模式特别适合游戏开发中需要灵活扩展功能的场景。
四、外观模式(Facade Pattern)
外观模式(Facade Pattern) 是一种结构型设计模式,提供了一个统一的接口,用来访问子系统中的一群接口。外观模式定义了一个高层接口,使得这一子系统更加容易使用。它封装了子系统的复杂性,简化了客户端的调用。
在复杂的系统中,往往会有许多类和接口相互交织,导致使用起来比较复杂。外观模式通过引入一个外观类,提供一个简单的接口来隐藏子系统的复杂实现,客户端只需通过这个外观类与子系统交互,而不需要直接面对复杂的内部逻辑。
外观模式的组成
1、什么时候使用外观模式
2、使用外观模式的好处
3、使用外观模式时的注意事项
通过引入一个外观类,将子系统的复杂性封装起来,提供一个简洁的接口,使得客户端可以方便地与子系统交互。
- 简化系统的使用,降低了客户端与子系统之间的耦合。
- 隐藏子系统的复杂实现,客户端无需关注细节。
- 提高系统的可维护性,外观类可以调整内部子系统的逻辑,而不会影响客户端。
使用场景:当系统内部结构复杂,且子系统之间相互依赖时;当希望简化客户端对复杂子系统的调用时;在分层架构中使用外观模式,提供清晰的接口分层;
注意事项,不要滥用外观模式,不要将所有功能都封装在外观类中,以免外观类变得过于复杂。
确保外观类的职责单一,专注于简化接口,而非实现过多业务逻辑。
总之,外观模式是一个非常有用的设计模式,尤其是在需要与复杂子系统交互时,通过引入外观类可以简化客户端的操作,同时提高系统的可维护性和扩展性。
五、在 Unity 中使用 外观模式
在 Unity 中,外观模式可以简化对多个复杂子系统的使用,例如场景中各种 3D 对象的创建、控制动画、音频管理等。我们可以创建一个外观类来封装多个功能,让客户端通过一个简单的接口管理这些复杂的子系统。
以下是一个使用外观模式的示例,展示如何创建、移动 3D 对象并播放音效。我们将外观类 GameManagerFacade
封装这些子系统,使得客户端只需通过外观类进行操作。
参考类图如下:
1、定义子系统类
1.1 子系统 1:3D 对象管理(ObjectManager
)
负责创建不同类型的 3D 对象(例如立方体、球体等):
using UnityEngine;
public class ObjectManager
{
public GameObject CreateObject(PrimitiveType type, Vector3 position)
{
GameObject obj = GameObject.CreatePrimitive(type);
obj.transform.position = position;
Debug.Log(type.ToString() + " created at " + position);
return obj;
}
}
1.2 子系统 2:对象移动管理(MovementManager
)
负责处理对象的移动操作:
using UnityEngine;
public class MovementManager
{
public void MoveObject(GameObject obj, Vector3 targetPosition, float speed)
{
obj.transform.position = Vector3.Lerp(obj.transform.position, targetPosition, speed * Time.deltaTime);
Debug.Log("Moving " + obj.name + " to " + targetPosition);
}
}
1.3 子系统 3:音频管理(AudioManager
)
负责播放音效:
using UnityEngine;
public class AudioManager
{
public void PlaySound(AudioClip clip, Vector3 position)
{
AudioSource.PlayClipAtPoint(clip, position);
Debug.Log("Playing sound at " + position);
}
}
2、实现外观类 GameManagerFacade
GameManagerFacade
封装了 ObjectManager
、MovementManager
和 AudioManager
,为客户端提供一个统一的接口:
using UnityEngine;
public class GameManagerFacade
{
private ObjectManager objectManager;
private MovementManager movementManager;
private AudioManager audioManager;
public GameManagerFacade()
{
objectManager = new ObjectManager();
movementManager = new MovementManager();
audioManager = new AudioManager();
}
// 创建对象
public GameObject CreateObject(PrimitiveType type, Vector3 position)
{
return objectManager.CreateObject(type, position);
}
// 移动对象
public void MoveObject(GameObject obj, Vector3 targetPosition, float speed)
{
movementManager.MoveObject(obj, targetPosition, speed);
}
// 播放音效
public void PlaySound(AudioClip clip, Vector3 position)
{
audioManager.PlaySound(clip, position);
}
}
3、客户端代码
通过 GameManagerFacade
操作子系统,简化了客户端的使用。以下示例展示如何使用外观模式创建 3D 对象、移动对象和播放音效。
using UnityEngine;
public class FacadePatternExample : MonoBehaviour
{
public AudioClip soundClip; // 拖入音效文件
private GameManagerFacade gameManager;
private GameObject cube;
void Start()
{
// 初始化外观类
gameManager = new GameManagerFacade();
// 创建立方体
cube = gameManager.CreateObject(PrimitiveType.Cube, new Vector3(0, 0, 0));
// 播放音效
gameManager.PlaySound(soundClip, new Vector3(0, 0, 0));
}
void Update()
{
// 在每帧更新时,移动立方体
if (cube != null)
{
gameManager.MoveObject(cube, new Vector3(5, 0, 0), 1f);
}
}
}
4、运行示例
场景设置
这个示例展示了如何使用外观模式简化 3D 对象的管理和控制,在 Unity 中使用这种模式可以帮助我们更好地组织和管理复杂的子系统。
总之,外观模式的好处
使用外观模式的注意事项:
六、享元模式(Flyweight Pattern)
享元模式(Flyweight Pattern) 是一种结构型设计模式,旨在减少对象的创建数量,以降低内存占用和提高性能。享元模式通过将对象的状态分为内部状态(共享)和外部状态(不共享)来实现共享对象的使用。
- 内部状态:可以被多个对象共享的状态,通常是不可变的(如颜色、形状等)。
- 外部状态:不能被共享的状态,通常与具体对象的上下文有关(如位置、大小等)。
享元模式通过共享相同的对象实例来减少内存占用,适合用于需要大量相似对象的场景。
1、什么时候使用享元模式
2、使用享元模式的好处
3、使用享元模式时的注意事项
享元模式是处理大量相似对象时的一种有效策略,能够显著降低内存占用和提高性能。
七、在 Unity 中使用 享元模式
以下是一个基于 Unity 的享元模式示例,展示如何在场景中高效地创建和管理大量相似的 3D 对象,例如树木。我们将实现一个简单的树木工厂,该工厂使用享元模式来共享树木的类型和外观,以减少内存占用。
参考类图如下:
1. 定义享元类
首先,我们定义一个树木类型类 TreeType
,它包含树木的内部状态(如颜色和预制体)。
using UnityEngine;
public class TreeType
{
public string Name { get; private set; }
public Color Color { get; private set; }
public GameObject Prefab { get; private set; }
public TreeType(string name, Color color, GameObject prefab)
{
Name = name;
Color = color;
Prefab = prefab;
}
public void Draw(Vector3 position)
{
GameObject tree = Object.Instantiate(Prefab, position, Quaternion.identity);
tree.GetComponent<Renderer>().material.color = Color;
}
}
2. 创建享元工厂
然后,我们实现一个工厂类 TreeFactory
,它负责创建和管理树木类型的实例。工厂会检查是否已经存在特定的树木类型,如果存在则重用。
using System.Collections.Generic;
public class TreeFactory
{
private Dictionary<string, TreeType> treeTypes = new Dictionary<string, TreeType>();
public TreeType GetTreeType(string name, Color color, GameObject prefab)
{
if (!treeTypes.ContainsKey(name))
{
treeTypes[name] = new TreeType(name, color, prefab);
}
return treeTypes[name];
}
}
3. 客户端代码
接下来,我们编写一个脚本来使用 TreeFactory
创建树木。在 Unity 的场景中放置一个树木预制体并将其拖入 treePrefab
字段。
using UnityEngine;
public class FlyweightExample : MonoBehaviour
{
public GameObject treePrefab; // 拖入树木预制体
private TreeFactory treeFactory;
void Start()
{
treeFactory = new TreeFactory();
// 创建不同颜色和类型的树木
CreateTrees();
}
private void CreateTrees()
{
for (int i = 0; i < 10; i++)
{
// 创建橡树
TreeType oakTree = treeFactory.GetTreeType("Oak", Color.green, treePrefab);
oakTree.Draw(new Vector3(i * 2, 0, 0));
// 创建松树
TreeType pineTree = treeFactory.GetTreeType("Pine", Color.red, treePrefab);
pineTree.Draw(new Vector3(i * 2, 0, 2));
}
}
}
4. 场景设置
- 树木预制体:在 Unity 中创建一个树木的预制体(例如使用一个简单的立方体或球体),然后将其拖入
FlyweightExample
脚本的treePrefab
字段。
5、运行示例
- 将
FlyweightExample
脚本添加到一个空的 GameObject。 - 运行场景,你将看到两种类型的树木(橡树和松树)在场景中生成,使用享元模式有效地减少了内存使用。
这个示例展示了如何在 Unity 中利用享元模式来优化对象管理,使得开发者能够更高效地处理复杂场景中的对象,通过共享树木类型的实例,降低了内存占用,允许大量相似对象的高效创建和管理,该模式在需要处理大量相似对象的场景中表现出色,尤其是在游戏开发中。
八、代理模式(Proxy Pattern)
代理模式(Proxy Pattern) 是一种结构型设计模式,通过为另一个对象提供一个代理以控制对该对象的访问。代理对象可以在客户端和真实对象之间充当中介,负责访问控制、延迟加载、安全检查等功能。
代理模式通常分为以下几种类型:
1、什么时候使用代理模式
2、使用代理模式的好处
3、使用代理模式时的注意事项
九、在 Unity 中使用 代理模式
以下是一个基于 Unity 的代理模式示例,展示如何使用虚拟代理来延迟加载和显示 3D 模型。这个示例通过代理类控制对真实 3D 模型的访问,以减少内存占用和提高性能。
参考类图如下:
1、定义组件接口 IModel
首先,定义一个 IModel 接口,包含一个 Display
方法用于展示角色。
这个接口规定了角色的基本行为,即 Display
方法,表示角色将如何在场景中展示。
public interface IModel
{
void Display();
}
2、定义真实对象
首先,我们定义一个真实对象类 RealModel
,它负责加载和显示 3D 模型。我们假设加载模型是一个耗时的操作。
using UnityEngine;
public class RealModel :IModel
{
private string modelPath;
private GameObject model;
public RealModel(string path)
{
modelPath = path;
LoadModelFromDisk(); // 假设这是一个耗时的操作
}
private void LoadModelFromDisk()
{
Debug.Log($"Loading model from: {modelPath}");
// 这里可以使用 Resources.Load 或者其他方式加载模型
model = GameObject.CreatePrimitive(PrimitiveType.Cube); // 使用立方体作为示例
model.name = modelPath; // 设置模型名称
}
public void Display()
{
if (model != null)
{
Debug.Log($"Displaying model: {model.name}");
model.SetActive(true); // 激活模型显示
}
}
}
3、定义代理类
接下来,我们实现一个代理类 ProxyModel
,用于延迟加载和控制对真实模型的访问。
public class ProxyModel : IModel
{
private IModel realModel;
private string modelPath;
public ProxyModel(string path)
{
modelPath = path;
}
public void Display()
{
if (realModel == null)
{
realModel = new RealModel(modelPath); // 延迟加载真实模型
}
realModel.Display();
}
}
4、客户端代码
我们编写一个客户端代码,在 Unity 场景中使用代理模式来加载和显示模型。
using UnityEngine;
public class ProxyPatternExample : MonoBehaviour
{
void Start()
{
IModel proxyModel = new ProxyModel("My3DModel");
// 第一次显示,触发真实对象的加载
proxyModel.Display();
// 再次显示,使用已加载的真实对象
proxyModel.Display();
}
}
5、 运行示例
-
在 Unity 中,创建一个空的 GameObject,并将
ProxyPatternExample
脚本附加到该对象上。 -
运行场景,你会看到第一次调用
Display
方法时,真实模型被加载,而第二次调用时则直接显示,不再加载。
代理模式的实现,通过 ProxyModel
控制对 RealModel
的访问,实现延迟加载和资源管理;适用场景,当加载 3D 模型或其他资源消耗较大时,代理模式能够优化性能,减少内存占用;示例说明,这个示例展示了如何在 Unity 中有效地使用代理模式,以提高资源管理的效率和灵活性。