转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/9153753

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

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

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

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

  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. }

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

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

  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接口,里面有两个待实现方法:

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

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

  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类用于统计计算机类书籍的浏览量和销售量:

  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接口,用于统计医学类书籍的浏览量和销售量,代码如下如示:

  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类用于对科技这一组合类书籍进行统计:

  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接口,具体代码如下所示:

  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方法中统计出所有书籍的浏览量和销售量。

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

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

  1. new TechnicalStatistics().getBrowseCount();

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

  1. new AllStatistics().getSalesCount();

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

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

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

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

  1. JAVA设计模式之组合模式(composite)

    组合模式:树状结构专用模式 代码如下: package com.srr.dp.composite; import java.util.ArrayList; import java.util.List; ...

  2. java设计模式之组合模式

    组合模式 组合模式,将对象组合成树形结构以表示“部分-整体”的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性.掌握组合模式的重点是要理解清楚 “部分/整体” 还有 ”单个对象“ 与 & ...

  3. [译]Java 设计模式之组合

    (文章翻译自Java Design Pattern: Composite) 组合模式相对来说是比较简单的,但是它在很多设计中被用到,比如SWT.Eclipse工作空间等等.它创建了一个可以通过唯一的方 ...

  4. C++设计模式实现--组合(Composite)模式

    一. 举例 这个样例是书上的,如果有一个公司的组结结构例如以下: 它的结构非常像一棵树,当中人力资源部和財务部是没有子结点的,详细公司才有子结点. 并且最关健的是,它的每一层结构非常相似. 代码实现例 ...

  5. Java设计模式应用——组合模式

    组合模式实际上是一种树形数据结构.以windows目录系统举例,怎么样用java语言描述一个文件夹? 定义一个文件夹类,文件夹类中包含若干个子文件类和若干个文件类. 进一步抽象,把文件夹和文件都看做节 ...

  6. java设计模式5.组合模式、门面模式、享元模式、桥接模式

    组合模式 在面向对象的语言中,树结构有着巨大的威力,一个基于继承的类型的等级结构便是一个数结构,一个基于合成的对象结构也是一个数结构.组合模式将部分与整体的关系用树结构表示出来,使得客户端把一个个单独 ...

  7. 老和尚给小和尚讲故事引发了Java设计模式:组合模式

    目录 示例 组合模式 定义 意图 主要解决问题 优缺点 安全式和透明式的组合模式 安全式的合成模式的结构 透明式的合成模式的结构 老和尚和小和尚的故事 示例 有一个绘图系统,可以描绘各种图形,假设现在 ...

  8. java设计模式之组合

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

  9. Head First 设计模式 —— 11. 组合 (Composite) 模式

    思考题 我们不仅仅要支持多个菜单,升值还要支持菜单中的菜单.你如何处理这个新的设计需求? P355 [提示]在我们的新设计中,真正需要以下三点: P354 我们需要某种属性结构,可以容纳菜单.子菜单和 ...

随机推荐

  1. word2vec和word embedding有什么区别?

    word2vec和word embedding有什么区别? 我知道这两个都能将词向量化,但有什么区别?这两个术语的中文是什么? from: https://www.zhihu.com/question ...

  2. [BZOJ 1741] Asteroids

    [题目链接] https://www.lydsy.com/JudgeOnline/problem.php?id=1741 [算法] 将每颗小行星的行,列相连,问题就转化为了求这张图的最小覆盖 由kon ...

  3. 5-8 第五天 微信 JS-SDK

    SDK的难点不多,但是容易出错的地方特别多.几乎任何一个环节都会犯错误. 难道没有域名就不让你测了吗?别担心,有公众测试号, 域名这个代理呢是通过QQ浏览器的服务器调试,来绑定这个端口的,把这个服务开 ...

  4. [Apple开发者帐户帮助]四、管理密钥(2)获取密钥标识符

    创建JSON Web令牌(JWT)以与启用的服务进行通信时,需要密钥标识符. 获取密钥标识符 在“ 证书,标识符和配置文件”中,选择侧栏中“键”下的“全部”. 在右侧,选择私钥. 密钥标识符显示在密钥 ...

  5. Rancher 2:添加 NFS client provisioner 动态提供 Kubernetes 后端存储卷

    一.前提说明 1.说明: NFS client provisioner 利用 NFS Server 给 Kubernetes 作为持久存储的后端,并且动态提供PV. 默认 rancher 2 的存储类 ...

  6. go之数据类型转换和类型断言

    一.类型转换 1.1 简单类型转换 格式 valueOfTypeB = typeB(valueOfTypeA) int 转 float64 package main import "fmt& ...

  7. 实体 和 XML格式的转换

    许多接口传输需要XML格式转换,那么如何实现?看下面帮助类→_→ /// <summary> /// XML处理帮助类,编码格式GBK!! /// </summary> pub ...

  8. 复习HTML+CSS(9)

    n  CSS简介 CSS:Cascading Style Sheets 层叠样式表 CSS的主要目的:是给HTML标记添加各种各样的表现(格式.样式).比如:文字样式.背景.文本样式.链接样式. 提示 ...

  9. VMWare linux安装mysql 5.7.13

    1.基础环境说明 虚拟机:VMWare 操作系统:linux 数据库版本:mysql 5.7.13 社区版(别问为什么不装企业版,因为企业版要钱) 背景:虚拟机可以连上外网 下载目录: /tools/ ...

  10. Oracle SQL 性能优化技巧

    Select语句完整的执行顺序: SQL Select语句完整的执行顺序: 1. from子句组装来自不同数据源的数据: 2.where子句基于指定的条件对记录行进行筛选: 3.group by子句将 ...