Unity 设计模式 之 结构型模式 -【装饰者模式】【外观模式】【享元模式】【代理模式】
目录
Unity 设计模式 之 结构型模式 -【装饰者模式】【外观模式】【享元模式】【代理模式】
4.2 添加武器功能的装饰者 WeaponDecorator
1.1 子系统 1:3D 对象管理(ObjectManager)
1.2 子系统 2:对象移动管理(MovementManager)
一、简单介绍
设计模式 是指在软件开发中为解决常见问题而总结出的一套 可复用的解决方案。这些模式是经过长期实践证明有效的 编程经验总结,并可以在不同的项目中复用。设计模式并不是代码片段,而是对常见问题的 抽象解决方案,它提供了代码结构和模块间交互的一种设计思路,帮助开发者解决特定的设计问题。
设计模式的特点:
- 通用性:设计模式针对的是软件开发中常见的设计问题,适用于各种软件工程项目。
- 可复用性:设计模式可以在不同项目和环境下被重复使用,提高代码的可维护性和扩展性。
- 可扩展性:设计模式有助于让代码结构更加灵活,易于扩展和修改。
- 模块化:通过设计模式,可以减少代码的耦合性,增强模块间的独立性。
- 提高沟通效率:设计模式为开发者提供了一种通用的设计语言,使得团队成员能够快速理解并讨论设计方案。
二、装饰者模式(Decorator Pattern)
装饰者模式(Decorator Pattern) 是一种结构型设计模式,允许动态地为对象添加新的功能,而不改变其原有的结构。它通过创建一个装饰类,包装原始对象,以扩展对象的功能。这种模式非常灵活,因为可以在不修改现有类的情况下动态地添加功能。
装饰者模式的核心思想是将功能封装到独立的类中,通过这些类来为对象提供额外的行为。它避免了继承带来的类爆炸问题,因为不需要创建大量的子类去实现不同的功能组合。
装饰者模式的组成部分
- Component(组件接口/抽象类):定义了对象的接口,装饰类和被装饰的类都需要实现该接口。
- ConcreteComponent(具体组件):被装饰的原始对象,通常是我们想要动态添加功能的对象。
- Decorator(装饰者):装饰者类,继承自组件接口,持有一个组件对象,并通过它为被装饰的对象增加额外的功能。
- ConcreteDecorator(具体装饰者):实际的装饰类,负责为对象增加具体的功能。
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()