设计模式学习笔记之 Composite
Composite模式的目的是让用户在处理同一系列的简单和复杂对象时(复杂对象可能包含简单对象),可以采用一致的方式,而不是区别简单和复杂对象再进行不同的操作,即解除了复杂的对象的内部结构与用户代码之间的耦合关系。
在遥远的艾泽拉斯大陆(wow病又发作了),有一个神秘的地方称为奥达曼,那里有一种名为Obsidian Sentinel (黑耀石哨兵)的怪,它在被攻击的时候会不时的从身上掉下小型的Obsidian Sentinel,但是这种小的就不能再分了。我们可以想到,它们应当都属于Obsidian Sentinel,但是一种是复杂的Obsidian Sentinel,可以再分,一种是简单的Obsidian Sentinel,不能再分。比较丑陋的实现是这样的:
public abstract class ObsidianSentinel
{
public abstract void SetObsidianSentinel();
}
public class AtomObsidianSentinel : ObsidianSentinel
{
public override void SetObsidianSentinel()
{
//do sth to descript ObsidianSentinel
}
}
public class ComplexObsidianSentinel : StoneSentinel
{
public override void SetObsidianSentinel()
{
//do sth to descript ObsidianSentinel
}
public override void ChangeToSmall()
{
Console.WriteLine("Change To Small");
}
}

在客户调用的时候则需要判断是AtomObsidianSentinel还是ComplexObsidianSentinel,因为它们的内部调用完全不同,对于ComplexObsidianSentinel来说,有一个AtomObsidianSentinel没有的方法,而且SetObsidianSentinel方法也应该是以一种递归的方法来对其中的每个AtomObsidianSentinel进行描述的,也就是说ComplexObsidianSentinel即是对象,也是AtomObsidianSentinel的容器。
下面我们应用Composite模式来修改代码:
public abstract class ObsidianSentinel
{
public abstract void SetObsidianSentinel();
public abstract void ChangeToSmall();
}
public class ComplexObsidianSentinel : ObsidianSentinel
{
public override void SetObsidianSentinel()
{
//do sth to descript ObsidianSentinel
//foreach(ObsidianSentinel in ComplexObsidianSentinel)
//{
// if (ObsidianSentinel is AtomObsidianSentinel)
// {
// //do sth to descript ObsidianSentinel
// }
// if (ObsidianSentinel is ComplexObsidianSentinel)
// {
// ObsidianSentinel.SetObsidianSentinel()
// }
//}
}
public override void ChangeToSmall()
{
Console.WriteLine("Change To Small");
}
}
public class AtomObsidianSentinel : ObsidianSentinel
{
public override void SetObsidianSentinel()
{
//do sth to descript ObsidianSentinel
}
public override void ChangeToSmall()
{
throw new Exception("Cann't to small");
}
}

我们统一了它们的接口,当不适当的调用ChangeToSmall方法时抛出异常。同时我们把递归描述放到复杂类内部。对用户来说,它们在调用SetObsidianSentinel时不需要了解是哪种对象。这样做的优点在于透明性比较高,因为有统一的接口。缺点在于安全性比较低,因为不适当的调用方法在编译阶段不会出现错误,在运行阶段才会报错。
我们还有另一种应用Composite模式的方法:
public abstract class ObsidianSentinel
{
public abstract void SetObsidianSentinel();
}
public class ComplexObsidianSentinel : ObsidianSentinel
{
public override void SetObsidianSentinel()
{
//do sth to descript ObsidianSentinel
//foreach(ObsidianSentinel in ComplexObsidianSentinel)
//{
// if (ObsidianSentinel is AtomObsidianSentinel)
// {
// //do sth to descript ObsidianSentinel
// }
// if (ObsidianSentinel is ComplexObsidianSentinel)
// {
// ObsidianSentinel.SetObsidianSentinel()
// }
//}
}
public void ChangeToSmall()
{
Console.WriteLine("Change To Small");
}
}
public class AtomObsidianSentinel : ObsidianSentinel
{
public override void SetObsidianSentinel()
{
//do sth to descript ObsidianSentinel
}
}

接口中已不包含复杂对象独有的方法。这样的做法使得安全性提高了,但是却降低了透明性。客户在使用ChangeToSmall方法前必须了解对象是否为复杂对象,否则不能通过编译。
上面两种方法各有利弊,透明和安全是很难两全齐美的。
参考:MSDNWebCast C#设计模式纵横谈 李建忠


浙公网安备 33010602011771号