Unity 设计模式 之 结构型模式 -【适配器模式】【桥接模式】 【组合模式】
目录
Unity 设计模式 之 结构型模式 -【适配器模式】【桥接模式】 【组合模式】
2.1 标准材质渲染器 StandardMaterialRenderer
2.2 自定义材质渲染器 CustomMaterialRenderer
一、简单介绍
设计模式 是指在软件开发中为解决常见问题而总结出的一套 可复用的解决方案。这些模式是经过长期实践证明有效的 编程经验总结,并可以在不同的项目中复用。设计模式并不是代码片段,而是对常见问题的 抽象解决方案,它提供了代码结构和模块间交互的一种设计思路,帮助开发者解决特定的设计问题。
二、适配器模式 (Adapter Pattern)
适配器模式 (Adapter Pattern) 是一种结构型设计模式,作用是将一个类的接口转换为客户希望的另一个接口。适配器使得原本由于接口不兼容而不能一起工作的类可以协同工作。
在开发中,适配器模式通常用于解决现有类和新接口不兼容的问题,而无需修改现有代码。它的核心思想是创建一个适配器类,使得不同接口之间的转换变得透明。
适配器模式有两种主要实现方式:
1、什么时候使用适配器模式
2、使用适配器模式的好处
3、适配器模式的注意事项
-
适配器本身需要额外的代码:引入适配器可能会增加系统的复杂性,尤其是当涉及多个适配器时。要权衡适配器的引入是否真正有必要。
-
可能引入性能开销:适配器模式通过引入中间层来解决接口不兼容问题,这可能在某些场景下增加调用的性能开销,特别是在大量频繁的接口调用时。
-
适配的灵活性:适配器的实现应该尽可能保持灵活,避免过于依赖具体的实现类。确保适配器可以应对接口或实现方式的变化。
总之,适配器模式 用于解决不兼容接口之间的转换问题,提供了将现有类复用到新环境中的灵活机制。它的主要好处是提高代码的复用性和扩展性,尤其适合在需要集成旧代码或第三方库时使用。通过适配器模式,开发者可以在不修改原始类的情况下,兼容不同接口,使系统更加灵活和可扩展。
三、在 Unity 中使用 适配器模式
在 Unity 中,我们可以使用适配器模式来处理不同类型的 3D 对象渲染,例如不同的 3D 模型格式(比如 OBJ
、FBX
、GLTF
等)或不同的材质处理方式。通过适配器模式,可以将这些不同的 3D 模型格式转换成统一的接口来使用。
我们将模拟一个场景,其中需要加载不同格式的 3D 模型。通过适配器模式,我们可以创建一个统一的接口,让系统无论加载哪种格式的模型,都会以相同的方式进行操作。
步骤概述:
参考类图如下:
1、定义目标接口 IModelLoader
该接口定义了一个加载模型的方法 LoadModel()
,通过这个接口,我们可以统一管理不同格式的模型加载。
public interface IModelLoader
{
void LoadModel();
}
2、模拟不同的 3D 模型加载器
在这个示例中,我们会模拟两个不同的 3D 模型加载器,一个用于加载 OBJ
模型,另一个用于加载 FBX
模型。
2.1 OBJ 模型加载器
using UnityEngine;
public class ObjModelLoader
{
public void LoadObjModel()
{
Debug.Log("Loading OBJ model...");
// 模拟加载 OBJ 模型
GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
cube.GetComponent<Renderer>().material.color = Color.blue;
}
}
2.2 FBX 模型加载器
using UnityEngine;
public class FbxModelLoader
{
public void LoadFbxModel()
{
Debug.Log("Loading FBX model...");
// 模拟加载 FBX 模型
GameObject sphere = GameObject.CreatePrimitive(PrimitiveType.Sphere);
sphere.GetComponent<Renderer>().material.color = Color.red;
}
}
3、创建适配器
我们将创建适配器类,将 ObjModelLoader
和 FbxModelLoader
适配为统一的 IModelLoader
接口。
3.1 OBJ 模型适配器
public class ObjModelAdapter : IModelLoader
{
private ObjModelLoader objModelLoader;
public ObjModelAdapter(ObjModelLoader loader)
{
objModelLoader = loader;
}
public void LoadModel()
{
objModelLoader.LoadObjModel();
}
}
3.2 FBX 模型适配器
public class FbxModelAdapter : IModelLoader
{
private FbxModelLoader fbxModelLoader;
public FbxModelAdapter(FbxModelLoader loader)
{
fbxModelLoader = loader;
}
public void LoadModel()
{
fbxModelLoader.LoadFbxModel();
}
}
4、在 Unity 场景中使用适配器模式
using UnityEngine;
public class ModelLoaderExample : MonoBehaviour
{
void Start()
{
// 使用 OBJ 模型适配器加载 OBJ 模型
ObjModelLoader objLoader = new ObjModelLoader();
IModelLoader objModelAdapter = new ObjModelAdapter(objLoader);
objModelAdapter.LoadModel(); // 加载并显示 OBJ 模型
// 使用 FBX 模型适配器加载 FBX 模型
FbxModelLoader fbxLoader = new FbxModelLoader();
IModelLoader fbxModelAdapter = new FbxModelAdapter(fbxLoader);
fbxModelAdapter.LoadModel(); // 加载并显示 FBX 模型
}
}
5、运行结果分析
通过适配器模式,我们将不同的 3D 模型加载逻辑统一到了 IModelLoader
接口,使得客户端可以以相同的方式处理不同格式的模型,而无需关心底层的具体实现。
通过适配器模式,我们能够将现有的类和接口兼容起来,简化系统中不同类之间的交互。在 Unity 中,适配器模式可以用于处理不同的资源格式(如模型、音频、材质等)的加载和使用。
它极大地提高了代码的复用性和可扩展性,特别是在处理与第三方库或遗留代码时。通过适配器,客户端代码可以以统一的方式使用不同实现,降低了代码的耦合度。
适配器模式特别适合在 Unity 中处理多种资源加载、外部库集成和扩展功能时使用。例如,将外部导入的模型格式或资产类型转换为系统能够理解和处理的标准格式。
四、桥接模式(Bridge Pattern)
桥接模式(Bridge Pattern) 是一种结构型设计模式,旨在将抽象部分与其实现部分分离,使它们可以独立变化。换句话说,桥接模式通过引入一个桥接接口,将类的抽象层次和实现层次分开,从而使得它们能够分别独立地扩展。
桥接模式通过组合(而不是继承)来实现这种分离,这样可以避免类爆炸式的增长(即由于多个抽象和实现的组合导致的大量子类)。
桥接模式的组成部分
1、什么时候使用桥接模式
2、使用桥接模式的好处
五、在 Unity 中使用 桥接模式
在 Unity 中,桥接模式可以用于将 渲染方式 与 3D 对象的形状 分离,从而实现更灵活的渲染系统。例如,我们可以有不同的渲染器(比如使用 标准材质 或 自定义材质 进行渲染),同时也可以有不同的形状(如 立方体、球体 等)。桥接模式允许我们将这些渲染器和形状解耦,使它们能够独立扩展。
使用桥接模式在 Unity 中渲染 3D 对象
我们将设计一个系统,能够以不同的方式渲染不同形状的 3D 对象。使用桥接模式,将渲染逻辑和形状解耦。渲染方式可以是使用标准材质渲染,也可以是自定义材质渲染。形状包括立方体和球体。
参考类图如下:
1、定义渲染接口 IRenderer
这个接口定义了一个 Render
方法,接受一个形状名称作为参数,用于渲染对应的形状。
public interface IRenderer
{
void Render(string shape);
}
2、实现具体的渲染类
2.1 标准材质渲染器 StandardMaterialRenderer
using UnityEngine;
public class StandardMaterialRenderer : IRenderer
{
public void Render(string shape)
{
Debug.Log($"Rendering {shape} with Standard Material");
// 创建 Unity 原生物体并应用标准材质
GameObject obj = shape == "Cube" ? GameObject.CreatePrimitive(PrimitiveType.Cube) : GameObject.CreatePrimitive(PrimitiveType.Sphere);
obj.GetComponent<Renderer>().material = new Material(Shader.Find("Standard"));
}
}
2.2 自定义材质渲染器 CustomMaterialRenderer
using UnityEngine;
public class CustomMaterialRenderer : IRenderer
{
public void Render(string shape)
{
Debug.Log($"Rendering {shape} with Custom Material");
// 创建 Unity 原生物体并应用自定义材质(如带颜色的材质)
GameObject obj = shape == "Cube" ? GameObject.CreatePrimitive(PrimitiveType.Cube) : GameObject.CreatePrimitive(PrimitiveType.Sphere);
Material customMaterial = new Material(Shader.Find("Standard"));
customMaterial.color = Color.green; // 自定义颜色
obj.GetComponent<Renderer>().material = customMaterial;
}
}
3、定义抽象类 Shape
Shape
类是抽象类,持有一个 IRenderer
的引用,并定义了 Draw
方法,用于渲染形状。
public abstract class Shape
{
protected IRenderer renderer;
public Shape(IRenderer renderer)
{
this.renderer = renderer;
}
public abstract void Draw();
}
4、创建具体的形状类
4.1 立方体类 Cube
public class Cube : Shape
{
public Cube(IRenderer renderer) : base(renderer) {}
public override void Draw()
{
renderer.Render("Cube");
}
}
4.2 球体类 Sphere
public class Sphere : Shape
{
public Sphere(IRenderer renderer) : base(renderer) {}
public override void Draw()
{
renderer.Render("Sphere");
}
}
5、在 Unity 场景中使用桥接模式
using UnityEngine;
public class BridgePatternExample : MonoBehaviour
{
void Start()
{
// 使用标准材质渲染器渲染立方体
IRenderer standardRenderer = new StandardMaterialRenderer();
Shape cube = new Cube(standardRenderer);
cube.Draw(); // 输出:Rendering Cube with Standard Material
// 使用自定义材质渲染器渲染球体
IRenderer customRenderer = new CustomMaterialRenderer();
Shape sphere = new Sphere(customRenderer);
sphere.Draw(); // 输出:Rendering Sphere with Custom Material
}
}
6、运行分析
- 当
cube.Draw()
被调用时,立方体将使用标准材质渲染。 - 当
sphere.Draw()
被调用时,球体将使用自定义材质(绿色)渲染。
在这个示例中,桥接模式将渲染方式(IRenderer
)和形状(Shape
)进行了分离。通过这种方式,我们可以独立扩展渲染方式和形状。比如你可以为不同的形状添加更多渲染方式(如自发光材质渲染器等),也可以扩展更多的形状,而不需要修改现有代码。
渲染逻辑和形状实现独立变化,增强了系统的扩展性和灵活性。
不同渲染逻辑可以复用,适应不同的形状,而不同形状也可以复用相同的渲染器。
桥接模式适合应用在需要多个维度变化的场景中,例如 Unity 中的渲染系统、音效处理系统、或者动画控制系统。在这些系统中,不同的维度可以独立扩展,而不影响彼此。
六、组合模式(Composite Pattern)
组合模式(Composite Pattern) 是一种结构型设计模式,它将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得客户端对单个对象和组合对象的使用具有一致性。
在组合模式中,叶子对象和容器对象被统一为一个对象接口,客户端可以同样地对待它们。这种模式常用于表示具有层次结构的数据结构,例如文件系统、公司组织结构、GUI 树等。
组合模式的组成部分
1、什么时候使用组合模式
2、使用组合模式的好处
3、使用组合模式时的注意事项
七、在 Unity 中使用 组合模式
在 Unity 中,组合模式可以应用于管理 3D 对象的层次结构,例如组合复杂的游戏对象,如一个包含多个部分的机器人或车辆。这些对象的每个部分(如身体、轮子、头部等)可以作为叶子节点,而整个对象则作为组合节点。
在这个示例中,我们将使用组合模式实现一个场景,其中包含不同的 3D 形状(立方体、球体、圆柱体等),并将它们组合成一个更复杂的对象。我们可以一致地操作单个形状和组合对象。
参考类图如下:
1、定义组件接口 IShape
这个接口定义了一个 Render
方法,用于渲染 3D 对象。
public interface IShape
{
void Render();
}
2、实现叶子节点类
这些类实现了 IShape
接口,用于渲染具体的 3D 形状。
2.1 立方体 Cube
using UnityEngine;
public class Cube : IShape
{
public void Render()
{
Debug.Log("Rendering Cube");
GameObject.CreatePrimitive(PrimitiveType.Cube);
}
}
2.2 球体 Sphere
using UnityEngine;
public class Sphere : IShape
{
public void Render()
{
Debug.Log("Rendering Sphere");
GameObject.CreatePrimitive(PrimitiveType.Sphere);
}
}
2.3 圆柱体 Cylinder
using UnityEngine;
public class Cylinder : IShape
{
public void Render()
{
Debug.Log("Rendering Cylinder");
GameObject.CreatePrimitive(PrimitiveType.Cylinder);
}
}
3、实现组合节点 ShapeGroup
ShapeGroup
是组合节点,它可以包含多个 IShape
对象,并对它们进行统一的渲染。
using System.Collections.Generic;
using UnityEngine;
public class ShapeGroup : IShape
{
private List<IShape> shapes = new List<IShape>();
public void AddShape(IShape shape)
{
shapes.Add(shape);
}
public void RemoveShape(IShape shape)
{
shapes.Remove(shape);
}
public void Render()
{
Debug.Log("Rendering Shape Group");
foreach (var shape in shapes)
{
shape.Render();
}
}
}
4、在 Unity 中使用组合模式
using UnityEngine;
public class CompositePattern3DExample : MonoBehaviour
{
void Start()
{
// 创建单个形状
IShape cube = new Cube();
IShape sphere = new Sphere();
IShape cylinder = new Cylinder();
// 创建组合对象
ShapeGroup complexObject = new ShapeGroup();
complexObject.AddShape(cube);
complexObject.AddShape(sphere);
// 创建更复杂的组合对象
ShapeGroup complexGroup = new ShapeGroup();
complexGroup.AddShape(complexObject);
complexGroup.AddShape(cylinder);
// 渲染所有对象
Debug.Log("Rendering all shapes:");
complexGroup.Render();
}
}
5、运行分析
当运行代码时,Unity 会在控制台输出如下信息,并且在场景中创建相应的 3D 对象:
Rendering all shapes:
Rendering Shape Group
Rendering Cube
Rendering Sphere
Rendering Cylinder
在场景中,你会看到一个立方体、球体和圆柱体被渲染出来。通过组合模式,我们可以轻松地将单个对象和组合对象进行统一处理,无需为每个对象写单独的渲染代码。
在这个示例中,组合模式允许我们将多个 3D 形状组合在一起,构建一个更复杂的 3D 对象层次结构,并能够一致地操作它们;客户端可以一致地处理单个对象和组合对象,无需关心它们是单一形状还是组合体;可以很方便地向组合对象中添加更多的形状,扩展性非常好。
组合模式适合用在需要表示层次结构的场景中,例如场景中的 3D 物体组合、游戏中的层次化对象管理等;要注意避免过度嵌套结构,尤其是在处理非常复杂的 3D 对象时,树形结构的深度可能会影响性能。