Unity 设计模式 之 结构型模式 -【装饰者模式】【外观模式】【享元模式】【代理模式】
Unity 设计模式 之 结构型模式 -【装饰者模式】【外观模式】【享元模式】【代理模式】
目录
Unity 设计模式 之 结构型模式 -【装饰者模式】【外观模式】【享元模式】【代理模式】
一、简单介绍
二、装饰者模式(Decorator Pattern)
1、什么时候使用装饰者模式
2、使用装饰者模式的好处
3、使用装饰者模式时的注意事项
三、在 Unity 中使用 装饰者模式
1、定义组件接口 ICharacter
2、实现具体角色类 PlayerCharacter
3、实现装饰者基类 CharacterDecorator
4、实现具体的装饰者类
4.1 添加盔甲功能的装饰者 ArmorDecorator
4.2 添加武器功能的装饰者 WeaponDecorator
5、在 Unity 中使用装饰者模式
6、运行结果分析
四、外观模式(Facade Pattern)
1、什么时候使用外观模式
2、使用外观模式的好处
3、使用外观模式时的注意事项
五、在 Unity 中使用 外观模式
1、定义子系统类
1.1 子系统 1:3D 对象管理(ObjectManager)
1.2 子系统 2:对象移动管理(MovementManager)
1.3 子系统 3:音频管理(AudioManager)
2、实现外观类 GameManagerFacade
3、客户端代码
4、运行示例
六、享元模式(Flyweight Pattern)
1、什么时候使用享元模式
2、使用享元模式的好处
3、使用享元模式时的注意事项
七、在 Unity 中使用 享元模式
1. 定义享元类
2. 创建享元工厂
3. 客户端代码
4. 场景设置
5、运行示例
八、代理模式(Proxy Pattern)
1、什么时候使用代理模式
2、使用代理模式的好处
3、使用代理模式时的注意事项
九、在 Unity 中使用 代理模式
1、定义组件接口 IModel
2、定义真实对象
3、定义代理类
4、客户端代码
5、 运行示例
一、简单介绍
设计模式 是指在软件开发中为解决常见问题而总结出的一套 可复用的解决方案。这些模式是经过长期实践证明有效的 编程经验总结,并可以在不同的项目中复用。设计模式并不是代码片段,而是对常见问题的 抽象解决方案,它提供了代码结构和模块间交互的一种设计思路,帮助开发者解决特定的设计问题。
设计模式的特点:
- 通用性:设计模式针对的是软件开发中常见的设计问题,适用于各种软件工程项目。
- 可复用性:设计模式可以在不同项目和环境下被重复使用,提高代码的可维护性和扩展性。
- 可扩展性:设计模式有助于让代码结构更加灵活,易于扩展和修改。
- 模块化:通过设计模式,可以减少代码的耦合性,增强模块间的独立性。
- 提高沟通效率:设计模式为开发者提供了一种通用的设计语言,使得团队成员能够快速理解并讨论设计方案。
二、装饰者模式(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(){// 创建一个基本的玩家角色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) 是一种结构型设计模式,提供了一个统一的接口,用来访问子系统中的一群接口。外观模式定义了一个高层接口,使得这一子系统更加容易使用。它封装了子系统的复杂性,简化了客户端的调用。
在复杂的系统中,往往会有许多类和接口相互交织,导致使用起来比较复杂。外观模式通过引入一个外观类,提供一个简单的接口来隐藏子系统的复杂实现,客户端只需通过这个外观类与子系统交互,而不需要直接面对复杂的内部逻辑。
外观模式的组成
- Facade(外观类):提供一个简单的接口,简化与复杂子系统的交互。
- Subsystems(子系统):一组复杂的类、模块或功能,外观类负责封装这些复杂的交互和依赖关系。
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、运行示例
在
Start
方法中:客户端使用GameManagerFacade
创建一个立方体,并在其位置播放音效。在
Update
方法中:每帧更新时,立方体会向目标位置移动,移动的过程由MovementManager
负责。
场景设置
- 音效文件:将一个音频文件(
AudioClip
)拖入FacadePatternExample
的soundClip
字段中。- Unity 场景:在 Unity 场景中运行该脚本,立方体会在场景中创建并开始移动,同时播放音效。
这个示例展示了如何使用外观模式简化 3D 对象的管理和控制,在 Unity 中使用这种模式可以帮助我们更好地组织和管理复杂的子系统。
总之,外观模式的好处
- 简化系统交互:通过
GameManagerFacade
统一管理对象创建、移动、音效播放的操作,客户端不需要了解子系统的细节。- 降低耦合:客户端与各个子系统之间通过外观类交互,降低了耦合性,使得系统更具扩展性和维护性。
- 隐藏复杂性:通过外观类,客户端无需关注子系统的复杂性,专注于使用简化的接口。
使用外观模式的注意事项:
- 职责单一:外观类应该仅负责简化系统接口,而不应该承担过多的业务逻辑。
- 不妨碍直接访问子系统:如果需要,客户端仍然可以直接访问子系统的功能。外观模式提供的是一个简化的选择,而不是替代所有交互。
六、享元模式(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) 是一种结构型设计模式,通过为另一个对象提供一个代理以控制对该对象的访问。代理对象可以在客户端和真实对象之间充当中介,负责访问控制、延迟加载、安全检查等功能。
代理模式通常分为以下几种类型:
- 虚拟代理(Virtual Proxy):用于延迟创建和初始化资源,只有在需要时才创建真实对象。
- 保护代理(Protection Proxy):控制对真实对象的访问,提供安全性和权限管理。
- 远程代理(Remote Proxy):用于表示位于不同地址空间的对象,常用于网络通信。
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 中有效地使用代理模式,以提高资源管理的效率和灵活性。
相关文章:

Unity 设计模式 之 结构型模式 -【装饰者模式】【外观模式】【享元模式】【代理模式】
Unity 设计模式 之 结构型模式 -【装饰者模式】【外观模式】【享元模式】【代理模式】 目录 Unity 设计模式 之 结构型模式 -【装饰者模式】【外观模式】【享元模式】【代理模式】 一、简单介绍 二、装饰者模式(Decorator Pattern) 1、什么时候使用装…...

Linux上Qt安装相关的内容及在QtCreator使用QChart模块需要的配置
引言 下面是Ubuntu上Qt安装相关的内容及在QtCreator使用QChart模块需要的配置。 关于Qt安装及环境 Qt的模块 查看已经安装的模块 sudo apt search qt5-安装新的模块 sudo apt install qt5-svg # 安装Qt SVG模块3.查看qt已经安装了哪些模块 dpkg -l | grep libqt安装qt,…...

lettuce引起的Redis command timeout异常
项目使用Lettuce,在自己的环境下跑是没有问题的。在给客户做售前压测时,因为客户端环境比较恶劣,service服务和中间件服务不在同一机房。服务启动后不一会就会出现Redis command timeout异常。 经过差不多两周的追查,最后没办法把…...

【Hadoop】一、Hadoop入门:基础配置、集群配置、常用脚本
基础设置 网络设置 创建好一个 centos 虚拟机,修改网络配置文件: /etc/sysconfig/network-scripts/ifcfg-ens33修改 BOOTPROTO 为 static 以及添加 IPADDR、GATEWAY、DNS1 TYPE"Ethernet" PROXY_METHOD"none" BROWSER_ONLY&quo…...

Ollama:本地运行大模型【含UI界面】
文章目录 Ollama 简介安装 ollamaWindows 安装Docker 安装其它平台安装支持的模型模型清单模型参数与运行内存快速启动 llama 模型llama 模型介绍运行 llama3.1 模型通过 HTTP API 访问ollama 命令语法常用示例特别示例自定义模型创建 Modelfile创建模型并运行集成 Web 页面Ope…...

【论文阅读】Grounding Language with Visual Affordances over Unstructured Data
Abstract 最近的研究表明,大型语言模型(llms)可以应用于将自然语言应用于各种各样的机器人技能。然而,在实践中,学习多任务、语言条件机器人技能通常需要大规模的数据收集和频繁的人为干预来重置环境或帮助纠正当前的…...

目标检测:滑块验证
最近在做一些爬虫相关的任务,有时候在登录时候需要去做滑块验证,刚好自己是做AI这一块得,就想着使用目标检测去做检测,然后绕过滑块。...

Unreal Engine 5 C++: 编辑器工具编写入门01(中文解释)
目录 准备工作 1.创建插件 2.修改插件设置 快速资产操作(quick asset action) 自定义编辑器功能 0.创建编辑器button,测试debug message功能 大致流程 详细步骤 1.ctrlF5 launch editor 2.创建新的cpp class,derived from AssetAction…...

力扣上刷题之C语言实现-Day2
一. 简介 本文记录一下,力扣C语言逻辑题。主要涉及 数组方面的知识。 二. 涉及数组的 C语言逻辑题 1. 两数之和 给你一个下标从 1 开始的整数数组 numbers ,该数组已按 非递减顺序排列 ,请你从数组中找出满足相加之和等于目标数 target…...

Visual Studio 2022 - QT 环境中文字符乱码问题
Visual Studio 2022 - QT 环境中文字符乱码问题 一、Visual Studio 2022 - Qt 环境 在 QT 中使用中文字符串常会出现乱码现象,如下:以下提供了几个解决方法,仅供参考 QString str "百香果真是一直可爱的小猫咪"; qDebug() <…...

获得ASPICE认证需要满足哪些条件?
要获得ASPICE认证,需要满足以下条件: ( 要明确的是:在ASPICE行业中专业来说,ASPICE项目是没有认证,而只有评估。不过,为了方便沟通,人们常将这一评估过程称为认证。) 一、基础条件…...

鸿蒙_异步详解
参考详细链接: 鸿蒙HarmonyOS异步并发开发指南...

linux日志查询搜索view
view 命令实际上是 vim 编辑器的一个只读模式。当你使用 view 打开一个文件时,实际上是在用 vim 查看该文件,只是不能编辑内容。因此,view 下的搜索操作与 vim 类似。 以下是如何在 view 模式下进行搜索: 启动 view 并打开文件&a…...

性能测试工具——JMeter
目录 一、JMeter介绍 1、下载安装JMeter 2、打开JMeter 方式一: 方式二: 3、JMeter基础设置 4、JMeter基本使用流程 (1)启动JMeter (2)在测试计划下添加线程组 (3)在 “线…...

1.《DevOps》系列K8S部署CICD流水线之部署K8S集群~version1.28.2
架构 服务器IP服务名称硬件配置192.168.1.100k8s-master8核、16G、120G192.168.1.101k8s-node18核、16G、120G192.168.1.102k8s-node28核、16G、120G192.168.1.103nfs2核、4G、500G 操作系统:Rocky9.3 后续通过K8S部署GitLab、Harbor、Jenkins 一、环境准备 关…...

c/c++八股文
c基础 一、指针和引用的区别 定义方式: 指针是通过 * 操作符定义的变量,用于存储另一个变量的地址。例如: int* p &x;引用是通过 & 操作符定义的别名,直接引用另一个变量。例如: int& r x; 内存占用: 指针是一个独立的变量,占用一定的内存空间。引用不是独立的…...

Docker配置代理解决pull超时问题
操作系统: CentOS Linux 8 Docker版本: 26.1.3 前置:你需拥有🐱 1. 配置 proxy.conf 1.1 创建配置文件目录 创建 docker.service.d,进入到 docker.service.d 中打开 proxy.conf (没有文件打开会自动创建)。 注意:每个人的路径可…...

ECharts的特点
ECharts是一款基于JavaScript的数据可视化图表库,由百度团队开源,并于2018年初捐赠给Apache基金会,成为ASF孵化级项目。ECharts提供了直观、生动、可交互、可个性化定制的数据可视化图表,广泛应用于数据分析和展示领域。以下是关于…...

JVM OutOfMemoryError 与 StackOverflowError 异常
目录 前言 堆溢出 虚拟机栈和本地方法栈溢出 方法区溢出 前言 JVM规范中规定, 除了程序计数器之外, 其他的运行时数据区域, 例如堆栈, 方法区, 都会出现OutOfMemoryError异常. 那么到底是怎么样的代码, 才会引起堆溢出, 栈溢出, 或者是方法区的溢出呢? 如果遇到了又该如何…...

linux防火墙学习
Linux 防火墙配置(iptables和firewalld) Linux 防火墙配置(iptables和firewalld)_iptables配置文件位置-CSDN博客 Linux查看防火墙状态及开启关闭命令_linux 查看防火墙-CSDN博客...

Java面试篇基础部分- Java中的阻塞队列
首先队列是一种前进后出的操作结构,也就是说它只允许从队列前端进入,从队列后端退出。这个前端和后端看个人如何理解,也就是通常所说的入队和出队,队头和队尾。 阻塞队列和一般队列的不同就在于阻塞队列是可以阻塞的,这里所说的并不是说队列中间或者队头队尾被拦截了,而是…...

Go语言并发编程之Channels详解
并发编程是Go语言的一大特色,而channel(通道)则是Go语言中用于实现并发的核心工具之一。它源于CSP(Communicating Sequential Processes)的概念,旨在让多个goroutine之间能够高效地进行通信和同步。本文将深入探讨channel的用法、原理和最佳实践,通过丰富的示例代码和详…...

【Java集合】LinkedList
概要 LinkedList是用链表结构存储数据的,很适合数据的动态插入和删除,随机访问速度比较慢。另外,他还提供了 List 接口中没有定义的方法,专门用于操作表头和表尾元素,可以当作堆栈、队列和双向队列使用。 链表 链表是…...

大模型之基准测试集(Benchmark)-给通义千问2.0做测评的10个权威测基准测评集
引言 在去年(2023)云栖大会上,阿里云正式发布千亿级参数大模型通义千问2.0。据现场介绍,在10个权威测评中,通义千问2.0综合性能超过GPT-3.5,正在加速追赶GPT-4。以下是通义千问在MMLU、C-Eval、GSM8K、HumanEval、MATH等10个主流…...

解决selenium爬虫被浏览器检测问题
文章目录 专栏导读1.问题解析2.代码解析(Edge/Chrome通用)2.1 设置Edge浏览器选项:2.2 尝试启用后台模式2.3 排除启用自动化模式的标志2.4 禁用自动化扩展2.5 设置用户代理2.6 实例化浏览器驱动对象并应用配置2.7 在页面加载时执行JavaScript代码 3.完整代码(可直接…...

计算机前沿技术-人工智能算法-大语言模型-最新论文阅读-2024-09-17
计算机前沿技术-人工智能算法-大语言模型-最新论文阅读-2024-09-17 1. Large Language Models in Biomedical and Health Informatics: A Review with Bibliometric Analysis H Yu, L Fan, L Li, J Zhou, Z Ma, L Xian, W Hua, S He… - Journal of Healthcare …, 2024 生物…...

LLM - 理解 多模态大语言模型(MLLM) 的 幻觉(Hallucination) 与相关技术 (七)
欢迎关注我的CSDN:https://spike.blog.csdn.net/ 本文地址:https://spike.blog.csdn.net/article/details/142463789 免责声明:本文来源于个人知识与公开资料,仅用于学术交流,欢迎讨论,不支持转载。 多模态…...

如何在C++中实现RDP协议的屏幕更新功能?
在C++中实现RDP协议的屏幕更新功能涉及多个步骤,包括接收RDP服务器发送的屏幕更新PDU(协议数据单元)、解析这些PDU以获取图像数据,以及将这些图像数据渲染到本地显示设备上。以下是一个简化的流程,指导你如何在C++中处理这一功能: 1. 监听和接收屏幕更新PDU 首先,你的…...

Tornado 是一个 Python 异步网络库和 web 框架
Tornado 是一个 Python 异步网络库和 web 框架,它最初由 FriendFeed 开发,后来被 Facebook 收购并开源。Tornado 因其非阻塞的 I/O 操作和优秀的性能而广受欢迎,特别是在需要处理大量并发连接的应用中。Tornado 的底层实现主要依赖于 Python …...

鹏哥C语言49---第5次作业:选择语句 if 和 switch
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> //---------------------------------------------------------------------------------第 5 次作业:选择语句 if 和 switch //-----------------------------------------------------------------1.输…...