您的位置:首页 > 其它

温故知新(5)——组合模式

2012-09-07 16:54 375 查看

概述

组合模式大概是每个人都用过的一种模式,或有心,或无意。因为如果要把一个一个的节点组合成“树”,组合模式的写法应该是比较自然的一种表达。但是个体与整体的访问一致性,可能需要特别注意一下。先看看GOF给出的模式意图。

将对象组合成树形结构以表示“部分-整体”的层次结构,使用户对单个对象和组合对象的使用具有一致性。

也是就是说:

1、组合模式用来构建“部分-整体”的层次结构,也就是树;

2、客户端对象使用对象个体和对象的组合体时,方法相同。

组合模式的实现过程中有一些值得考虑的地方,下节将详述。

结构

下面是组合模式的类图:





整理一下模式参与者(为了表述清楚,将使用树形结构的术语):

1、树节点的抽象,叶节点和非叶节点都要实现这个接口——IComposite;

2、叶节点,组合中最基础的单位——Leaf;

3、分支节点,可以包含其他分支节点或叶节点——Composite;

4、客户端代码——Client。

上图的IComposite的接口中,除定义了Operation这样一个公共的操作以外,还定义了Add、Remove的子对象的管理方法,这样当Leaf类实现这个接口时就必须实现这些对它来说没有意义的方法(因为叶子不包含子对象)。这虽然有些违反“类只能定义对它子类有意义的操作”这条设计原则,但是换来的是Client对Leaf和Composite使用的一致性。我们可以将上面的做法称为接口最大化,相反也可以采用接口最小化的做法,即IComposite只包含Operation方法,而将子对象的管理方法放入Composite中实现,这样Leaf将变得清晰,但是Client在使用Leaf和Composite就要区别对待。由于GOF的意图中强调了这种使用的一致性,因此本文将采用接口最大化的方法。但在实际使用中应该具体分析,在两种方法中做出取舍。

示例

金融研究机构经常针对当前的经济形势撰写研究报告,为了更好的管理这些报告,需要对这些报告进行分类。分类不仅限于一级,即分类下可以包含子分类,子分类下可以再包含子分类,由此构成了一个分类树,研究报告包含在叶子节点的分类中。需求要求选择任意一个分类节点(跟、分支、叶子),列出这个分类下所有的报告。下面的实现在把叶子节点称为分类Category,分支节点成为分类组。

1、定义分类的接口ICategory。

usingSystem;


usingSystem.Collections.Generic;




namespaceDesignPatterns.Composite


{


///<summary>


///报告分类接口


///</summary>


publicinterfaceICategory


{


//分类名称


stringName{get;set;}




//获取分类下的报告


List<Report>GetReports();




//添加子分类


voidAddSubCategory(ICategorycategory);


//移除子分类


voidRemoveSubCategory(ICategorycategory);


//获取子分类


ICategoryGetSubCategory(intindex);


//子分类个数


intCount{get;}


}


}




.codearea{color:black;background-color:white;line-height:18px;border:1pxsolid#4f81bd;margin:0;width:auto!important;width:100%;overflow:auto;text-align:left;font-size:12px;font-family:"CourierNew","Consolas","Fixedsys","BitStreamVeraSansMono",courier,monospace,serif}
.codeareapre{color:black;line-height:18px;padding:00012px!important;margin:0em;background-color:#fff!important}
.linewrappre{white-space:pre-wrap;white-space:-moz-pre-wrap;white-space:-pre-wrap;white-space:-o-pre-wrap;word-wrap:break-word;word-break:normal}
.codeareapre.alt{background-color:#f7f7ff!important}
.codearea.lnum{color:#4f81bd;line-height:18px}

2、实现分类Category。可以看到Category空实现了子分类的管理方法。

usingSystem;


usingSystem.Collections.Generic;




namespaceDesignPatterns.Composite


{


///<summary>


///具体分类


///</summary>


publicclassCategory:ICategory


{


publicCategory(stringname)


{


this.Name=name;


}




publicstringName{get;set;}




publicList<Report>GetReports()


{


returnnewList<Report>(){newReport(),newReport(),newReport()};


}




publicvoidAddSubCategory(ICategorycategory)


{


thrownewNotSupportedException("具体分类不支持此操作。");


}




publicvoidRemoveSubCategory(ICategorycategory)


{


thrownewNotSupportedException("具体分类不支持此操作。");


}




publicICategoryGetSubCategory(intindex)


{


thrownewNotSupportedException("具体分类不支持此操作。");


}




publicintCount


{


get{thrownewNotSupportedException("具体分类不支持此操作。");}


}


}


}




.codearea{color:black;background-color:white;line-height:18px;border:1pxsolid#4f81bd;margin:0;width:auto!important;width:100%;overflow:auto;text-align:left;font-size:12px;font-family:"CourierNew","Consolas","Fixedsys","BitStreamVeraSansMono",courier,monospace,serif}
.codeareapre{color:black;line-height:18px;padding:00012px!important;margin:0em;background-color:#fff!important}
.linewrappre{white-space:pre-wrap;white-space:-moz-pre-wrap;white-space:-pre-wrap;white-space:-o-pre-wrap;word-wrap:break-word;word-break:normal}
.codeareapre.alt{background-color:#f7f7ff!important}
.codearea.lnum{color:#4f81bd;line-height:18px}

3、实现分类组CategoryGroup。

usingSystem;


usingSystem.Collections.Generic;




namespaceDesignPatterns.Composite


{


///<summary>


///分类组


///</summary>


publicclassCategoryGroup:ICategory


{


publicCategoryGroup(stringname)


{


this.Name=name;


this.subCategories=newList<ICategory>();


}




publicstringName{get;set;}




publicList<Report>GetReports()


{


List<Report>reports=newList<Report>();


foreach(varcinthis.subCategories)


{


reports.AddRange(c.GetReports());


}


returnreports;


}




privateList<ICategory>subCategories;




publicvoidAddSubCategory(ICategorycategory)


{


this.subCategories.Add(category);


}




publicvoidRemoveSubCategory(ICategorycategory)


{


this.subCategories.Remove(category);


}




publicICategoryGetSubCategory(intindex)


{


returnthis.subCategories[index];


}




publicintCount


{


get


{


returnthis.subCategories.Count;


}


}


}


}




.codearea{color:black;background-color:white;line-height:18px;border:1pxsolid#4f81bd;margin:0;width:auto!important;width:100%;overflow:auto;text-align:left;font-size:12px;font-family:"CourierNew","Consolas","Fixedsys","BitStreamVeraSansMono",courier,monospace,serif}
.codeareapre{color:black;line-height:18px;padding:00012px!important;margin:0em;background-color:#fff!important}
.linewrappre{white-space:pre-wrap;white-space:-moz-pre-wrap;white-space:-pre-wrap;white-space:-o-pre-wrap;word-wrap:break-word;word-break:normal}
.codeareapre.alt{background-color:#f7f7ff!important}
.codearea.lnum{color:#4f81bd;line-height:18px}

4、添加一个表示报告的实体类Report。

usingSystem;




namespaceDesignPatterns.Composite


{


///<summary>


///报告实体类


///</summary>


publicclassReport


{


publicReport()


{


//每次生成GUID对象的hash码做种子,生成随机数


Randomr=newRandom(Guid.NewGuid().GetHashCode());


this.Id=r.Next(1,999999);


}




//报告ID


publicintId{get;set;}


}


}




.codearea{color:black;background-color:white;line-height:18px;border:1pxsolid#4f81bd;margin:0;width:auto!important;width:100%;overflow:auto;text-align:left;font-size:12px;font-family:"CourierNew","Consolas","Fixedsys","BitStreamVeraSansMono",courier,monospace,serif}
.codeareapre{color:black;line-height:18px;padding:00012px!important;margin:0em;background-color:#fff!important}
.linewrappre{white-space:pre-wrap;white-space:-moz-pre-wrap;white-space:-pre-wrap;white-space:-o-pre-wrap;word-wrap:break-word;word-break:normal}
.codeareapre.alt{background-color:#f7f7ff!important}
.codearea.lnum{color:#4f81bd;line-height:18px}

5、客户端代码。

usingSystem;




namespaceDesignPatterns.Composite


{


classProgram


{


staticvoidMain(string[]args)


{


//组合过程


ICategoryroot=newCategoryGroup("全部报告");


ICategorymacro=newCategory("宏观研究");


ICategoryindustry=newCategoryGroup("行业研究");


ICategoryagriculture=newCategory("农业");


ICategoryfinance=newCategory("金融业");


industry.AddSubCategory(agriculture);


industry.AddSubCategory(finance);


root.AddSubCategory(macro);


root.AddSubCategory(industry);




//根节点、分支节点、叶节点的使用完成一致。


DisplayReports(root);


DisplayReports(industry);


DisplayReports(agriculture);




Console.WriteLine("按任意键结束...");


Console.ReadKey();


}




//输出


privatestaticvoidDisplayReports(ICategorycategory)


{


Console.WriteLine(category.Name+":");


foreach(varrincategory.GetReports())


{


Console.WriteLine(r.Id);


}


Console.WriteLine("===============================");


}


}


}




.codearea{color:black;background-color:white;line-height:18px;border:1pxsolid#4f81bd;margin:0;width:auto!important;width:100%;overflow:auto;text-align:left;font-size:12px;font-family:"CourierNew","Consolas","Fixedsys","BitStreamVeraSansMono",courier,monospace,serif}
.codeareapre{color:black;line-height:18px;padding:00012px!important;margin:0em;background-color:#fff!important}
.linewrappre{white-space:pre-wrap;white-space:-moz-pre-wrap;white-space:-pre-wrap;white-space:-o-pre-wrap;word-wrap:break-word;word-break:normal}
.codeareapre.alt{background-color:#f7f7ff!important}
.codearea.lnum{color:#4f81bd;line-height:18px}

6、运行,查询结果。





博文(/article/2813759.html),中有一个取自开源测试框架JUnit中的例子,可以参考。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
章节导航