0
点赞
收藏
分享

微信扫一扫

在IDEA中创建好项目之后再引入新的依赖

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) 是一种结构型设计模式,允许动态地为对象添加新的功能,而不改变其原有的结构。它通过创建一个装饰类,包装原始对象,以扩展对象的功能。这种模式非常灵活,因为可以在不修改现有类的情况下动态地添加功能。

装饰者模式的核心思想是将功能封装到独立的类中,通过这些类来为对象提供额外的行为。它避免了继承带来的类爆炸问题,因为不需要创建大量的子类去实现不同的功能组合。


装饰者模式的组成部分

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、运行结果分析

  1. 在控制台中,你将看到以下输出:

    Displaying Player Character
    Adding Armor to Character
    Adding Weapon to Character
    
  2. 在 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 封装了 ObjectManagerMovementManagerAudioManager,为客户端提供一个统一的接口:

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、运行示例

  1. FlyweightExample 脚本添加到一个空的 GameObject。
  2. 运行场景,你将看到两种类型的树木(橡树和松树)在场景中生成,使用享元模式有效地减少了内存使用。

这个示例展示了如何在 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、 运行示例

  1. 在 Unity 中,创建一个空的 GameObject,并将 ProxyPatternExample 脚本附加到该对象上。

  2. 运行场景,你会看到第一次调用 Display 方法时,真实模型被加载,而第二次调用时则直接显示,不再加载。

代理模式的实现,通过 ProxyModel 控制对 RealModel 的访问,实现延迟加载和资源管理;适用场景,当加载 3D 模型或其他资源消耗较大时,代理模式能够优化性能,减少内存占用;示例说明,这个示例展示了如何在 Unity 中有效地使用代理模式,以提高资源管理的效率和灵活性。

举报

相关推荐

0 条评论