组合模式
组合模式
定义
组合模式(Composite)
主要用来处理一类具有“容器特征”的对象——即它们在充当对象的同时,又可以作为容器包含其他多个对象。
成员:
节点抽象:Component
叶子结点:Leaf,继承自Component
枝干节点:Composite,继承自Composite
解释:
其实就是一个树形对象,类似于Unity的Transform,可以通过这个对象获取到他的子物体的对象
对于叶子结点(Leaf):不能添加、移除叶子结点
对于枝干节点(Composite):可以添加、移除其他的叶子/枝干节点
UML
PS:
透明模式 将Add和Remove方法定义在抽象(Component)类中
优点:对Leaf和Composite不加区分,使用Component统一调用
缺点:Leaf需要实现对应的函数,并添加判错处理
安全模式 将Add和Remove方法定义在枝干(Composite)类中
优点:1.可以防止leaf节点对方法的错误调用
2.leaf不需要实现Add和Remove方法或对这两个方法的判错处理
缺点:对于要添加节点的枝干(Composite)节点,必须明确转换为Composite
代码
抽象节点(Component)
namespace DesignModel.Composite
{
//透明模式
public abstract class Component
{
protected string _name;
public Component(string name)
{
this._name = name;
}
public abstract void Add(Component c);
public abstract void Remove(Component c);
public abstract void Display(int depth);
}
//另一种定义方法 安全模式
// public abstract class Component
// {
// public abstract void Display(int depth);
// }
}
枝干节点(Composite)和叶子节点(Leaf)
namespace DesignModel.Composite
{
//定义枝干节点
public class Composite : Component
{
private List<Component> _children = new List<Component>();
public Composite(string name):base(name)
{
}
public override void Add(Component c)
{
if (!_children.Contains(c))
_children.Add(c);
}
public override void Remove(Component c)
{
if (_children.Contains(c))
_children.Remove(c);
}
public override void Display(int depth)
{
Debug.Log(new String('_', depth) + _name);
foreach (var item in _children)
{
item.Display(depth + 1);
}
}
}
//定义叶子节点
public class Leaf : Component
{
public Leaf(string name):base(name)
{
}
public override void Add(Component c)
{
Debug.LogWarning("Leaf Can't Add leaf");
}
public override void Remove(Component c)
{
Debug.LogWarning("Leaf Can't Remove Leaf");
}
public override void Display(int depth)
{
Debug.Log(new String('_', depth) + _name);
}
}
}
简单测试
using DesignModel.Composite;
public class Client_Composite : MonoBehaviour
{
void Start ()
{
DesignModel.Composite.Component root = new Composite("root");
root.Add(new Leaf("Leaf: 1"));
root.Add(new Leaf("Leaf: 2"));
Composite comp1 = new Composite("Composite: 3");
Composite comp2 = new Composite("Composite: 4");
root.Add(comp1);
root.Add(comp2);
comp1.Add(new Leaf("Leaf: 31"));
comp1.Add(new Leaf("Leaf: 32"));
Composite comp3 = new Composite("Composite: 41");
comp2.Add(comp3);
comp2.Add(new Leaf("Leaf : 42"));
comp3.Add(new Leaf("Leaf : 411"));
comp3.Add(new Leaf("Leaf : 412"));
root.Display(1);
}
}
测试结果
拓展
通过观察组合模式的UML图,我发现组合模式和昨天思考的装饰模式UML非常类似
装饰模式
那么二者的区别在哪里呢?
从定义上看
装饰模式是为了给原对象添加“装饰”,通过装饰模式层层嵌套后,我们可以为一个基本对象添加很多的额外的属性或功能
组合模式是为了处理一类具有“容器特征”的对象,组合模式层层嵌套后,我们取任意一个枝干节点,通过递归可以获得所有整个枝干
装饰模式是装饰一个"对象"
组合模式是处理一个"容器"
从实现上看
相同:组合模式和装饰模式都是继承并持有了他们的抽象
不同:
装饰模式至少嵌套持有一个基本对象,通过最外层包装,可以一层一层的回朔到基本对象
组合模式持有零个或多个儿子对象,通过任意枝干节点,可以获取整个枝干的所有子孙枝干和叶子对象
实际上:
在组合模式中,添加一个parent属性,就可以通过任意节点回朔到根节点(类似Transform)
组合模式也至少含有一个根节点
结合使用
组合模式和装饰模式可以结合使用(二者的实现区别不大)
1.将装饰的基本对象定义为根节点
2.将包装层次以树形区分
3.同层次的不同包装可以多次添加
举例:
根节点为人(Human)
头、上身、下身、左手、右手、左脚、右脚为枝干节点
头也可以细分为五官和毛发等枝干节点
五官可以分为眼睛、鼻子、嘴巴、耳朵等叶子结点
通过对所有节点的实现和添加,可以获取一个完整的人,这体现了对人的细节进行多层次包装
而取任意节点,可以获取这个节点的枝干,体现了节点的容器属性
#####代码
抽象Person和实现Human
namespace DesignModel.Composite
{
//抽象Person
public abstract class Person
{
protected string _name;
public Person(string name)
{
_name = name;
}
public abstract void Display(int depth);
}
//实现Human
public class Human : Person
{
private PersonComposite c;
public Human(string name, PersonComposite c = null):base(name)
{
this.c = c;
}
public override void Display(int depth = 0)
{
if (c != null)
{
c.Display(depth);
}
}
}
}
节点抽象
namespace DesignModel.Composite
{
public abstract class PersonComponent : Person
{
protected Person parent;
public PersonComponent(string name):base(name){}
public abstract void Add(PersonComponent c);
public abstract void Remove(PersonComponent c);
public virtual void SetParent(PersonComponent c)
{
parent = c;
c.Add(this);
}
}
}
枝干和叶子节点
namespace DesignModel.Composite
{
//枝干
public class PersonComposite : PersonComponent
{
private List<PersonComponent> _children = new List<PersonComponent>();
public PersonComposite(string name):base(name){}
public override void Add(PersonComponent c)
{
if (c != null && !_children.Contains(c))
{
_children.Add(c);
c.SetParent(this);
}
}
public override void Remove(PersonComponent c)
{
if (c != null && _children.Contains(c))
{
_children.Remove(c);
c.SetParent(null);
}
}
public override void Display(int depth)
{
Debug.Log(new String('_', depth) + _name);
foreach (var item in _children)
{
item.Display(depth + 2);
}
}
}
//叶子
public class PersonLeaf : PersonComponent
{
public PersonLeaf(string name):base(name){}
public override void Add(PersonComponent c)
{
Debug.Log("Eye is Leaf! Can't Add Child!");
}
public override void Remove(PersonComponent c)
{
Debug.Log("Eye is Leaf! Can't Remove Child");
}
public override void Display(int depth)
{
Debug.Log(new String('_', depth) + _name);
}
}
}
简单测试
public class Client_Composite_Example2 : MonoBehaviour
{
void Start ()
{
PersonComposite root = new PersonComposite("root");
Human human = new Human("man", root);
PersonComposite head = new PersonComposite("Head");
head.Add(new PersonLeaf("Eye"));
head.Add(new PersonLeaf("Ear"));
head.SetParent(root);
human.Display();
}
}
结果
我们也可以将头或躯干等继承自PersonComposite去实现独有装饰功能,眉毛等继承自PersonLeaf去实现独有功能