Java设计模式透析之 —— 组合(Composite)
听说你们公司最近新推出了一款电子书阅读应用,市场反应很不错,应用里还有图书商城,用户可以在其中随意选购自己喜欢的书籍。你们公司也是对此项目高度重视,加大了投入力度,决定给此应用再增加点功能。
好吧,你也知道你是逃不过此劫了,没过多久你的leader就找到了你。他告诉你目前的应用对每本书的浏览量和销售量做了统计,但现在想增加对每个书籍分类的浏览量和销售量以及所有书籍总的浏览量和销售量做统计的功能,希望你可以来完成这项功能。
领导安排的工作当然是推脱不掉的,你只能硬着头皮上了,不过好在这个功能看起来也不怎么复杂。
你比较喜欢看小说,那么就从小说类的统计功能开始做起吧。首先通过getAllNovels方法可以获取到所有的小说名,然后将小说名传入getBrowseCount方法可以得到该书的浏览量,将小说名传入getSaleCount方法可以得到该书的销售量。你目前只有这几个已知的API可以使用,那么开始动手吧!
- publicintgetNovelsBrowseCount(){
- intbrowseCount=0;
- List<String>allNovels=getAllNovels();
- for(Stringnovel:allNovels){
- browseCount+=getBrowseCount(novel);
- }
- returnbrowseCount;
- }
- publicintgetNovelsSaleCount(){
- intsaleCount=0;
- List<String>allNovels=getAllNovels();
- for(Stringnovel:allNovels){
- saleCount+=getSaleCount(novel);
- }
- returnsaleCount;
- }
小说类的统计就完成了,然后你开始做计算机类书籍的统计功能,代码如下所示:
- publicintgetComputerBooksBrowseCount(){
- intbrowseCount=0;
- List<String>allComputerBooks=getAllComputerBooks();
- for(StringcomputerBook:allComputerBooks){
- browseCount+=getBrowseCount(computerBook);
- }
- returnbrowseCount;
- }
- publicintgetComputerBooksSaleCount(){
- intsaleCount=0;
- List<String>allComputerBooks=getAllComputerBooks();
- for(StringcomputerBook:allComputerBooks){
- saleCount+=getSaleCount(computerBook);
- }
- returnsaleCount;
- }
现在你才完成了两类书籍的统计功能,后面还有医学类、自然类、历史类、法律类、政治类、哲学类、旅游类、美食类等等等等书籍。你突然意识到了一些问题的严重性,工作量大倒还不算什么,但再这么写下去,你的方法就要爆炸了,这么多的方法让人看都看不过来,别提怎么使用了。
这个时候你只好向你的leader求助了,跟他说明了你的困惑。只见你的leader思考了片刻,然后自信地告诉你,使用组合模式不仅可以轻松消除你的困惑,还能出色地完成功能。
他立刻向你秀起了编码操作,首先定义一个Statistics接口,里面有两个待实现方法:
- publicinterfaceStatistics{
- intgetBrowseCount();
- intgetSalesCount();
- }
- publicclassNovelStatisticsimplementsStatistics{
- @Override
- publicintgetBrowseCount(){
- intbrowseCount=0;
- List<String>allNovels=getAllNovels();
- for(Stringnovel:allNovels){
- browseCount+=getBrowseCount(novel);
- }
- returnbrowseCount;
- }
- @Override
- publicintgetSalesCount(){
- intsaleCount=0;
- List<String>allNovels=getAllNovels();
- for(Stringnovel:allNovels){
- saleCount+=getSaleCount(novel);
- }
- returnsaleCount;
- }
- }
- publicclassComputerBookStatisticsimplementsStatistics{
- @Override
- publicintgetBrowseCount(){
- intbrowseCount=0;
- List<String>allComputerBooks=getAllComputerBooks();
- for(StringcomputerBook:allComputerBooks){
- browseCount+=getBrowseCount(computerBook);
- }
- returnbrowseCount;
- }
- @Override
- publicintgetSalesCount(){
- intsaleCount=0;
- List<String>allComputerBooks=getAllComputerBooks();
- for(StringcomputerBook:allComputerBooks){
- saleCount+=getSaleCount(computerBook);
- }
- returnsaleCount;
- }
- }
再定义一个MedicalBookStatistics类实现了Statistics接口,用于统计医学类书籍的浏览量和销售量,代码如下如示:
- publicclassMedicalBookStatisticsimplementsStatistics{
- @Override
- publicintgetBrowseCount(){
- intbrowseCount=0;
- List<String>allMedicalBooks=getAllMedicalBooks();
- for(StringmedicalBook:allMedicalBooks){
- browseCount+=getBrowseCount(medicalBook);
- }
- returnbrowseCount;
- }
- @Override
- publicintgetSalesCount(){
- intsaleCount=0;
- List<String>allMedicalBooks=getAllMedicalBooks();
- for(StringmedicalBook:allMedicalBooks){
- saleCount+=getSaleCount(medicalBook);
- }
- returnsaleCount;
- }
- }
- publicclassTechnicalStatisticsimplementsStatistics{
- privateList<Statistics>statistics=newArrayList<Statistics>();
- publicTechnicalStatistics(){
- statistics.add(newComputerBookStatistics());
- statistics.add(newMedicalBookStatistics());
- }
- @Override
- publicintgetBrowseCount(){
- intbrowseCount=0;
- for(Statisticss:statistics){
- browseCount+=s.getBrowseCount();
- }
- returnbrowseCount;
- }
- @Override
- publicintgetSalesCount(){
- intsaleCount=0;
- for(Statisticss:statistics){
- saleCount+=s.getBrowseCount();
- }
- returnsaleCount;
- }
- }
组合模式的扩展性非常好,没有各种条条框框,想怎么组合就怎么组合,比如所有书籍就是由各个分类组合而来的,你的leader马上又向你炫耀了统计所有书籍的浏览量和销售量的办法。
定义一个AllStatistics类实现了Statistics接口,具体代码如下所示:
- publicclassAllStatisticsimplementsStatistics{
- privateList<Statistics>statistics=newArrayList<Statistics>();
- publicAllStatistics(){
- statistics.add(newNovelStatistics());
- statistics.add(newTechnicalStatistics());
- }
- @Override
- publicintgetBrowseCount(){
- intbrowseCount=0;
- for(Statisticss:statistics){
- browseCount+=s.getBrowseCount();
- }
- returnbrowseCount;
- }
- @Override
- publicintgetSalesCount(){
- intsaleCount=0;
- for(Statisticss:statistics){
- saleCount+=s.getBrowseCount();
- }
- returnsaleCount;
- }
- }
当前组合结构的示意图如下:
现在你就可以非常方便的得到任何分类书籍的浏览量和销售量了,比如说获取科技类书籍的浏览量,你只需要调用:
- newTechnicalStatistics().getBrowseCount();
- newAllStatistics().getSalesCount();
当然你后面还可以对这个组合结构随意地改变,添加各种子分类书籍,而且子分类的层次结构可以任意深,正如前面所说,组合模式的扩展性非常好。
你的leader告诉你,目前他写的这份代码重复度比较高,其实还可以好好优化一下的,把冗余代码都去除掉。当然这个任务就交给你来做了,你的leader可是大忙人,早就一溜烟跑开了。
组合:将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。