组合模式

组合模式

定义

组合模式(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去实现独有功能

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值