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

好吧,你也知道你是逃不过此劫了,没过多久你的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. popen:让进程看似文件

    popen打开一个指向进程的带缓冲的连接: FILE *fp; fp = popen("ls", "r"); fgets(buf, len, fp); pclo ...

  2. Aamazon Web Service EC2 Ubuntu 新建用户而且用ssh连接host

    本文參照 http://docs.aws.amazon.com/zh_cn/AWSEC2/latest/UserGuide/managing-users.html http://docs.aws.am ...

  3. linux下U盘状态检测

    Linux的文件系统是异步的,也就是说写一个文件不是立刻保存到介质(硬盘,U盘等)中,而是存到缓冲区内,等积累到一定程度再一起保存到介质中.如果没有umount就非法拔出U盘,程序是不知道的,fope ...

  4. mysql 存相同内容:utb8mb4 会比 utf8 占用更多的内存吗,utf8mb4 浪费内存吗?utf8 utf8mb4 区别

    原文:mysql 存相同内容:utb8mb4 会比 utf8 占用更多的内存吗,utf8mb4 浪费内存吗?utf8 utf8mb4 区别 参考:http://www.fengyunxiao.cn u ...

  5. Linux 下查看线程信息

    1. 使用 pstree -p PID ps aux | grep firefox | grep -v grepcharles  26058  0.0  0.0   4908  1152 ?      ...

  6. error: { "$err" : "not master and slaveOk=false", "code" : 13435 }

    rsguo:SECONDARY> db.users.find();error: { "$err" : "not master and slaveOk=false&q ...

  7. [Angular2] Map keyboards events to Function

    The idea is when we tape the arrow keys on the keyboard, we want the ball move accodingly. const lef ...

  8. Android JAVA中的时间大小比较

    import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; imp ...

  9. 忙里偷闲( ˇˍˇ )闲里偷学【C语言篇】——(8)枚举、补码

    一.枚举 # include <stdio.h> enum WeekDay //定义了一个数据类型(值只能写以下值) { MonDay, TuesDay, WednesDay, Thurs ...

  10. jquery 点击其他地方

    <script type="text/javascript"> function stopPropagation(e) { if (e.stopPropagation) ...