设计模式之Composite(组合)模式

1、出现原因

1在面向对象系统中,我们常会遇到一类具有“容器”特征的对象——即它们在充当对象的同时,又是其他对象的容器

如何将“客户代码与复杂的对象容器结构解耦(将这种组合容器对象设计成树形结构,从而可以对下面所有的容器都可以通过最上层 的根对象 实现 统一 的调用,进而客户端就不在乎 其内部是怎么实现的,耦合 降低了)?让对象容器自己来实现自身的复杂结构,从而使得客户代码就像处理简单对象一样来处理复杂的对象容器?

2、意图

将对象组合成树形结构以表示“部分-整体”的层次结构。Composite使得用户对单个对象和组合对象的使用具有一致性。(通过 树根对象的 执行方法,从而调用 他下面的所有的子节点)

3、结构图

4、代码演示 

业务实现:

  1     /// <summary>
  2     /// 抽象的统一 入口
  3     /// </summary>
  4     public abstract class AbsCompany
  5     {
  6         protected string Name { set; get; }
  7 
  8         public AbsCompany(string str)
  9         {
 10             this.Name = str;
 11         }
 12 
 13         public abstract void Add(AbsCompany item);
 14 
 15         public abstract void Remove(AbsCompany item);
 16 
 17         public abstract void Show();
 18 
 19         public abstract void DoDuty();
 20 
 21         //增加子节点的 方式可以 设置 父节点属性
 22         public AbsCompany Father { set; get; }
 23 
 24     }
 25 
 26     /// <summary>
 27     /// 叶子节点1
 28     /// </summary>
 29     public class HRPart : AbsCompany
 30     {
 31         public HRPart(string name)
 32             : base(name)
 33         {
 34         }
 35         //叶子节点不能实现 增删节点操作
 36         public override void Add(AbsCompany item)
 37         {
 38             throw new InvalidOperationException("叶子节点不允许操作");
 39         }
 40 
 41         public override void Remove(AbsCompany item)
 42         {
 43             throw new InvalidOperationException("叶子节点不允许操作");
 44         }
 45 
 46         public override void Show()
 47         {
 48             Console.WriteLine("人力资源部:"+base.Name);
 49         }
 50 
 51         public override void DoDuty()
 52         {
 53             Console.WriteLine(base.Name+":致力于招募人才");
 54         }
 55     }
 56 
 57 
 58     /// <summary>
 59     /// 叶子节点2
 60     /// </summary>
 61     public class MoneyPart : AbsCompany
 62     {
 63         public MoneyPart(string name)
 64             : base(name)
 65         {
 66         }
 67 
 68         //叶子节点不能实现 增删节点的操作
 69         public override void Add(AbsCompany item)
 70         {
 71             throw new InvalidOperationException("叶子节点不允许操作");
 72         }
 73 
 74         public override void Remove(AbsCompany item)
 75         {
 76             throw new InvalidOperationException("叶子节点不允许操作");
 77         }
 78 
 79         public override void Show()
 80         {
 81             Console.WriteLine("财务部分:"+base.Name);
 82         }
 83 
 84         public override void DoDuty()
 85         {
 86             Console.WriteLine(base.Name+":致力于管理好公司的财务");
 87         }
 88     }
 89 
 90     /// <summary>
 91     /// 树形节点(Composite)
 92     /// </summary>
 93     public class Company : AbsCompany
 94     {
 95         public Company(string name)
 96             : base(name)
 97         {
 98         }
 99 
100         //对应 上级节点是 聚合关系
101         private List<AbsCompany> items = new List<AbsCompany>();
102 
103 
104         public override void Add(AbsCompany item)
105         {
106             items.Add(item);
107 
108             //父节点方式:增加子节点
109             item.Father = this;
110         }
111        
112         public override void Remove(AbsCompany item)
113         {
114             items.Remove(item);
115 
116             //父节点 方式 :删除子节点:
117             item.Father = null;//此节点没有父节点了,因此它的父节点下面就没有这个子节点了
118         }
119         /// <summary>
120         /// 实现了加载  次节点下面的所有节点:递归
121         /// (**)调用的顺序:当 调用到一个子节点,会将他的下面 子节点全都调用完成之后,才调用他的兄弟节点
122         /// </summary>
123         /// <param name="item"></param>
124         public override void Show()
125         {
126             //先显示自己
127             Console.WriteLine("公司名称:"+base.Name);
128 
129             //显示下面所有的子节点的 名称
130             foreach (var item in items)
131             {
132                 item.Show();
133             }
134 
135             //注意:要是使用 父节点的方式 实现树形组合:这个时候递归遍历下面所有的子节点的方式就不能执行了
136         }
137 
138         public override void DoDuty()
139         {
140             //先执行自己的职责
141             Console.WriteLine(base.Name+":致力于管理好整个公司");
142 
143             //在执行 他的节点下面的所有的节点的 所有的职责
144             foreach (var item in items)
145             {
146                 item.DoDuty();
147             }
148 
149         }
150     }
Composite模式

客户端调用代码:

 1             //都是通过AbsCompany,后面创建的过程 可以通过 反射进行创建。所以客户 就可以直接 通过树形节点的 最顶层节点 就行创建下面一系列的节点,不用关心具体的实现  (耦合 大大降低)
 2             AbsCompany HeadCompany = new Company("阿里巴巴");
 3             AbsCompany hr1 = new HRPart("总公司人力资源部");
 4             AbsCompany money1 = new MoneyPart("总公司财务部");
 5             AbsCompany company1 = new Company("上海分公司");
 6             AbsCompany company2 = new Company("北京办事处");
 7             AbsCompany hr2 = new HRPart("上海分公司人力资源部");
 8             AbsCompany money2 = new MoneyPart("上海分公司财务部");
 9             AbsCompany hr3 = new HRPart("北京人力资源部");
10             AbsCompany money3 = new MoneyPart("北京财务部门");
11             AbsCompany company3 = new Company("深圳办事处");
12             AbsCompany hr4 = new HRPart("深圳人力资源部");
13             AbsCompany money4 = new MoneyPart("深圳财务部");
14             HeadCompany.Add(company1);
15             HeadCompany.Add(hr1);
16             HeadCompany.Add(money1);
17             company1.Add(hr2);
18             company1.Add(money2);
19             company1.Add(company2);
20             company1.Add(company3);
21             company2.Add(hr3);
22             company2.Add(money3);
23             company3.Add(hr4);
24             company3.Add(money4);
25             HeadCompany.Show();
26             HeadCompany.DoDuty();
客户端调用代码

每个对象的创建可以通过反射的方式进行创建 

5、实现Composite模式两种方式

透明方式和安全模式

7、适用性

两个或者多个类有相似的形式,或者共同代表某个完整概念,(所有的类都有几乎相同的成员,并且叶子节点可以含有多个)外界的用户也希望他们合而为一(通过一个抽象的基类和接口 就可以 指向所有的类,用户不用关心具体的实现),就可以把这几个类“组合”起来,成为一个新的类,用户只需要调用这个新的类就可以了。

8、总结

1Composite模式采用树形结构实现普遍存在的对象容器,从而将“一对多”的关系转化“一对一”的关系,使得客户代码可以一致地处理对象和对象容器,无需关心处理的是单个的对象还是组合的对象容器。(因为这些对象都有几乎相同的结构,所以可以通过抽象基类或接口 进行 调用)

 

2将“户代码与复杂的对象容器结构解耦(因为所有的类含有 几乎相同的 结构,所以可以通过抽象基类或 接口进行调用,所以用户 只需直接 调用,不许关心 其内部是怎么实现的,耦合大大降低)是Composite模式的核心思想,解耦之后,客户代码将与纯粹的抽象接口——而非对象容器的内部实现结构——发生依赖关系,从而更能“应对变化”。(客户端和抽象接口发生依赖关系)

 

3Composite模式中,是将“AddRemove等和对象容器相关的方法”定义在“表示抽象对象的Component类”中,还是将其定义在“表示对象容器的Composite类”中,是一个关乎“透明性”和“安全性”的两难问题需要仔细权衡。这里有可能违背面向对象的“单一职责原则”(基于透明性:则违背;基于安全性:则遵守),但是对于这种特殊结构,这又是必须付出的代价。

 

4、Composite模式在具体实现中,可以让父对象中的子对象反向追溯;如果父对象有频繁的遍历需求,可使用缓存技巧来改善效率。

5、Composite模式一般都 和 Builder 模式组合起来使用(Builder模式:http://www.cnblogs.com/xiaoxiaogogo/p/3572618.html),每个对象的创建通过Builder模式进行创建(将表示和创建隔离,达到解耦的目的)

原文地址:https://www.cnblogs.com/xiaoxiaogogo/p/3590977.html