参考材料
1. 【UE4 设计模式】策略模式 Strategy Pattern
2. 组合模式
3. UE开发中的设计模式(四) —— 组合模式
1. 概述
1.1 描述
$\cdot$ 组合模式(Composite Pattern), 又叫部分整体模式, 是用于把一组相似的对象当作一个单一的对象. 组合模式依据树形结构来组合对象, 用来表示部分以及整体层次. 这种类型的设计模式属于结构型模式, 它创建了对象组的树形结构.
$\\$ 这种模式创建了一个包含自己对象组的类. 该类提供了修改相同对象组的方式.
1.2 套路
$\cdot$ 实现方式:
$\\$ 1) 统一接口: 定义一个接口, 所有对象(树枝和叶子) 都实现这个接口.
$\\$ 2) 组合结构: 树枝对象包含一个接口的引用列表, 这些引用可以是叶子或树枝.
$\cdot$ 关键代码:
$\\$ 1) Component接口:定义了所有对象必须实现的操作.
$\\$ 2) Leaf类: 实现Component接口, 代表树中的叶子节点.
$\\$ 3) Composite类: 也实现Component接口, 并包含其他Component对象的集合.
$\cdot$ 结构:
$\\$ 组合模式的核心角色包括:
$\\$ 1) 组件(Component): 定义了组合中所有对象的通用接口, 可以是抽象类或接口. 它声明了用于访问和管理子组件的方法, 包括添加, 删除, 获取子组件等.
$\\$ 2) 叶子节点(Leaf): 表示组合中的叶子节点对象, 叶子节点没有子节点. 它实现了组件接口的方法, 但通常不包含子组件.
$\\$ 3) 复合节点(Composite): 表示组合中的复合对象, 复合节点可以包含子节点, 可以是叶子节点, 也可以是其他复合节点. 它实现了组件接口的方法, 包括管理子组件的方法.
$\\$ 4) 客户端(Client): 通过组件接口与组合结构进行交互, 客户端不需要区分叶子节点和复合节点, 可以一致地对待整体和部分.
1.3 使用场景
$\cdot$ 当需要表示对象的层次结构时, 如文件系统或组织结构.
$\\$ $\cdot$ 当希望客户端代码能够以一致的方式处理树形结构中的所有对象时.
1.4 优缺点
$\cdot$ 优点:
$\\$ 1) 简化客户端代码: 客户端可以统一处理所有类型的节点.
$\\$ 2) 易于扩展: 可以轻松添加新的叶子类型或树枝类型.
$\cdot$ 缺点:
$\\$ 1) 违反依赖倒置原则: 组件的声明是基于具体类而不是接口, 这可能导致代码的灵活性降低.
2. UE5实践
2.1 问题提出
考虑这样一个场景, 我们有一个敌人的基类, 其包含一些公有的属性和方法, 比如他有一个Mesh(Static Mesh / Skeletal Mesh), 他在世界空间中有一个位置, 他可以攻击我们的玩家. 然而对于不同类型的敌人, 他可能有自己独特的属性和方法, 比如有些怪可以给己方加血, 有些怪会飞, 有些怪会上buff…
$\\$ 一种常见的设计方法是通过继承实现.

从上面的UML图可以看出, EnemyClassA可以飞, 可以上buff, EnemyClassB可以上buff, 可以治疗队友, EnemyClassC可以治疗队友, 可以飞. 想象一下, 如果再有一个新的方法, 比如可以隐身, 那么可能又会产生三个新的基类(考虑两种独有的方法), 但其实完全不必要, 因为fly, buff, treat明显是重复实现了好多次.
$\\$ 那么有什么办法可以解决这个问题吗.
2.2 概述
这时候就需要组合模式闪亮登场了, 组合模式通过少继承多组合的方式, 可以提高代码复用, 且我们想给一个类增加一个新功能, 直接给他加一个组件就行了, 很方便.
$\\$在UE里也是用了组件, 比如UE里的移动组件, 网格组件, 摄像机组件, 一个Actor可以说是一个组件的容器, 组件给Actor提供了各种技能.
$\\$ 且组件也支持嵌套, 即组件可以包含很多的子组件, 比如UE里的USceneComponent, 其有一个AttachParent为父节点, 还有一个AttachChildren保存多个子节点, 明显是一个树形结构. 这种设计哲学, 感觉更像是这些组件组合起来, 成为了一个我们想要的类, 而不是像继承一样, 通过分类的方式, 子类相较于父类多了什么.

这种设计模式也很适合用来设计UI, UI本身也可以看作一个树形结构, 每一个组件提供了一些功能, 其下也能包含其它的一些小的组件.
2.3 问题解决
我们用组合的方式来解决一开始敌人的场景问题, 我们可以有飞行组件, 治疗组件, 加buff组件, 那么对于一个具体的敌人, 我们只需要添加对应的组件即可.
$\\$ 这个场景比较简单, 且有明显的继承关系, 个人感觉其实用继承更合适. 但其实好多类他们没有继承关系, 比如玩家和敌人, 没有明显的继承关系, 但都可以行走, 那么完全可以独立出一个移动组件, 而UE本身也是这么做的.
$\\$ 而且组合模式, 可以动态地增减节点, 对应的就是增加/减少功能, 更加灵活(更低的耦合). 比如玩家捡到一把枪, 那么玩家就获得了开火射击的技能. 枪也相当于组件挂载到玩家这个整体组件的树形结构上.
$\\$ UE文档里有一个很好的例子, 我觉得构建成一个树形结构, 一个好处就是我们可以将组件的变换定义在其父组件的局部空间中, 另一个好处就是更明确了整体和局部的关系.

————————————————
版权声明:本小节内容来源于CSDN博主「hhhcbw」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_44491423/article/details/141688163