Design Patterns之Composite Pattern总结
本文总结一下今天学习的Composite(组合)模式。内容主要基于李建忠WebCast《C#面向对象设计模式纵横谈》。
首先,看一下Composite模式的意图:将对象组合成树形结构以表示“部分-整体”的层次结构。Composite模式使得用户对单个对象和组合对象的使用具有一致性。――GOF 《设计模式》
我们看这们个例子:我们有两类盒子,设为A类和B类,A类盒子的内部还装有盒子,而B类盒子内部不能装盒子,A类盒内部装的盒子既可以是A类的也可以是B类的,假设现在桌子上放有许多盒子(可能是A类或B类盒子),要求计算桌子上的总盒子数,如果是A类盒子,则需要算上它内部的盒子数,比如桌上有1个A类盒子box1和1个B类盒子box2,A类盒子内有1个B类盒子box3和1个A类盒子box4,这个box4内又装有3个B类盒子box5,box6,box7,那么总盒子数就是7。
那现在要怎么做呢?A、B两类盒子都是盒子,那我们就定义一个盒子接口,GetBoxCount()方法将返回此盒子内的所有盒子数。
public interface IBox
{
//取得盒子总数
int GetBoxCount();
}
然后是A类和B类盒子:
/// <summary>
/// 表示B类盒子,内部不能再装盒子,用SingleBox作类名,一会不容易搞混
/// </summary>
public class SingleBox : IBox
{
//取得盒子总数
public int GetBoxCount()
{
return 1;
}
}
/// <summary>
/// A类盒子,内部可再装盒子,用“盒子容器”作类名
/// </summary>
public class BoxContainer : IBox
{
//private List<SingleBox> boxes;
private List<IBox> boxes;
//添加盒子
public void Add(IBox box)
{
if (boxes == null)
{
//boxes = new List<SingleBox>();
boxes = new List<IBox>();
}
boxes.Add(box);
}
//删除盒子
public void Remove(IBox box)
{
if ( (boxes == null) || (boxes.Count == 0) )
{
throw new Exception("Sorry,已经没有盒子可Remove了");
}
boxes.Remove(box);
}
//取得盒子总数
public int GetBoxCount()
{
int count = 1; //因为BoxContainer本身就是一个盒子,所以初始化为1
if ( (boxes != null) && (boxes.Count > 0) )
{
foreach (IBox box in boxes)
{
count += box.GetBoxCount();
}
}
return count;
}
}
然后就可以在Main()方法中测试了:
public static void Main(string[] args)
{
//新建4个内部不能装盒子的盒子
IBox box1 = new SingleBox();
IBox box2 = new SingleBox();
IBox box3 = new SingleBox();
IBox box4 = new SingleBox();
//新建 2个内部可以装盒子的盒子
BoxContainer boxContainer1 = new BoxContainer();
BoxContainer boxContainer2 = new BoxContainer();
//把box2,box3装入boxContainer2
boxContainer2.Add(box2);
boxContainer2.Add(box3);
//把box4和boxContainer2装入boxContainer1
boxContainer1.Add(box4);
boxContainer1.Add(boxContainer2);
//求总盒子数并输出
int total = box1.GetBoxCount() + boxContainer1.GetBoxCount();
Console.WriteLine("Total:{0}", total);
}
我们可以得到结果:Total:6
可以看到,虽然A类和B类是不同的盒子,但客户程序却可以不去理会盒子内部还有没有装盒子,装的是什么盒子,只要简简单单地调用GetBoxCount()方法。这样的话,客户程序就可以跟A类盒子(内部可以再装盒子)的内部结构解耦。BoxContainer类就是Composite模式中的组合类,它内部可以再装有盒子。
同时注意到BoxContainer类的私有成员boxes是被声明为List,而不是注释中的List,虽然本例中声明为List也可以,但是当B类盒子不只一种时就不行了,声明为List也体现了面向对象程序设计中依赖抽象而不是依赖实现的思想。
上面的Composite模式叫安全的Composite模式(为什么叫安全的?在段尾见答案),因为对于SingleBox来说,内部不能装盒子,所以没有Add和Remove方法,而BoxContainer有,但这种模式却导致了SingleBox和BoxContainer的接口不一致。还有一种Composite模式叫透明的Composite模式,它把Add和Remove等这些操作子对象的方法定义在IBox接口中,这样接口就一致了(比如在Main方法中就都可以用IBox来声明一个BoxContainer了:IBox boxContainer1 = new BoxContainer();),但是SingleBox里用Add和Remove明显是不行的,所以,在SingleBox的Add和Remove方法中都抛出一个异常,而在安全的Composite模式中是没有这个问题的,所以才叫安全的Composite模式。在实际应用中选择哪种Composite模式,要根据具体情况来看,其实也就是权衡安全性和透明性的问题了。
总 结:
1. Composite(组合)模式解耦了客户程序与复杂元素内部结构,从而使客户程序可以像处理简单元素一样来处理复杂元素。
2. 有安全型的Composite模式和透明型的Composite模式,在它们中做选择也就是在安全性和透明性中找个平衡点。
首先,看一下Composite模式的意图:将对象组合成树形结构以表示“部分-整体”的层次结构。Composite模式使得用户对单个对象和组合对象的使用具有一致性。――GOF 《设计模式》
我们看这们个例子:我们有两类盒子,设为A类和B类,A类盒子的内部还装有盒子,而B类盒子内部不能装盒子,A类盒内部装的盒子既可以是A类的也可以是B类的,假设现在桌子上放有许多盒子(可能是A类或B类盒子),要求计算桌子上的总盒子数,如果是A类盒子,则需要算上它内部的盒子数,比如桌上有1个A类盒子box1和1个B类盒子box2,A类盒子内有1个B类盒子box3和1个A类盒子box4,这个box4内又装有3个B类盒子box5,box6,box7,那么总盒子数就是7。
那现在要怎么做呢?A、B两类盒子都是盒子,那我们就定义一个盒子接口,GetBoxCount()方法将返回此盒子内的所有盒子数。





然后是A类和B类盒子:



















































然后就可以在Main()方法中测试了:




















我们可以得到结果:Total:6
可以看到,虽然A类和B类是不同的盒子,但客户程序却可以不去理会盒子内部还有没有装盒子,装的是什么盒子,只要简简单单地调用GetBoxCount()方法。这样的话,客户程序就可以跟A类盒子(内部可以再装盒子)的内部结构解耦。BoxContainer类就是Composite模式中的组合类,它内部可以再装有盒子。
同时注意到BoxContainer类的私有成员boxes是被声明为List
上面的Composite模式叫安全的Composite模式(为什么叫安全的?在段尾见答案),因为对于SingleBox来说,内部不能装盒子,所以没有Add和Remove方法,而BoxContainer有,但这种模式却导致了SingleBox和BoxContainer的接口不一致。还有一种Composite模式叫透明的Composite模式,它把Add和Remove等这些操作子对象的方法定义在IBox接口中,这样接口就一致了(比如在Main方法中就都可以用IBox来声明一个BoxContainer了:IBox boxContainer1 = new BoxContainer();),但是SingleBox里用Add和Remove明显是不行的,所以,在SingleBox的Add和Remove方法中都抛出一个异常,而在安全的Composite模式中是没有这个问题的,所以才叫安全的Composite模式。在实际应用中选择哪种Composite模式,要根据具体情况来看,其实也就是权衡安全性和透明性的问题了。
总 结:
1. Composite(组合)模式解耦了客户程序与复杂元素内部结构,从而使客户程序可以像处理简单元素一样来处理复杂元素。
2. 有安全型的Composite模式和透明型的Composite模式,在它们中做选择也就是在安全性和透明性中找个平衡点。
分类:
Design Patterns
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架