Composite模式定义了单个对象和组合对象的类层次结构。(1)单个对象组合成复杂的组合对象,而多个组合对象又可以被组合成一个新的组合对象。其中,一个组合对象中的某些对象可能包容了其他对象。某些对象则表现单个基本对象,称为叶对象;某些对象代表一组对象,称为枝对象。 叶对象可以组合成复杂的枝对象,而枝对象又可以被组合成新的枝对象,可以不断组合下去。
Composite模式使用户对单个对象和组合对象的使用具有一致性。客户可以一致地使用组合结构和单个对象,而不用关心处理的是一个叶节点还是一个组合组件。Composite关键是一个抽象类,这个类既可以代表一个对象,也可以代表一个容器来包含一组对象。(1)那么。Composite关键两个思想: 一、一个Composite对象既可包容Leaf对象,也可以其他包容Composite对象。 二、一个Leaf类和一个Composite共享一个在抽象类中定义的公共接口Component。(2)
在以下情况下考虑使用合成模式: (1)表示对象的部分和整体层次结构 (2)忽略枝对象和叶对象的不同,用户同意使用组合结构的所有对象。
结构图如下: 合成模式的实现根据公共接口Component的区别分为两种形式,分别称为安全模式和透明模式。
透明模式: 公共接口Component中里面声明所有的用来管理子类对象的方法 ,包括Add()、Remove(),以及GetChild()方法。这样所有的叶对象和枝对象都有相同的接口,那么客户端可以将叶对象和枝对象同等对待而不用区分。 示例代码如下:
using System; using System.Collections.Generic; using System.Text; namespace Composite ... { /**/ /// <summary> /// 透明模式 /// 一个Leaf类和一个Composite共享一个在抽象类中定义的公共接口 /// </summary> public interface ITransparence ... { /**/ /// <summary> /// 获得学生的总成绩 /// </summary> /// <returns> 返回学生的总成绩 </returns> int GetStudentScores(); /**/ /// <summary> /// 获得学生的所有科目名称 /// </summary> /// <returns> 返回学生的所有科目名称 </returns> string GetStudentSubjects(); /**/ /// <summary> /// 增加叶节点 /// </summary> /// <param name="transparence"> 接口类型对象 </param> /// <returns> 操作是否成功 </returns> bool Add(ITransparence transparence); /**/ /// <summary> /// 移除叶节点 /// </summary> /// <param name="transparence"> 接口类型对象 </param> /// <returns> 操作是否成功 </returns> bool Remove(ITransparence transparence); /**/ /// <summary> /// 获得所有的子节点 /// </summary> /// <returns></returns> List < ITransparence > GetChild(); } /**/ /// <summary> /// 学生叶子节点 /// </summary> public class StudentLeaf : ITransparence ... { /**/ /// <summary> /// 语文 /// </summary> private string _chinese; /**/ /// <summary> /// 数学 /// </summary> private string _math; /**/ /// <summary> /// 英语 /// </summary> private string _english; /**/ /// <summary> /// 语文成绩 /// </summary> private int _chineseScore; /**/ /// <summary> /// 数学成绩 /// </summary> private int _mathScore; /**/ /// <summary> /// 英语成绩 /// </summary> private int _englishScore; /**/ /// <summary> /// 构造函数:语文,数学,英语的中文名称和它的分数 /// </summary> /// <param name="chinese"></param> /// <param name="math"></param> /// <param name="english"></param> public StudentLeaf( string chinese, string math, string english, int chineseScore, int mathScore, int englishScore) ... { _chinese = chinese; _math = math; _english = english; _chineseScore = chineseScore; _mathScore = mathScore; _englishScore = englishScore; } /**/ /// <summary> /// 获得某个学生总成绩 /// </summary> /// <returns></returns> public int GetStudentScores() ... { int scores = _chineseScore + _mathScore + _englishScore; return scores; } /**/ /// <summary> /// 获得某个学生的所有科目名称 /// </summary> /// <returns></returns> public string GetStudentSubjects() ... { StringBuilder sb = new StringBuilder(); sb.Append( " 语文: " ); sb.Append(_chinese); sb.Append( " " ); sb.Append( " 数学: " ); sb.Append(_math); sb.Append( " " ); sb.Append( " 英语: " ); sb.Append(_english); sb.Append( " " ); return sb.ToString(); } /**/ /// <summary> /// 增加叶节点 /// </summary> /// <param name="transparence"> 接口类型对象 </param> /// <returns> 操作是否成功 </returns> public bool Add(ITransparence transparence) ... { // 叶子节点不能使用该方法 return false ; } /**/ /// <summary> /// 移除叶节点 /// </summary> /// <param name="transparence"> 接口类型对象 </param> /// <returns> 操作是否成功 </returns> public bool Remove(ITransparence transparence) ... { // 叶子节点不能使用该方法 return false ; } /**/ /// <summary> /// 获得所有的子节点 /// </summary> /// <returns></returns> public List < ITransparence > GetChild() ... { return null ; } } /**/ /// <summary> /// 学生合成类 /// </summary> public class StudentComposite : ITransparence ... { private List < ITransparence > studentComposite = new List < ITransparence > (); /**/ /// <summary> /// 获得某个学生总成绩 /// </summary> /// <returns></returns> public int GetStudentScores() ... { int scores = 0 ; foreach (ITransparence transparence in studentComposite) ... { scores += transparence.GetStudentScores(); } return scores; } /**/ /// <summary> /// 获得某个学生的所有科目名称 /// </summary> /// <returns></returns> public string GetStudentSubjects() ... { StringBuilder sb = new StringBuilder(); foreach (ITransparence transparence in studentComposite) ... { sb.Append(transparence.GetStudentSubjects()); sb.Append( " " ); } return sb.ToString(); } /**/ /// <summary> /// 增加叶节点 /// </summary> /// <param name="transparence"> 接口类型对象 </param> /// <returns> 操作是否成功 </returns> public bool Add(ITransparence transparence) ... { studentComposite.Add(transparence); return true ; } /**/ /// <summary> /// 移除叶节点 /// </summary> /// <param name="transparence"> 接口类型对象 </param> /// <returns> 操作是否成功 </returns> public bool Remove(ITransparence transparence) ... { studentComposite.Remove(transparence); return true ; } /**/ /// <summary> /// 获得所有的子节点 /// </summary> /// <returns></returns> public List < ITransparence > GetChild() ... { return studentComposite; } } /**/ /// <summary> /// 客户类 /// </summary> public class Client ... { static void Main( string [] args) ... { int scores = 0 ; string studentSubjectsName = string .Empty; StudentComposite sudentCompositeRoot = new StudentComposite(); StudentLeaf studentLeafRootOne = new StudentLeaf( " 11 " , " 12 " , " 13 " , 70 , 71 , 72 ); StudentLeaf studentLeafRootTwo = new StudentLeaf( " 21 " , " 22 " , " 23 " , 80 , 81 , 82 ); StudentLeaf studentLeafRootThree = new StudentLeaf( " 31 " , " 32 " , " 33 " , 90 , 91 , 92 ); sudentCompositeRoot.Add(studentLeafRootOne); sudentCompositeRoot.Add(studentLeafRootTwo); sudentCompositeRoot.Add(studentLeafRootThree); StudentComposite sudentCompositeChild = new StudentComposite(); StudentLeaf studentLeafChildOne = new StudentLeaf( " 41 " , " 42 " , " 43 " , 60 , 61 , 62 ); StudentLeaf studentLeafChildTwo = new StudentLeaf( " 51 " , " 52 " , " 53 " , 50 , 51 , 52 ); sudentCompositeChild.Add(studentLeafChildOne); sudentCompositeChild.Add(studentLeafChildTwo); sudentCompositeRoot.Add(sudentCompositeChild); scores = sudentCompositeRoot.GetStudentScores(); Console.WriteLine( " 总成绩:{0} " , scores); studentSubjectsName = sudentCompositeRoot.GetStudentSubjects(); Console.WriteLine( " 所有科目名称:{0} " , studentSubjectsName); StudentLeaf studentLeaf = new StudentLeaf( " 61 " , " 62 " , " 63 " , 40 , 41 , 42 ); scores = studentLeaf.GetStudentScores(); Console.WriteLine( " 总成绩:{0} " , scores); studentSubjectsName = studentLeaf.GetStudentSubjects(); Console.WriteLine( " 所有科目名称:{0} " , studentSubjectsName); } } }
安全模式:
公共接口Component中里面不声明所有的用来管理子类对象的方法 ,而将这些方法(Add()、Remove(),以及GetChild()方法)在Composite类中实现。因为叶对象不包含任何对象,所以叶对象不需要操作叶子对象的方法,故是安全的。在这种情况下,在客户端使用管理子类对象的方法,自然就出错了。 示例代码如下:
using System; using System.Collections.Generic; using System.Text; namespace Composite ... { /**/ /// <summary> /// 安全模式 /// 一个Leaf类和一个Composite共享一个在抽象类中定义的公共接口 /// </summary> public interface ITransparence ... { /**/ /// <summary> /// 获得学生的总成绩 /// </summary> /// <returns> 返回学生的总成绩 </returns> int GetStudentScores(); /**/ /// <summary> /// 获得学生的所有科目名称 /// </summary> /// <returns> 返回学生的所有科目名称 </returns> string GetStudentSubjects(); } /**/ /// <summary> /// 学生叶子节点 /// </summary> public class StudentLeaf : ITransparence ... { /**/ /// <summary> /// 语文 /// </summary> private string _chinese; /**/ /// <summary> /// 数学 /// </summary> private string _math; /**/ /// <summary> /// 英语 /// </summary> private string _english; /**/ /// <summary> /// 语文成绩 /// </summary> private int _chineseScore; /**/ /// <summary> /// 数学成绩 /// </summary> private int _mathScore; /**/ /// <summary> /// 英语成绩 /// </summary> private int _englishScore; /**/ /// <summary> /// 构造函数:语文,数学,英语的中文名称和它的分数 /// </summary> /// <param name="chinese"></param> /// <param name="math"></param> /// <param name="english"></param> public StudentLeaf( string chinese, string math, string english, int chineseScore, int mathScore, int englishScore) ... { _chinese = chinese; _math = math; _english = english; _chineseScore = chineseScore; _mathScore = mathScore; _englishScore = englishScore; } /**/ /// <summary> /// 获得某个学生总成绩 /// </summary> /// <returns></returns> public int GetStudentScores() ... { int scores = _chineseScore + _mathScore + _englishScore; return scores; } /**/ /// <summary> /// 获得某个学生的所有科目名称 /// </summary> /// <returns></returns> public string GetStudentSubjects() ... { StringBuilder sb = new StringBuilder(); sb.Append( " 语文: " ); sb.Append(_chinese); sb.Append( " " ); sb.Append( " 数学: " ); sb.Append(_math); sb.Append( " " ); sb.Append( " 英语: " ); sb.Append(_english); sb.Append( " " ); return sb.ToString(); } } /**/ /// <summary> /// 学生合成类 /// </summary> public class StudentComposite : ITransparence ... { private List < ITransparence > studentComposite = new List < ITransparence > (); /**/ /// <summary> /// 获得某个学生总成绩 /// </summary> /// <returns></returns> public int GetStudentScores() ... { int scores = 0 ; foreach (ITransparence transparence in studentComposite) ... { scores += transparence.GetStudentScores(); } return scores; } /**/ /// <summary> /// 获得某个学生的所有科目名称 /// </summary> /// <returns></returns> public string GetStudentSubjects() ... { StringBuilder sb = new StringBuilder(); foreach (ITransparence transparence in studentComposite) ... { sb.Append(transparence.GetStudentSubjects()); sb.Append( " " ); } return sb.ToString(); } /**/ /// <summary> /// 增加叶节点 /// </summary> /// <param name="transparence"> 接口类型对象 </param> /// <returns> 操作是否成功 </returns> public bool Add(ITransparence transparence) ... { studentComposite.Add(transparence); return true ; } /**/ /// <summary> /// 移除叶节点 /// </summary> /// <param name="transparence"> 接口类型对象 </param> /// <returns> 操作是否成功 </returns> public bool Remove(ITransparence transparence) ... { studentComposite.Remove(transparence); return true ; } /**/ /// <summary> /// 获得所有的子节点 /// </summary> /// <returns></returns> public List < ITransparence > GetChild() ... { return studentComposite; } } /**/ /// <summary> /// 客户类 /// </summary> public class Client ... { static void Main( string [] args) ... { int scores = 0 ; string studentSubjectsName = string .Empty; StudentComposite sudentCompositeRoot = new StudentComposite(); StudentLeaf studentLeafRootOne = new StudentLeaf( " 11 " , " 12 " , " 13 " , 70 , 71 , 72 ); StudentLeaf studentLeafRootTwo = new StudentLeaf( " 21 " , " 22 " , " 23 " , 80 , 81 , 82 ); StudentLeaf studentLeafRootThree = new StudentLeaf( " 31 " , " 32 " , " 33 " , 90 , 91 , 92 ); sudentCompositeRoot.Add(studentLeafRootOne); sudentCompositeRoot.Add(studentLeafRootTwo); sudentCompositeRoot.Add(studentLeafRootThree); StudentComposite sudentCompositeChild = new StudentComposite(); StudentLeaf studentLeafChildOne = new StudentLeaf( " 41 " , " 42 " , " 43 " , 60 , 61 , 62 ); StudentLeaf studentLeafChildTwo = new StudentLeaf( " 51 " , " 52 " , " 53 " , 50 , 51 , 52 ); sudentCompositeChild.Add(studentLeafChildOne); sudentCompositeChild.Add(studentLeafChildTwo); sudentCompositeRoot.Add(sudentCompositeChild); scores = sudentCompositeRoot.GetStudentScores(); Console.WriteLine( " 总成绩:{0} " , scores); studentSubjectsName = sudentCompositeRoot.GetStudentSubjects(); Console.WriteLine( " 所有科目名称:{0} " , studentSubjectsName); StudentLeaf studentLeaf = new StudentLeaf( " 61 " , " 62 " , " 63 " , 40 , 41 , 42 ); scores = studentLeaf.GetStudentScores(); Console.WriteLine( " 总成绩:{0} " , scores); studentSubjectsName = studentLeaf.GetStudentSubjects(); Console.WriteLine( " 所有科目名称:{0} " , studentSubjectsName); } } }
参考资料: (1)《C# 设计模式》《 Desing Patterns in C#》(美)Steven John Metsker 著, 颜炯 译。 (2)设计模式 可复用面向对象软件的基础 Design Patterns Elements of Reusable Object-Oriented Software 》(美)Erich Gamma Richard Helm Ralph Johnson John Vlissides 著,李英军 等译。