详解组合模式的结构及其在Ruby设计模式编程中的运用_ruby专题

定义:也叫合成模式,或者部分-整体模式,主要是用来描述部分与整体的关系,定义,将对象组合成树形结构以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。

类图:

角色说明:

Componnent抽象构件角色:定义参加组合对象的共有方法和属性,可以定义一些默认的行为或属性。
Leaf叶子构件:叶子对象,其下再也没有其他的分支,也就是遍历的最小单位。
Composite树枝构件:树枝对象,它的作用是组合树枝节点和叶子节点形成一个树形结构。

实例:
听说你们公司最近新推出了一款电子书阅读应用,市场反应很不错,应用里还有图书商城,用户可以在其中随意选购自己喜欢的书籍。你们公司也是对此项目高度重视,加大了投入力度,决定给此应用再增加点功能。
好吧,你也知道你是逃不过此劫了,没过多久你的leader就找到了你。他告诉你目前的应用对每本书的浏览量和销售量做了统计,但现在想增加对每个书籍分类的浏览量和销售量以及所有书籍总的浏览量和销售量做统计的功能,希望你可以来完成这项功能。
领导安排的工作当然是推脱不掉的,你只能硬着头皮上了,不过好在这个功能看起来也不怎么复杂。
你比较喜欢看小说,那么就从小说类的统计功能开始做起吧。首先通过get_all_novels方法可以获取到所有的小说名,然后将小说名传入get_browse_count方法可以得到该书的浏览量,将小说名传入get_sale_count方法可以得到该书的销售量。你目前只有这几个已知的API可以使用,那么开始动手吧!

def get_novels_browse_count
  browse_count = 0
  all_novels = get_all_novels()
  all_novels.each do |novel|
    browse_count += get_browse_count(novel)
  end
  browse_count
end 

def get_novels_sale_count
  sale_count = 0
  all_novels = get_all_novels()
  all_novels.each do |novel|
    sale_count += get_browse_count(novel)
  end
  sale_count
end

很快你就写下了以上两个方法,这两个方法都是通过获取到所有的小说名,然后一一计算每本小说的浏览量和销售量,最后将结果相加得到总量。
小说类的统计就完成了,然后你开始做计算机类书籍的统计功能,代码如下所示:

def get_computer_books_browse_count
  browse_count = 0
  all_computer_books = get_all_computer_books()
  all_computer_books.each do |computer_book|
    browse_count += get_browse_count(computer_book)
  end
  browse_count
end 

def get_computer_books_sale_count
  sale_count = 0
  all_computer_books = get_all_computer_books()
  all_computer_books.each do |computer_book|
    sale_count += get_browse_count(computer_book)
  end
  sale_count
end

除了使用了get_all_computer_books方法获取到所有的计算机类书名,其它的代码基本和小说统计中的是一样的。
现在你才完成了两类书籍的统计功能,后面还有医学类、自然类、历史类、法律类、政治类、哲学类、旅游类、美食类等等等等书籍。你突然意识到了一些问题的严重性,工作量大倒还不算什么,但再这么写下去,你的方法就要爆炸了,这么多的方法让人看都看不过来,别提怎么使用了。
这个时候你只好向你的leader求助了,跟他说明了你的困惑。只见你的leader思考了片刻,然后自信地告诉你,使用组合模式不仅可以轻松消除你的困惑,还能出色地完成功能。
他立刻向你秀起了编码操作,首先定义一个Statistics类,里面有两个方法:

class Statistics 

  def get_browse_count
    raise "You should override this method in subclass."
  end 

  def get_sale_count
    raise "You should override this method in subclass."
  end 

end

这两个方法都是简单地抛出一个异常,因为需要在子类中重写这两个方法。
然后定义一个用于统计小说类书籍的NovelStatistics类,继承刚刚定义的Statistics类,并重写Statistics中的两个方法:

class NovelStatistics < Statistics 

  def get_browse_count
    browse_count = 0
    all_novels = get_all_novels()
    all_novels.each do |novel|
      browse_count += get_browse_count(novel)
    end
    browse_count
  end 

  def get_sale_count
    sale_count = 0
    all_novels = get_all_novels()
    all_novels.each do |novel|
      sale_count += get_browse_count(novel)
    end
    sale_count
  end 

end

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

class ComputerBookStatistics < Statistics 

  def get_browse_count
    browse_count = 0
    all_computer_books = get_all_computer_books()
    all_computer_books.each do |computer_book|
      browse_count += get_browse_count(computer_book)
    end
    browse_count
  end 

  def get_sale_count
    sale_count = 0
    all_computer_books = get_all_computer_books()
    all_computer_books.each do |computer_book|
      sale_count += get_browse_count(computer_book)
    end
    sale_count
  end 

end

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

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

class MedicalBookStatistics < Statistics 

  def get_browse_count
    browse_count = 0
    all_medical_books = get_all_medical_books()
    all_medical_books.each do |medical_book|
      browse_count += get_browse_count(medical_book)
    end
    browse_count
  end 

  def get_sale_count
    sale_count = 0
    all_medical_books = get_all_medical_books()
    all_medical_books.each do |medical_book|
      sale_count += get_browse_count(medical_book)
    end
    sale_count
  end 

end

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

class TechnicalStatistics < Statistics 

  def initialize
    @statistics = []
    @statistics << ComputerBookStatistics.new
    @statistics << MedicalBookStatistics.new
  end 

  def get_browse_count
    browse_count = 0
    @statistics.each do |s|
      browse_count += s.get_browse_count
    end
    browse_count
  end 

  def get_sale_count
    sale_count = 0
    @statistics.each do |s|
      sale_count += s.get_sale_count
    end
    sale_count
  end 

end

可以看到,由于这个类是组合类,和前面几个类还是有不少区别的。首先TechnicalStatistics中有一个构造函数,在构造函数中将计算机类书籍和医学类书籍作为子分类添加到statistics数组当中,然后分别在get_browse_count和get_sale_count方法中遍历所有的子分类,计算出它们各自的浏览量和销售量,然后相加得到总额返回。
组合模式的扩展性非常好,没有各种条条框框,想怎么组合就怎么组合,比如所有书籍就是由各个分类组合而来的,你的leader马上又向你炫耀了统计所有书籍的浏览量和销售量的办法。
定义一个AllStatistics类继承Statistics,具体代码如下所示:

class AllStatistics < Statistics 

  def initialize
    @statistics = []
    @statistics << NovelStatistics.new
    @statistics << TechnicalStatistics.new
  end 

  def get_browse_count
    browse_count = 0
    @statistics.each do |s|
      browse_count += s.get_browse_count
    end
    browse_count
  end 

  def get_sale_count
    sale_count = 0
    @statistics.each do |s|
      sale_count += s.get_sale_count
    end
    sale_count
  end 

end

在AllStatistics的构造函数中将小说类书籍和科技类书籍作为子分类添加到了statistics数组当中,目前你也就只写好了这几个分类。然后使用同样的方法在get_browse_count和get_sale_count方法中统计出所有书籍的浏览量和销售量。
当前组合结构的示意图如下:

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

TechnicalStatistics.new.get_browse_count

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

AllStatistics.new.get_sale_count

当然你后面还可以对这个组合结构随意地改变,添加各种子分类书籍,而且子分类的层次结构可以任意深,正如前面所说,组合模式的扩展性非常好。
你的leader告诉你,目前他写的这份代码重复度比较高,其实还可以好好优化一下的,把冗余代码都去除掉。当然这个任务就交给你来做了,你的leader可是大忙人,早就一溜烟跑开了。

总结

组合模式的优点:
能够灵活的组合局部对象和整体对象之间的关心,对客户端来说,局部对象和整体对象的调用没有差别,使调用简单。

组合模式的缺点:
1.组合操作的成本很高,如果一个对象树中有很多子对象,可能一个简单的调用就可能使系统崩溃;
2.对象持久化的问题,组合模式是树形结构,不能很好地在关系数据库中保存数据,但是却非常适合用于xml持久化。

组合模式的适用场景:
1.维护和展示部分—整体关系得场景,如树形菜单、文件和文件夹管理。
2.从一个整体中能够独立出部分模块或功能的场景。

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索设计模式
, ruby
组合模式
ruby元编程 第2版 pdf、ruby编程语言 pdf、ruby编程语言、ruby元编程、ruby元编程第二版 pdf,以便于您获取更多的相关知识。

时间: 2024-11-02 12:36:26

详解组合模式的结构及其在Ruby设计模式编程中的运用_ruby专题的相关文章

解析proxy代理模式在Ruby设计模式开发中的运用_ruby专题

代理模式Proxy代理模式是一种结构型设计模式,主要解决的问题是:在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上.在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层.如下图:     比如说C和A不在一个服务器上,A要频繁的调用C,我们可以在A上做一个代理类Proxy,把访问C的工作交给Proxy,这样对于A来说,就好像在直接访问

详解Ruby设计模式编程中对单例模式的运用_ruby专题

简介      单例模式是设计模式中最简单的形式之一.这一模式的目的是使得类的一个对象成为系统中的唯一实例.要实现这一点,可以从客户端对其进行实例化开始.因此需要用一种只允许生成对象类的唯一实例的机制,"阻止"所有想要生成对象的访问.使用工厂方法来限制实例化过程.这个方法应该是静态方法(类方法),因为让类的实例去生成另一个唯一实例毫无意义. 要点      显然单例模式的要点有三个:一是某个类只能有一个实例:二是它必须自行创建这个实例:三是它必须自行向整个系统提供这个实例.      

深入剖析Ruby设计模式编程中对命令模式的相关使用_ruby专题

命令模式是对象行为型使用率比较高的设计模式,别名:Action(动作),Transaction(事务) 意图: 将一个请求封装为一个对象,从而使你可对不同的请求进行参数化:对请求排队或记录请求日志,以及支持可取消的操作 这里所谓的"不同的请求"也既意味着请求可能发生的变化,是一个可能扩展的功能点. 动机: 方便扩展 结构: 协作说明:    参与角色:     Command 声明一个接口以用来实现某个操作.     ConcreteCommand 将动作与Reciver对外绑定,通过

Ruby设计模式编程中对外观模式的应用实例分析_ruby专题

何为外观模式?     外观模式为子系统中一组不同的接口提供统一的接口.外观定义了上层接口,通过降低复杂度和隐藏子系统间的通信以及依存关系,让子系统更加易于使用.     比方说子系统中有一组不同的类,其中一些彼此依赖.这让客户端难以使用子系统中的类,因为客户端需要知道每一个类.外观起到整个子系统的入口.有些客户端只需要子系统的某些基本行为,而对子系统的类不做太多定制,外观为这样的客户端提供简化的接口.只有需要从某些子系统的类定制更多行为的客户端,才会关注外观背后的细节.     外观模式:为系

实例解析Ruby设计模式编程中Strategy策略模式的使用_ruby专题

今天你的leader兴致冲冲地找到你,希望你可以帮他一个小忙,他现在急着要去开会.要帮什么忙呢?你很好奇. 他对你说,当前你们项目的数据库中有一张用户信息表,里面存放了很用户的数据,现在需要完成一个选择性查询用户信息的功能.他说会传递给你一个包含许多用户名的数组,你需要根据这些用户名把他们相应的数据都给查出来. 这个功能很简单的嘛,你爽快地答应了.由于你们项目使用的是MySQL数据库,你很快地写出了如下代码: require 'mysql' class QueryUtil def find_us

Ruby设计模式编程中使用Builder建造者模式的实例_ruby专题

先来复习一下设计模式的基本概念:定义将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示. 建造者隐藏了该产品是如何组装的,所以若需要改变一个产品的内部表示,只需要重新定一个建造者就可以了.实用范围1.当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时. 2.当构造过程必须允许被构造的对象有不同表示时.角色在这样的设计模式中,有以下几个角色: 1.builder:为创建一个产品对象的各个部件指定抽象接口. 2.ConcreteBuilder:实现Builder

详解C语言的结构体中成员变量偏移问题_C 语言

c语言中关于结构体的位置偏移原则简单,但经常忘记,做点笔记以是个记忆的好办法 原则有三个: a.结构体中的所有成员其首地址偏移量必须为器数据类型长度的整数被,其中第一个成员的首地址偏移量为0, 例如,若第二个成员类型为int,则其首地址偏移量必须为4的倍数,否则就要"首部填充":以此类推 b.结构体所占的总字节数即sizeof()函数返回的值必须是最大成员的长度的整数倍,否则要进行"末尾填充": c.若结构体A将结构体B作为其成员,则结构体B存储的首地址的偏移量必须

详解Java设计模式编程中命令模式的项目结构实现_java

正论: 命令模式把一个请求或者操作封装到一个对象中.命令模式运行系统使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能. 通俗: 其实很好理解.命令模式,关心的就是命令(或者称为操作).打个比方.在一个公司里面,整个运作就像一个系统.某个boss发布了一个命令,中层领导接到这个命令,然后指派给具体负责这个员工.整个流程很清晰吧.有一个需求,如何将这个流程固定下来,形成一个系统.我们只要抓住了重点:命令.将它抽取出来,其他的都迎刃而解了.抽取出命令,封装成一个独

详解Java设计模式编程中的Flyweight享元模式的开发结构_java

享元(Flyweight)模式:通过共享技术以便有效的支持大量细粒度的对象. 享元模式在阎宏的<java与模式>中分为单纯享元模式和复合享元模式,复合模式的复合享元是不可以共享的,享元对象能做到共享的关键是区分内蕴态(Internal State)和外蕴态( External State).这两个"蕴态"翻译的太难懂,我不是说翻译的不好,可能是我理解能力差,还是<Design Pattern Elements of Reusable Object-Oriented S