帮酷LOGO
0 0 评论
  • 显示原文与译文双语对照的内容
文章标签:设计  DES  BOO  patterns  设计模式  design-pattern  pattern  
Examples from the book Design Patterns in Ruby by Russ Olsen

  • 源代码名称:design-patterns-in-ruby
  • 源代码网址:http://www.github.com/nslocum/design-patterns-in-ruby
  • design-patterns-in-ruby源代码文档
  • design-patterns-in-ruby源代码下载
  • Git URL:
    git://www.github.com/nslocum/design-patterns-in-ruby.git
  • Git Clone代码到本地:
    git clone http://www.github.com/nslocum/design-patterns-in-ruby
  • Subversion代码到本地:
    $ svn co --depth empty http://www.github.com/nslocum/design-patterns-in-ruby
    Checked out revision 1.
    $ cd repo
    $ svn up trunk
    
  • ruby 中的设计模式

    已经更新为使用 ruby 2.2.0

    书名:。

    这本书涵盖了 14种原始 23 GoF设计模式。

    五个要点

    从保持不变的事物中分离出事物。

    • 提升代码
    • 提高可维护性

    针对 接口 编程, 而 非 ( 接口 的 ) 实现.

    代码应该属于最一般的对象,并且特定类应该从通用类继承。

    • 增加模块化

    优先于继承

    避免说对象是某种事物,而是说它有某些东西。 换句话说,引用其他类或者模块通常比在超类中放置功能更好。

    • 增加封装
    • 增加模块化,实现可以轻松交换

    委托,委托,委托

    让其他类处理它的域内的功能

    你不需要它( Russ加了 Olsen )

    不要以你不需要的灵活性来实现特性或者设计,因为你可能永远不需要它。

    设计模式

    模板方法

    • 使用算法之间常见的方法创建一个的骨骼类
    • 为每个算法创建一个的子类,并覆盖骨骼类中的常用方法。

    缺点:

    • 无运行时灵活性

    策略

    它的基本思想是给分配任务以封装算法,即运行时的interchangable。

    策略 Pattern 中我们有一个对象( 上下文 ),它试图完成一些事情。

    • 定义一组对象,所有对象都做相同的事情( 例如: 格式化输出,生成图形 等等 )。
    • 确保对象族共享相同的接口,以便它们是 interchangable。

    从上下文对象传递数据的策略有两种策略,即对象到对象的策略。 我们可以在调用策略时将数据作为参数传递,或者我们可以将上下文对象作为单个参数传递。

    如果策略非常简单且只有一种方法,我们甚至可以使用代码块,并仅使用 block.call。 但是,如果需要多种方法,则策略必须以独立类的形式进行。

    优点:

    • 算法在运行时是 interchangable
    • 促进模块化

    观察者

    观察者 Pattern 用于事件驱动编程

    名为主题的对象维护它的依赖项列表,并根据任何状态更改自动通知它们的所有状态变化,通常是通过调用其中一个方法来通知他们。

    标准库包含一个可以实现这里 Pattern的可以观察的MODULE。

    也可以将代码块作为观察者使用。 ruby 标准库中的可见 MODULE 不支持这一点,但它很容易实现。

    其他问题

    在使用可见的Pattern 时,还需要考虑一些额外的问题。

    推 vs-拉

    在默认实现中,发送给观察者的通知没有指定哪个主体的属性已经更改。 为了找出哪个属性已经改变,观察者必须检查主体的属性,这是的pull方法。

    另一种方法是推送方法,其中通知包含其他属性,向观察者提供其他信息,如下面的示例。

    observer.update(self, :salary_changed)
    observer.update(self, :salary_changed, old_salary, new_salary)

    观察者可能只需要知道主题的特定属性什么时候改变。 简单的实现将在任何属性更改时通知观察者。

    原子事件通知

    如果更新了一个主题的多个属性并且更新不是独立的,则在执行所有更新之前通知观察者。

    fred =Employee.new('Fred', 'Crane Operator', 30000)
    fred.salary =1000000# Warning! Inconsistent state here!fred.title ='Vice President of Sales'
    当观察者引发异常时应执行的操作

    当通知导致观察者引发异常时,也很重要。 处理异常的正确方法因情况不同而不同。

    复合

    复合设计 Pattern 是用于表示具有层次树结构的对象的结构 Pattern。 它允许对单个叶节点和由许多节点组成的分支进行统一处理。

    本书的实现不灵活,不允许动态创建任务,也不允许将任务动态分解成子任务。 要将任务细分为多个子任务,必须先将叶任务的类更改为 CompositeTask,然后才能添加子任务。 更好的解决方案是对叶子和内部节点使用单个节点类。 使用这里实现,叶节点可以在不需要更改它的类的情况下添加子节点。

    对于特定实现,可以简单地从节点类继承,并使用你可能需要的任何附加函数扩展它。

    迭代器

    迭代器设计 Pattern 提供对容器内元素的顺序访问,而不公开容器实际表示元素的方式。 迭代器可以被认为是可以移动的指针,它允许访问容器中封装的元素。

    外部迭代器

    迭代逻辑包含在单独的类中。 只要允许索引,迭代类就可以被泛化为处理多个对象类型。

    外部迭代器要求它的他类执行实际迭代,但是它们允许 GREATER 灵活性,因为可以控制迭代。

    内部迭代器

    所有迭代逻辑都发生在聚合对象内部。 使用代码块将逻辑传递到聚合中,然后调用块为每个元素调用块。

    colors = ['red', 'green', 'blue']
    colors.each { |color| puts color }
    可以枚举模块

    ruby 包含一个枚举器 MODULE 函数。

    可以枚举mixin提供具有多个遍历和搜索方法的集合类,并具有排序的能力。 类必须提供一个方法,每个方法都会生成集合的后续成员。 如果使用 Enumerable#max。#min, 或者 #sort,集合中的对象也必须实现有意义的<=> 运算符,因为这些方法依赖于集合成员之间的排序。

    使用自定义迭代器实现,如果在迭代它的元素时发生了原始集合类,则可以创建意外结果。 要纠正这个问题,你可以让迭代器在原始集合的副本上操作。

    classArrayIteratordefinitialize(array)
     @array=Array.new(array)
     @index=0end. . .

    命令

    命令 Pattern 是一种行为设计 Pattern,用于存储在将来调用方法所必需的信息。

    使用 GUI元素

    对于许多 gui,你可能有一个通用按钮类,你希望在许多不同的情况。 在每种情况下,按钮可能需要做不同的事情。 一种方法是为接口所需的每个按钮创建一个子类,但这会导致过多的按钮类。 更好的方法是为在单击按钮时执行的代码创建单独的类。 这个动作类可以被传递给按钮,告诉它点击时按钮应该做什么。

    从保持不变的部分( 在本例中是操作) 中,将所发生的更改分为1,即通用按钮对象(。 这种方法的一个优点是,由于操作被传递给按钮,它可以在运行时更改。

    命令只是包装在对象中的一组动作。 使用 ruby,我们可以使用Procs做同样的事情,而无需创建单独的对象。 当操作简单且不需要保存状态信息时,这是一个不错的选项,否则,命令类就是更好的选项。

    宏录制

    许多现代应用程序都具备撤销操作的能力,包括字处理器。电子表格和数据库。 可以通过使用命令设计 Pattern 来实现这里撤销功能,方法是跟踪执行的代码。

    使用命令设计 Pattern的宏录制的另一个很好的例子是在 Rails 上处理 ruby 中的迁移。

    为了撤销操作,我们需要存储一些状态信息,所以我们必须使用一个命令类而不是一个简单的过程。

    队列命令

    命令允许我们对令应用程序更方便用户和系统的命令进行排队。

    安装向导

    在安装应用程序时,许多用户在启动实际安装之前会提示用户使用许多安装选项。 用户选择决定了哪些方法调用被添加到安装程序列表的do。 如果没有命令,安装将不得不定期停止,以查询用户的首选项。

    固定开销

    在某些情况下,执行特定类型的命令有固定的开销。 将多个命令排队并执行它们将减少我们运行开销代码所需的次数。 数据库操作就是这样的一个例子。 如果没有持久数据库连接,那么每次运行数据库操作时都必须创建一个。 由于连接到数据库有一个成本,一个好的方法可以能是排队数据库操作并在批处理中执行。 当你需要向外部应用程序发出API调用时,web应用程序同样拥有相同的逻辑。

    中介

    中介 Pattern 将"。many-to-many关系网络"提升为"完整对象状态"。 使用对象建模与对象的关系增强封装,并允许将这些关系的行为修改或者扩展。

    中介为同事对象之间的通信定义接口。 ConcreteMediator实现中介接口并协调同事对象之间的通信。 在内部 communication.The ConcreteColleague中,所有的同事及其目的都是通过中介与其他同事交流。

    没有这个 Pattern,所有的同事都会彼此了解,从而导致高耦合。 我们通过一个中心点进行通信,我们有一个分离的系统,同时保持对对象交互的控制。

    命令行适配器

    将某个类的接口转换为接口客户所需的类型。 适配器允许类一起工作,因为接口不兼容。

    http://en.wikipedia.org/wiki/Design_pattern_(computer_science)

    ruby 允许在运行时修改类。 因此,我们可以在运行时修改类来添加或者更改方法,而不是创建适配器来修改类 API。

    另外,ruby 还允许对单个实例进行运行时修改。

    在运行时修改实例或者类仅在以下情况下是可取的:

    • 修改很简单
    • 你知道你正在修改的类,并且确信你的改变不会破坏事情。

    如果这些点之一不是 true,那么最好创建一个单独的适配器。

    代理

    代理保护代理

    保护代理保护对象不受未经授权的访问。 为了确保方法只能由授权用户运行,我们可以在消息传递到底层对象之前运行授权检查。

    远程代理

    远程代理提供对在远程计算机上运行的对象的访问。

    远程代理的一个很好的例子是分布式 ruby ( DRb ),它允许 ruby 程序通过网络进行通信。 使用 DRb,客户机运行一个代理,它处理后台的所有网络通信。

    虚拟代理

    虚拟代理允许我们延迟对象的创建,直到绝对必要。 这在创建对象的computationaly昂贵时很有用。

    使用消息传递简化代理

    构建代理时,我们可以为底层对象中的每个方法实现一个方法。 然而,这会导致大量重复代码,并将代理与底层对象紧密结合。 更好的方法是将方法调用direcly传递给底层对象。 ruby 包含了一种非常适合这种情况的方法,称为 method_missing。

    在 ruby 中,当对对象调用方法时,ruby 会在初始对象中查找方法,然后工作到对象超类。 如果找不到方法,那么 ruby 在初始对象中查找方法 method_missing,然后是父对象,它是父对象。 等等.

    我们可以使用 method_missing 简单地传递方法调用到基础对象,而不是实现代理中的每个基础对象方法。

    同样,这也有很大的优势:

    • 代理大大简化了,Having的方法少得多
    • 代理不与基础对象耦合,因此可以在多个对象类中重用。

    装饰器

    decorator设计 Pattern 允许将特性动态添加到现有对象中。

    通过维基百科的动机

    使用 ruby,使用 decorator Pattern 最简单的方法是为每个装饰器创建一个 MODULE。 要使用的decorator可以使用 extend 动态地添加到实例中。

    ruby 包括一个 forwardable MODULE 插件,它提供了一种简单的方法来添加委派方法。forwardable 可以让你创建更清晰更清晰的代码,这真的依赖于。

    动态替代方案

    ruby 允许动态修改实例。 我们可以使用这个特性在运行时添加 decorator。

    这是向实例添加修饰符的快速而肮脏的方法。 用原始方法创建别名,然后使用与原始方法相同的NAME 添加一个 MODULE。 如下所示:

    w =SimpleWriter.new('out')class <<walias old_write_line write_line
     defwrite_line(line)
     old_write_line("#{Time.new}: #{line}")
     endend

    对于 SimpleWriterw 实例,原始的write_line 方法仍然存在,并且由 old_write_line 指出。 现在调用 write_line 时,将执行新方法,然后执行 old_write_line

    使用扩展模块的缺点:

    • 无法轻松删除模块

    单实例

    singleton Pattern 用于确保类只有一个实例,并提供对该实例的全局访问。

    当你希望一个类的实例和许多不同的对象需要访问它时,这个 Pattern 非常有用。

    GoF: 让singleton对象的类管理创建和访问它的唯一实例。

    ruby 标准库包含一个单变量 MODULE。

    singleton module 是彻底的,它重写了多个方法以防止单例类的多个实例。 它还使用惰性实例化,只在调用 instance 方法时创建实例。

    可选的全局变量

    另外,还可以使用全局变量存储单例。 只能有一个全局变量的实例,并且按照定义它们是全局可以访问的。

    缺点:

    • 全局变量可以在任何时候重新分配。
    • 不可能进行迟缓实例化。
    常数

    由于我们可以使用全局变量来保存单例,所以我们也可以使用常量。

    缺点:

    • 可以在任何时候重新分配常量变量。 当发生更改时将返回警告,但它们仍被更改。
    • 不可能进行迟缓实例化。
    • 我们不能阻止创建单实例类的多个实例。
    类作为单例类型

    缺点:

    • 使用类来保存所有的变量和方法并不遵循传统的编码约定。

    优点:

    • 只能有一个给定的NAME 类,因此只能有一个实例的实例。
    模块作为单例

    代码与使用类作为单个类完全相同,除了我们定义一个 MODULE 而不是类。

    一个 MODULE 是方法和变量的集合,但与类不同,模块不能被实例化。 因此,使用 MODULE 更好地遵循编码约定,并使代码更加清晰。

    工厂

    工厂 Pattern 是面向对象设计中最常用的设计 Pattern 之一。 这种类型的设计 Pattern 位于创建的Pattern 之下,因为这个 Pattern 提供了创建对象的最佳方法之一。

    在工厂 Pattern 中,我们创建对象,而不向客户端公开创建逻辑,并使用公共接口引用新创建的对象。

    实现

    我们将创建一个形状接口和具体类来实现形状接口。 工厂类ShapeFactory被定义为下一个步骤。

    FactoryPatternDemo,我们的演示类将使用ShapeFactory来获取形状对象。 它将传递信息( 循环/rectangle/SQUARE) 到ShapeFactory以获取所需对象的类型)。

    抽象工厂

    抽象工厂模式围绕一个超级工厂工作,工厂创建其他工厂。 这家工厂也被称为工厂工厂。 这种类型的设计 Pattern 位于创建的Pattern 之下,因为这个 Pattern 提供了创建对象的最佳方法之一。

    在抽象工厂 Pattern 中,接口负责创建相关对象的工厂,而不明确指定它们的类。 每个生成的工厂都可以根据工厂 Pattern 给对象。

    实现

    我们将创建一个形状和颜色接口以及实现这些接口的具体类。 我们创建一个抽象工厂类AbstractFactory作为下一步。 工厂类 RedCircleFactory。GreenSquareFactory和BlueRectangleFactory定义每个工厂扩展 AbstractFactory。 创建工厂创建者/生成器类 ColoredShapeFactory。

    AbstractFactoryPatternDemo,我们的演示类使用ColoredShapedFactory来获取AbstractFactory对象。 它将信息 (RedCircle/GreenSquare/BlueRectangle) 传递给ColoredShapeFactory以获取所需工厂对象的类型。

    生成器

    构建器 Pattern 背后非常简单的思想是,将对象构造逻辑封装在它自己的类后面。 生成器类负责装配复杂对象的所有组件。 每个生成器都有一个接口,允许你逐步指定新对象的配置。

    在某种意义上,生成器是一种类似于多部分的新方法,它的中对象是在扩展过程中创建的。

    实现

    我们首先按照本书中的例子开始实现计算机系统( CPU,主板,驱动器)的基本组件类。 母板类是一个包含组件的复合组件,如 CPU。 计算机类是一个复合,包含显示。母板和驱动器。 有两种类型的CPU ( BasicCPU和 TurboCPU )。 现在你可能会体会到每次你可能需要一个计算机系统的复杂性。

    在构建我们的构建器ComputerBuilder的过程中,我们将简化在客户机应用程序中构建计算机的方法。 ComputerBuilder类处理计算机对象的实例。 然后我们构造实例方法来处理构建我们的计算机实例对象。 方法,如 turbo() 告诉我们的主板有一个 TurboCPU,而不是BasicCPU的。 另外,将驱动器添加到我们的计算机的( add_cd,add_dvd,add_hard_disk ) 方法。 还有其他方法。

    最后,在客户端应用程序中实例化一个ComputerBuilder对象,并在构建器上调用上述方法在运行时构建自定义计算机对象。

    注意,我们也利用魔术方法快速构建我们的计算机对象。 根据本书,我们重写了 method_missing() 方法来解析 NAME 方法,快速构建我们需要的对象。

    解释器

    解释器 Pattern 指定如何在语言中使用表达式。 基本思想是为可能出现在表达式中的每个符号创建一个类。 如果我们实例化这些类并将对象连接起来,它们将形成语言的语法树。

    使用

    这个设计 Pattern 很好地解决了包含的,有界的问题。 解释器设计 Pattern的一些特性使用如下:

    • Pattern 匹配语言,如 正规表达式
    • 查询语言,如 SQL
    • 配置语言(。比如。描述通信协议的语言)。
    实现

    本书展示了 Pattern的一个有趣案例,我们希望通过 NAME。大小和更复杂的搜索来查找文件。 Pattern 实现可以分为类定义。分析器开发和表达式计算三部分。

    类定义

    让我们考虑book示例,其中包含以下符号:

    '|'-> 或者类

    '&'-> 和类

    对于上面的每个符号标记,我们创建一个类,它负责解释它是表达式的一部分。

    在定义和实现类之后,我们需要解析器。 解析器将读取输入并生成抽象语法树或者 AST。 树的节点是我们类的实例化对象。

    解析器

    生成的AST是文件查找表达式的对象表示。 树的每个节点都是终端( 不会再被分解的对象) 或者非( 更抽象的对象)。

    表达式评估

    这基本上是由解析器构建的AST的评估。 树的节点根据我们设置的特定条件进行评估。



    文章标签:BOO  DES  设计  pattern  patterns  design-pattern  设计模式  

    Copyright © 2011 HelpLib All rights reserved.    知识分享协议 京ICP备05059198号-3  |  如果智培  |  酷兔英语