听说你们公司最近新推出了一款电子书阅读应用,市场反应很不错,应用里还有图书商城,用户可以在其中随意选购自己喜欢的书籍。你们公司也是对此项目高度重视,加大了投入力度,决定给此应用再增加点功能。

好吧,你也知道你是逃不过此劫了,没过多久你的leader就找到了你。他告诉你目前的应用对每本书的浏览量和销售量做了统计,但现在想增加对每个书籍分类的浏览量和销售量以及所有书籍总的浏览量和销售量做统计的功能,希望你可以来完成这项功能。

领导安排的工作当然是推脱不掉的,你只能硬着头皮上了,不过好在这个功能看起来也不怎么复杂。

你比较喜欢看小说,那么就从小说类的统计功能开始做起吧。首先通过getAllNovels方法可以获取到所有的小说名,然后将小说名传入getBrowseCount方法可以得到该书的浏览量,将小说名传入getSaleCount方法可以得到该书的销售量。你目前只有这几个已知的API可以使用,那么开始动手吧!

[java] view
plain
copy

  1. public int getNovelsBrowseCount() {
  2. int browseCount = 0;
  3. List<String> allNovels = getAllNovels();
  4. for (String novel : allNovels) {
  5. browseCount += getBrowseCount(novel);
  6. }
  7. return browseCount;
  8. }
  9. public int getNovelsSaleCount() {
  10. int saleCount = 0;
  11. List<String> allNovels = getAllNovels();
  12. for (String novel : allNovels) {
  13. saleCount += getSaleCount(novel);
  14. }
  15. return saleCount;
  16. }

很快你就写下了以上两个方法,这两个方法都是通过获取到所有的小说名,然后一一计算每本小说的浏览量和销售量,最后将结果相加得到总量。

小说类的统计就完成了,然后你开始做计算机类书籍的统计功能,代码如下所示:

[java] view
plain
copy

  1. public int getComputerBooksBrowseCount() {
  2. int browseCount = 0;
  3. List<String> allComputerBooks = getAllComputerBooks();
  4. for (String computerBook : allComputerBooks) {
  5. browseCount += getBrowseCount(computerBook);
  6. }
  7. return browseCount;
  8. }
  9. public int getComputerBooksSaleCount() {
  10. int saleCount = 0;
  11. List<String> allComputerBooks = getAllComputerBooks();
  12. for (String computerBook : allComputerBooks) {
  13. saleCount += getSaleCount(computerBook);
  14. }
  15. return saleCount;
  16. }

除了使用了getAllComputerBooks方法获取到所有的计算机类书名,其它的代码基本和小说统计中的是一样的。

现在你才完成了两类书籍的统计功能,后面还有医学类、自然类、历史类、法律类、政治类、哲学类、旅游类、美食类等等等等书籍。你突然意识到了一些问题的严重性,工作量大倒还不算什么,但再这么写下去,你的方法就要爆炸了,这么多的方法让人看都看不过来,别提怎么使用了。

这个时候你只好向你的leader求助了,跟他说明了你的困惑。只见你的leader思考了片刻,然后自信地告诉你,使用组合模式不仅可以轻松消除你的困惑,还能出色地完成功能。

他立刻向你秀起了编码操作,首先定义一个Statistics接口,里面有两个待实现方法:

[java] view
plain
copy

  1. public interface Statistics {
  2. int getBrowseCount();
  3. int getSalesCount();
  4. }

然后定义一个用于统计小说类书籍的NovelStatistics类,实现接口中定义的两个方法:

[java] view
plain
copy

  1. public class NovelStatistics implements Statistics {
  2. @Override
  3. public int getBrowseCount() {
  4. int browseCount = 0;
  5. List<String> allNovels = getAllNovels();
  6. for (String novel : allNovels) {
  7. browseCount += getBrowseCount(novel);
  8. }
  9. return browseCount;
  10. }
  11. @Override
  12. public int getSalesCount() {
  13. int saleCount = 0;
  14. List<String> allNovels = getAllNovels();
  15. for (String novel : allNovels) {
  16. saleCount += getSaleCount(novel);
  17. }
  18. return saleCount;
  19. }
  20. }

在这两个方法中分别统计了小说类书籍的浏览量和销售量。那么同样的方法,你的leader又定义了一个ComputerBookStatistics类用于统计计算机类书籍的浏览量和销售量:

[java] view
plain
copy

  1. public class ComputerBookStatistics implements Statistics {
  2. @Override
  3. public int getBrowseCount() {
  4. int browseCount = 0;
  5. List<String> allComputerBooks = getAllComputerBooks();
  6. for (String computerBook : allComputerBooks) {
  7. browseCount += getBrowseCount(computerBook);
  8. }
  9. return browseCount;
  10. }
  11. @Override
  12. public int getSalesCount() {
  13. int saleCount = 0;
  14. List<String> allComputerBooks = getAllComputerBooks();
  15. for (String computerBook : allComputerBooks) {
  16. saleCount += getSaleCount(computerBook);
  17. }
  18. return saleCount;
  19. }
  20. }

这样将具体的统计实现分散在各个类中,就不会再出现你刚刚那种方法爆炸的情况了。不过这还没开始真正使用组合模式呢,好戏还在后头,你的leader吹嘘道。



再定义一个MedicalBookStatistics类实现了Statistics接口,用于统计医学类书籍的浏览量和销售量,代码如下如示:

[java] view
plain
copy

  1. public class MedicalBookStatistics implements Statistics {
  2. @Override
  3. public int getBrowseCount() {
  4. int browseCount = 0;
  5. List<String> allMedicalBooks = getAllMedicalBooks();
  6. for (String medicalBook : allMedicalBooks) {
  7. browseCount += getBrowseCount(medicalBook);
  8. }
  9. return browseCount;
  10. }
  11. @Override
  12. public int getSalesCount() {
  13. int saleCount = 0;
  14. List<String> allMedicalBooks = getAllMedicalBooks();
  15. for (String medicalBook : allMedicalBooks) {
  16. saleCount += getSaleCount(medicalBook);
  17. }
  18. return saleCount;
  19. }
  20. }

不知道你发现了没有,计算机类书籍和医学类书籍其实都算是科技类书籍,它们是可以组合在一起的。这个时候你的leader定义了一个TechnicalStatistics类用于对科技这一组合类书籍进行统计:

[java] view
plain
copy

  1. public class TechnicalStatistics implements Statistics {
  2. private List<Statistics> statistics = new ArrayList<Statistics>();
  3. public TechnicalStatistics() {
  4. statistics.add(new ComputerBookStatistics());
  5. statistics.add(new MedicalBookStatistics());
  6. }
  7. @Override
  8. public int getBrowseCount() {
  9. int browseCount = 0;
  10. for (Statistics s : statistics) {
  11. browseCount += s.getBrowseCount();
  12. }
  13. return browseCount;
  14. }
  15. @Override
  16. public int getSalesCount() {
  17. int saleCount = 0;
  18. for (Statistics s : statistics) {
  19. saleCount += s.getBrowseCount();
  20. }
  21. return saleCount;
  22. }
  23. }

可以看到,由于这个类是组合类,和前面几个类还是有不少区别的。首先TechnicalStatistics中有一个构造函数,在构造函数中将计算机类书籍和医学类书籍作为子分类添加到statistics列表当中,然后分别在getBrowseCount和getSalesCount方法中遍历所有的子分类,计算出它们各自的浏览量和销售量,然后相加得到总额返回。

组合模式的扩展性非常好,没有各种条条框框,想怎么组合就怎么组合,比如所有书籍就是由各个分类组合而来的,你的leader马上又向你炫耀了统计所有书籍的浏览量和销售量的办法。

定义一个AllStatistics类实现了Statistics接口,具体代码如下所示:

[java] view
plain
copy

  1. public class AllStatistics implements Statistics {
  2. private List<Statistics> statistics = new ArrayList<Statistics>();
  3. public AllStatistics() {
  4. statistics.add(new NovelStatistics());
  5. statistics.add(new TechnicalStatistics());
  6. }
  7. @Override
  8. public int getBrowseCount() {
  9. int browseCount = 0;
  10. for (Statistics s : statistics) {
  11. browseCount += s.getBrowseCount();
  12. }
  13. return browseCount;
  14. }
  15. @Override
  16. public int getSalesCount() {
  17. int saleCount = 0;
  18. for (Statistics s : statistics) {
  19. saleCount += s.getBrowseCount();
  20. }
  21. return saleCount;
  22. }
  23. }

在AllStatistics的构造函数中将小说类书籍和科技类书籍作为子分类添加到了statistics列表当中,目前你也就只写好了这几个分类。然后使用同样的方法在getBrowseCount和getSalesCount方法中统计出所有书籍的浏览量和销售量。

当前组合结构的示意图如下:

现在你就可以非常方便的得到任何分类书籍的浏览量和销售量了,比如说获取科技类书籍的浏览量,你只需要调用:

[java] view
plain
copy

  1. new TechnicalStatistics().getBrowseCount();

而获取所有书籍的总销量,你只需要调用:

[java] view
plain
copy

  1. new AllStatistics().getSalesCount();

当然你后面还可以对这个组合结构随意地改变,添加各种子分类书籍,而且子分类的层次结构可以任意深,正如前面所说,组合模式的扩展性非常好。

你的leader告诉你,目前他写的这份代码重复度比较高,其实还可以好好优化一下的,把冗余代码都去除掉。当然这个任务就交给你来做了,你的leader可是大忙人,早就一溜烟跑开了。

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

Java设计模式透析之 —— 组合(Composite)的更多相关文章

  1. Ruby设计模式透析之 —— 组合(Composite)

    转载请注明出处:http://blog.csdn.net/sinyu890807/article/details/9153761 此为Java设计模式透析的拷贝版,专门为Ruby爱好者提供的,不熟悉R ...

  2. Java设计模式透析之 —— 单例(Singleton)

    写软件的时候经常需要用到打印日志功能,可以帮助你调试和定位问题,项目上线后还可以帮助你分析数据.但是Java原生带有的System.out.println()方法却很少在真正的项目开发中使用,甚至像f ...

  3. Java设计模式透析之 —— 策略(Strategy)

    今天你的leader兴致冲冲地找到你,希望你能够帮他一个小忙.他如今急着要去开会.要帮什么忙呢?你非常好奇. 他对你说.当前你们项目的数据库中有一张用户信息表.里面存放了非常用户的数据.如今须要完毕一 ...

  4. Java设计模式透析之 —— 模板方法(Template Method)

    今天你还是像往常一样来上班,一如既往地開始了你的编程工作. 项目经理告诉你,今天想在server端添加一个新功能.希望写一个方法.能对Book对象进行处理.将Book对象的全部字段以XML格式进行包装 ...

  5. Java设计模式透析之 —— 适配器(Adapter)

    转载请注明出处:http://blog.csdn.net/sinyu890807/article/details/9400141 今天一大早,你的leader就匆匆忙忙跑过来找到你:“快,快,紧急任务 ...

  6. Ruby设计模式透析之 —— 适配器(Adapter)

    转载请注明出处:http://blog.csdn.net/sinyu890807/article/details/9400153 此为Java设计模式透析的拷贝版,专门为Ruby爱好者提供的,不熟悉R ...

  7. Java设计模式(8)组合模式(Composite模式)

    Composite定义:将对象以树形结构组织起来,以达成“部分-整体” 的层次结构,使得客户端对单个对象和组合对象的使用具有一致性. Composite比较容易理解,想到Composite就应该想到树 ...

  8. java设计模式--结构型模式--组合模式

    什么是组合模式,这个有待研究,个人觉得是各类组合而形成的一种结构吧. 组合模式: 组合模式 概述 将对象组合成树形结构以表示"部分-整体"的层次结构."Composite ...

  9. 设计模式C++描述----11.组合(Composite)模式

    一. 举例 这个例子是书上的,假设有一个公司的组结结构如下: 它的结构很像一棵树,其中人力资源部和财务部是没有子结点的,具体公司才有子结点. 而且最关健的是,它的每一层结构很相似. 代码实现如下: / ...

随机推荐

  1. vue项目实现导出数据到excel

    实现导出功能分两种,一种是客户端拿到数据做导出,第二种是服务器端处理好,返回一个数据流实现导出 第一种网上很容易找到,也很好用,本文要说的是第二种. fetchExport({ id: this.so ...

  2. POJ 2983 Is the Information Reliable? 依旧差分约束

    http://poj.org/problem?id=2983 题目大意: 星际大战开始了.你购买了情报,需要判断它的准确性.已知地方的根据地在由南向北排成一条直线.P A B X,表示A在B北面距离X ...

  3. 【Codeforces Round #440 (Div. 2) C】 Maximum splitting

    [链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 肯定用尽量多的4最好. 然后对4取模的结果 为0,1,2,3分类讨论即可 [代码] #include <bits/stdc++ ...

  4. 5DXTPlayer串口调试小结

    小结 过程总是艰难. 首先是没有准备好. 没有安装vs2012,安装的时候,出现各种状况,因为先安装的2013高版本,造成12安装不正确,程序编译出问题.没有办法,只好卸载vs2012,2013及其各 ...

  5. JS冲突解决方法

    Prototype   jquery 冲突解决: 在页面中同时存在jquery 和 prototype ,当用到 $ 的时候,难免产生冲突,所以一定要区分开来: <script type=&qu ...

  6. iOS调试 - 基本技巧

    在程序中,无论是你想弄清楚为什么数组中有3个对象而不是5个,或者为什么一个新的玩家开始之后,游戏在倒退——调试在这些处理过程中是比较重要的一部 分.通过本文的学习,我们将知道在程序中,可以使用的大多数 ...

  7. mysql查询字段所在表

    use information_schema;select * from columns where column_name='字段名' ;

  8. Android的事件分发

    1. Touch事件和绘制事件的异同之处 Touch事件和绘制事件非常相似,都是由ViewRoot派发下来的,可是不同之处在绘制事件是由应用中的某个View发起请求,一层一层上传到ViewRoot.再 ...

  9. 浏览器对象模型(BOM)是什么?(体系结构+知识详解)(图片:结构)

    浏览器对象模型(BOM)是什么?(体系结构+知识详解)(图片:结构) 一.总结 1.BOM操作所有和浏览器相关的东西:网页文档dom,历史记录,浏览器屏幕,浏览器信息,文档的地址url,页面的框架集. ...

  10. oracle 内存结构具体解释

    Oracle 内存结构 与 Oracle 实例关联的基本内存结构包含: 系统全局区 (SGA):由全部server和后台进程共享.SGA 中存储的数据演示样例包含快速缓存的数据块和共享 SQL 区域. ...