组合(Composite)模式将对象组合成树形结构以表示“部分-整体”的层次结构。这样使得用户对单个对象和组合对象的使用具有一致性。
组合模式,听名字你可能比较陌生,但是你很可能是这种模式的用户,它的应用非常广泛,例如IOS里的UIView、Cocos2d-x里的Node以及Unity3d里的Transform都是典型的组合模式。
本文简单举个例子,假设我们要开发一款机甲类的游戏,机甲由身体、手臂、腿等组件组成,这些部件还可以搭载火箭助推器和激光加农炮等组件,那么我们就可以考虑使用组合模式来设计。
首先定义一个机器组件的抽象类:
public abstract class RobotComponent
{
protected RobotComponent _parent = null;
public virtual RobotComponent parent{
get
{
return _parent;
}
set
{
if (_parent == value) {
return;
}
if (_parent != null)
{
_parent.RemoveComponentFromList (this);
}
_parent = value;
if (_parent != null)
{
_parent.AddComponentToList (this);
}
}
}
public virtual void RemoveFromParent ()
{
parent = null;
}
protected List<RobotComponent> _children = new List<RobotComponent> ();
protected virtual void AddComponentToList(RobotComponent component)
{
if (!CanAddComponent (component)) {
return;
}
if (!_children.Contains (component)) {
_children.Add (component);
}
}
public virtual RobotComponent AddComponent(RobotComponent component)
{
component.parent = this;
return component;
}
public RobotComponent AddComponent<T> () where T : RobotComponent, new()
{
RobotComponent component = new T ();
return AddComponent (component);
}
protected virtual void RemoveComponentFromList (RobotComponent component)
{
if (!_children.Contains (component)) {
return;
}
_children.Remove (component);
component._parent = null;
}
protected virtual bool CanAddComponent(RobotComponent component)
{
return true;
}
}
这个抽象类维护了一个RobotComponent类型的List,用来存放子组件,对外开放了AddComponent和RemoveFromParent方法以及Parent属性。
接着我们可能会考虑到某种类型的组件不能为它添加子组件,例如火箭助推器和激光加农炮,所以我们又定义了一个抽象类——LeafRobotComponent。
public abstract class LeafRobotComponent : RobotComponent
{
public override RobotComponent AddComponent(RobotComponent component)
{
Console.WriteLine ("LeafRobotComponent cannot add component!");
return null;
}
protected override void RemoveComponentFromList (RobotComponent component)
{
Console.WriteLine ("LeafRobotComponent cannot add component! So removing component make no sense!");
}
}
还有,机器人的身体应该不能被添加到其他组件上,所以需要再定义一个抽象类——RootRobotComponent。
public abstract class RootRobotComponent : RobotComponent
{
public override RobotComponent parent{
get
{
return null;
}
set
{
Console.WriteLine ("RootRobotComponent cannot has a parent!");
_parent = null;
}
}
}
这样我们就完成了一个包含根和叶的组合体系了。
然而还没有具体的功能,我们为RobotComponent添加一些属性,并修改CanAddComponent方法。
public abstract class RobotComponent
{
protected RobotComponent _parent = null;
public virtual RobotComponent parent{
get
{
return _parent;
}
set
{
if (_parent == value) {
return;
}
if (_parent != null)
{
_parent.RemoveComponentFromList (this);
}
_parent = value;
if (_parent != null)
{
_parent.AddComponentToList (this);
}
}
}
public virtual void RemoveFromParent ()
{
parent = null;
}
protected List<RobotComponent> _children = new List<RobotComponent> ();
protected virtual void AddComponentToList(RobotComponent component)
{
if (!CanAddComponent (component)) {
return;
}
if (!_children.Contains (component)) {
_children.Add (component);
}
}
public virtual RobotComponent AddComponent(RobotComponent component)
{
component.parent = this;
return component;
}
public RobotComponent AddComponent<T> () where T : RobotComponent, new()
{
RobotComponent component = new T ();
return AddComponent (component);
}
protected virtual void RemoveComponentFromList (RobotComponent component)
{
if (!_children.Contains (component)) {
return;
}
_children.Remove (component);
component._parent = null;
}
protected virtual bool CanAddComponent(RobotComponent component)
{
if (RemainingTech <= component.TechCost) {
Console.WriteLine ("We do not have enough technology");
return false;
}
return true;
}
protected int _attack = 0;
public virtual int Attack
{
get {
int ret = _attack;
foreach (var comp in _children) {
ret += comp.Attack;
}
return ret;
}
}
protected int _speed = 0;
public virtual int Speed
{
get {
int ret = _speed;
foreach (var comp in _children) {
ret += comp.Speed;
}
return ret;
}
}
protected int _techCost = 0;
public virtual int TechCost
{
get {
int ret = _techCost;
foreach (var comp in _children) {
ret += comp.TechCost;
}
return ret;
}
}
protected int _maxTech = 0;
public virtual int RemainingTech
{
get {
int ret = _maxTech;
foreach (var comp in _children) {
ret -= comp.TechCost;
if (ret < 0) {
ret = 0;
break;
}
}
if (_parent != null && ret > _parent.RemainingTech) {
ret = _parent.RemainingTech;
}
return ret;
}
}
}
然后我们定义继承自RootRobotComponent的RobotBody:
public class RobotBody:RootRobotComponent
{
public RobotBody()
{
_maxTech = 100;
}
}
继承自RobotComponent的RobotArm和RobotLeg:
public class RobotArm : RobotComponent
{
public RobotArm()
{
_attack = 10;
_techCost = 20;
_maxTech = 10;
}
}
public class RobotLeg : RobotComponent
{
public RobotLeg()
{
_attack = 5;
_speed = 5;
_techCost = 20;
_maxTech = 10;
}
}
以及继承自LeafRobotComponent的RocketBooster和LaserCannon:
public class RocketBooster : LeafRobotComponent
{
public RocketBooster()
{
_techCost = 3;
}
public override int Speed
{
get {
return 2;
}
}
}
public class LaserCannon : LeafRobotComponent
{
public LaserCannon()
{
_techCost = 3;
}
public override int Speed
{
get {
return -1;
}
}
public override int Attack
{
get {
return 3;
}
}
}
然后我们就可以组装机甲了:
RobotComponent robot = new RobotBody ();
RobotComponent arm1 = new RobotArm ();//robot.AddComponent<RobotArm> ();
RobotComponent arm2 = robot.AddComponent<RobotArm> ();
RobotComponent leg1 = new RobotLeg ();//robot.AddComponent<RobotLeg> ();
RobotComponent leg2 = robot.AddComponent<RobotLeg> ();
arm1.AddComponent<LaserCannon> ();
arm2.AddComponent<LaserCannon> ();
leg1.AddComponent<RocketBooster> ();
leg2.AddComponent<RocketBooster> ();
robot.AddComponent<RocketBooster> ();
robot.AddComponent<RocketBooster> ();
robot.AddComponent (arm1);
robot.AddComponent (leg1);
robot.AddComponent<LaserCannon> ();
Console.WriteLine (robot.RemainingTech);
Console.WriteLine (robot.Speed);
Console.WriteLine (robot.Attack);
输出:
We do not have enough technology
2
16
36
组合模式的优点:
1、定义了组合对象的层次结构,可以不断的递归产生各种复杂的组件。
2、简化了用户的代码,用户可以一致地使用组合组件和单个组件。
3、易于增加新类型的组件,新组件添加后可以和原来的组件一起使用。
缺点:
很难限制组合中的组件:有时候你可能希望一个组合只能包含某些特定的组件,就只能在运行时进行检查。