第6章:可维护性软件构建方法 6.2可维护性设计模式

大纲

创造性模式算法

  • 工厂方法模式建立对象而不指定要建立的确切类。
  • 抽象工厂模式将具备共同主题的对象工厂分组。
  • Builder模式经过分离构造和表示来构造复杂的对象。

结构模式设计模式

  • Bridge将抽象从其实现中分离出来,这样二者能够独立变化。
  • 代理提供另外一个对象的占位符来控制访问,下降成本并下降复杂性。
  • 复合材料组成零个或多个类似的对象,以便它们能够做为一个对象进行操做。

*行为模式数组

  • 中介者能够经过成为惟一具备其方法详细知识的课程,从而实现课堂间的松散耦合。
  • Observer是一种发布/订阅模式,它容许许多观察者对象查看事件。
  • 访问者经过将方法的层次结构移动到一个对象中来将算法从对象结构中分离出来。
  • 责任链将命令委托给一系列处理对象。
  • 命令建立封装动做和参数的对象。

对可重用性和可维护性设计模式的高层考虑缓存

创造性模式

(1) Factory Method pattern

工厂方法模式
也称为“虚拟构造器”服务器

意图:ide

  • 定义一个用于建立对象的接口,但让子类决定实例化哪一个类。
  • 工厂方法让类将实例化推迟到子类。

何时应该使用工厂方法? ----当一个类:函数

  • 没法预测它须要建立的对象的类别
  • 但愿它的子类指定它建立的对象
  • 将责任委托给多个帮助者子类中的一个,而且您须要本地化哪些帮助者是委托人的知识。

当客户不知道要建立哪一个具体类的实例,或者不想在客户代码中指明具体建立的实例时,用工厂方法。定义一个用于建立对象的接口,让其子类来决定实例化哪个类 ,从而使一个类的实例化延迟到其子类。工具

优势:布局

  • 消除了将特定于应用程序的类绑定到代码的须要。
  • 代码只处理产品接口(Trace),所以它能够与任何用户定义的ConcreteProduct(FileTrace,SystemTrace)

潜在的缺点优化

  • 客户可能必须建立Creator的子类,以便他们能够建立某个ConcreteProduct。
  • 若是客户不管如何都要继承创造者的话,这是能够接受的,但若是不是,那么客户必须处理另外一个进化点。

Open-Closed Principle(OCP) - 对扩展的开放,对修改已有代码的封闭

(2) Abstract Factory

抽象工厂模式

示例1:考虑一个用户界面工具包,它支持不一样操做系统的多个外观和感受标准:一个UI,包含多个窗口控件,这些控件在不一样的OS中实现不一样

  • 如何编写单个用户界面,并使这些界面在这些窗口管理器的不一样外观和感受标准之间移植?

示例2:考虑支持不一样控制系统的智能住宅的设施管理系统:一个仓库类,要控制多个设备,这些设备的制造商各有不一样,控制接口有差别

  • 如何编写独立于制造商的单个控制系统?

抽象工厂模式:提供接口以建立一组相关/相互依赖的对象,但不须要指明其具体类。

名称:抽象工厂(或工具包)
意图:容许独立于实现建立相关对象的家族
方法:使用工厂返回可用于建立相关对象集的工厂。

适用性

  • 独立于初始化或表示的不一样系列组件(产品)
  • 必须以互斥和一致的方式使用
  • 隐藏来自客户的多个家庭的存在
  • 制造商独立
  • 应对即将到来的变化

笔记

Abstract Factory建立的不是一个完整产品,而是“遵循固定搭配规则的多类产品的实例”,获得的结果是:多个不一样产品的对象,各产品建立过程对客户可见,但“搭配”不能改变。
本质上,Abstract Factory是把多类产品的工厂方法组合在一块儿

例如

GraphPoet由Word列表和WordNeighborhood列表组成
NetworkTopology由计算机/服务器/路由器列表和NetworkConnection列表组成
换句话说,Vertex和Edge的对象建立密切相关,但不该该是独立的。 Vertex和Edge的子类,要有固定搭配,不能随意组合

抽象工厂vs工厂方法

工厂方法仅用于建立一个产品,但抽象工厂用于建立相关或依赖产品系列。

  • 建立一个对象vs建立多个类型的对象

工厂方法模式向客户端公开建立对象的方法,而抽象工厂则显示由这些工厂方法组成的一系列相关对象。

  • 一个工厂方法vs多个工厂方法

抽象工厂模式使用组合来将建立对象的责任委派给另外一个类,而工厂方法模式使用继承并依赖于派生类或子类来建立对象。

  • 使用组合/委派vs使用继承/子类型

(3) Builder

构造器模式

构造器模式:将复杂对象的构造与其表示分开,以便相同的构建过程能够建立不一样的表示。建立复杂对象,包含多个组成部分

  • 复杂对象的构建在多个表示中很常见

示例:将文档转换为多种不一样的格式

  • 写出文件的步骤是相同的
  • 每一步的细节取决于格式

就像你在麦当劳点餐同样!

途径

  • 构建算法由单个类(“导演”)指定
  • 算法的抽象步骤(每一个部分一个)由接口(“构造器”)指定
  • 每一个表示都提供了界面的具体实现(“实际构造者”)

注意:客户端要的不是一堆零散的对象(抽象工厂那样的结果),而是一个完整的产品,客户端不关心其中的细节组成部分是什么,如何建立

比较:抽象工厂vs构造器

Abstract Factory建立的不是一个完整产品,而是“遵循固定搭配规则的多类产品实例”,获得的结果是:多个不一样产品的实例对象,各产品建立过程对客户可见,但“搭配”不能改变。

  • 专一于产品系列(相似的产品类型)
  • 不隐藏构造过程

Builder建立的是一个完整的产品,有多个部分组成,客户不须要了解每一个部分是怎么建立,各个部分怎么组合,最终获得一个产品的完整对象

  • 基础产品须要构建为系统的一部分,但建立很是复杂(产品组合)
  • 复杂产品的构造会不时变化
  • 隐藏用户的建立过程

抽象工厂和构造器能够很好地协同工做,适用于多种复杂产品系列

例如

GraphPoet由Word列表和WordNeighborhood列表组成
NetworkTopology由计算机/服务器/路由器列表和NetworkConnection列表组成
换句话说,ConcreteGraph的对象建立由Vertex和Edge子类型的对象建立组成。 四个图应用都要建立图对象,要为不一样应用创建出不一样的顶点列表和边列表。

比较:模板方法vs构造器

模板方法:行为模式,目标是为了复用算法的公共结构(次序)

  • 定义了一个操做中算法的骨架(步),而将具体步骤的实现延迟到子类中,从而复用算法的结构并可从新定义算法的某些特定步骤的实现逻辑。
  • 复用算法骨架,强调步骤的次序 - 子类override算法步骤

Builder:一种创造模式,目标是“建立复杂对象”,灵活扩展

  • 将一个复杂对象的构造方法与对象内部的具体表示分离出来,一样的构造方法能够创建不一样的表现。
  • 不强调复杂对象内部各部分的“次序”
  • 子类override复杂对象内部各部分的“建立”
  • 适应变化:经过派生新的builder来构造新的对象(即新的内部表示),OCP

结构模式

(1) Bridge

桥接模式

适用性

  • 用不一样的实现去耦合抽象概念
  • 实施可能会在运行时切换
  • 实施变动不该影响客户
  • 从客户端隐藏类的界面

结构:使用两个层次结构

  • 适合客户的逻辑
  • 为不一样的实现物理一个

对象:提升可扩展性

  • 逻辑类和物理类独立变化
  • 隐藏来自客户的实施细节

桥接模式是OOP最基本的结构模式,经过委托+继承创建两个具体类之间的关系(DIP依赖转置,抽象依赖于抽象)

桥接模式与策略模式

桥接:结构模式,强调双方的运行时委派链接

  • 结构模式代表对象组成或关联或继承以造成更大对象的方式,即它们关注对象组成。

一个类A的对象中有其余类B的对象做为其组成部分,但A的对象具体绑定到B的哪一个具体子类的实现?在运行时经过委托加以组合,并永久保存这种代理关系。

  • 它将抽象和实现分离开来,并容许二者独立地变化。

策略:行为模式,强调一方运行时使用另外一方的“算法”

  • 行为模式处理算法或业务逻辑(而不是对象建立自己),即它们专一于对象之间的协做。
  • 它使您可以在运行时在一系列算法中切换多种算法。

“算法”一般实现为“类的某个方法”的形式,策略模式的目的并不是在“调用算法的类”与“被调用算法所在的类”之间创建起永久联系,而只是帮助前者临时使用后者中的“算法”,前者无需永久保存后者的实例。

策略:使用螺丝刀的时候,针对不一样的工做任务,选取不一样的“刀头”,但目的并不是将螺丝刀与刀头组合起来创建永久的团队,而只是临时经过委派完成任务(即调用刀头的“算法”),而后两者再无联系。

桥接:类A须要完成“通信”功能,它有一个组成部分Device类,这是个抽象接口(模式中的实现者),有多种实现方式(PC,tablet,phone,即ConcreteImplementor),该模式在运行时为类A的具体子类型实例设定具体的Device的具体实现(强调对A和Device两个抽象类的各自实例之间如何创建委托),进而在其余各项功能中利用其通信功能(这再也不是桥接模式关注的目标)。

(2) Proxy

代理模式

代理模式动机

目标:

  • 防止客户直接访问对象
  • 经过充当传递实体或占位符对象来容许进行对象级访问控制。

解决方案:

  • 使用一个称为代理的附加对象
  • 客户端只能经过代理访问受保护的对象
  • 代理跟踪受保护对象的状态和/或位置

某个对象比较“敏感”/“私密”/“贵重”,不但愿被客户端直接访问到,故设置代理,在两者之间创建防火墙。

代理模式:3种类型

信息缓存(“远程代理”)

  • 代理对象是不一样地址空间中对象的本地代理
  • 若是信息没有常常变化,那么很好

Standin(“虚拟代理”)

  • 对象建立成本过高或者下载成本过高。
  • 若是真正的对象不被太频繁地访问,那么很好

访问控制(“保护代理”)

  • 代理对象为真实对象提供保护
  • 好的时候,不一样的演员应该对同一个对象有不一样的访问和查看权限
  • 示例:管理员,教师和学生访问的成绩信息。

代理与适配器

适配器:结构模式,目的是将类/库A的接口更改成客户端B的指望。
目的:消除不兼容,目的是B以客户端指望的统一的方式与A创建起联系。

  • 典型的实现是一个包装类或一组类。
  • 目的不是为了方便未来的界面更改,而是当前的界面不兼容。

代理:行为模式,也使用包装类,但其目的是为实际资源建立一个替身。
目的:隔离对复杂对象的访问,下降难度/代价,定位在“访问/使用行为”

  • 真正的资源驻留在远程计算机上(代理方便与远程资源的交互)
  • 真正的资源建立成本很高(代理确保成本不会发生,除非/直到真正须要)
  • 代理提供了替代它所表明的真实资源的插入替代品,所以它必须提供相同的接口。

3) Composite

组合模式

问题:

  • 应用程序须要操做“原始”和“复合”对象的分层集合。
  • 原始对象的处理是单向处理的,处理复合对象的处理方式不一样。
  • 在尝试处理每一个对象以前必须查询每一个对象的“类型”是不可取的。

意图:将对象组成树结构以表示总体部分层次结构。

  • 组合模式让客户能够统一处理单个物体和物体的组合。
  • 递归组合
  • “目录包含条目,每一个条目均可以是目录。”
  • 一对多“has a”之上“is a”层次

组合策略

包含菜单项的菜单,每一个菜单项均可以是菜单。
包含小部件的行列GUI布局管理器,每一个小部件均可以是行列GUI布局管理器。
包含文件的目录,每一个文件均可以是一个目录。
包含元素的容器,其中每一个容器均可以是容器。
树结构

复合vs装饰器

复合:结构模式,容许您以容许外部代码将整个结构视为单个实体的方式构建分层结构(树)。目的是在同类型的对象之间创建起树型层次结构,一个上层对象可包含多个下层对象

  • 叶实体的接口与复合实体的实体彻底相同。
  • 复合结构中的全部元素都具备相同的界面,即便有些元素是叶节点,而其余元素是整个结构。

装饰器:结构模式,也容许一个实体彻底包含另外一个实体,以便使用装饰器看起来与包含的实体相同。强调的是同类型对象之间的“特性增长”问题,它们之间是平等的,区别在于“拥有特性”的多少,每次装饰只能做用于一个对象。

  • 这容许修饰器修改其封装的行为或内容,而不改变实体的外观。

行为模式

(1) Observer

观察者模式

问题:依赖的状态必须与主人的状态一致

解决方案:定义四种对象:

  • 摘要主题:保留家眷名单; 当主人更改时通知他们
  • 抽象观察者:定义更新依赖者的协议
  • 具体主题:为受抚养人管理数据; 当主人更改时通知他们
  • 具体的观察者:在收到更新消息后得到新的主体状态

“粉丝”对“偶像”感兴趣,但愿随时得知偶像的一举一动
粉丝到偶像那里注册,偶像一旦有新闻发生,就推送给已注册的粉丝(回调粉丝的特定功能)

建模对象之间的一对多依赖关系

  • 链接观察对象的状态,主体与许多观察对象,观察者

用法:

  • 保持冗余状态的一致性
  • 优化一批更改以保持一致性

保持一致性的三个变体:

  • 推送通知:每当主题状态改变时,全部观察者都会收到通知
  • 推送更新通知:主题还将已更改的状态发送给观察者
  • 拉通知:观察者询问对象的状态

也称为发布 - 订阅(Publish-ubscribe)。

优势:

  • 主体和观察者之间的低耦合:主体不知道家眷
  • 支持广播:动态添加和删除观察员
  • 意外更新:由观察者在计算上不受主题的控制

实施问题

  • 存储观察员列表:一般在主题中
  • 观察多个主题:一般将参数添加到update()
  • 谁触发更新:主体的状态设置操做

(2) Visitor

访客模式

访问者模式:容许在运行时将一个或多个操做应用于一组对象,将操做与对象结构分离。 对特定类型的对象的特定操做(访问),在运行时将两者动态绑定到一块儿,该操做能够灵活更改,无需更改被访问的类

  • Visitor模式实际上作的是建立一个使用其余类中的数据的外部类。
  • 若是操做逻辑改变了,那么咱们只须要在访问者实现中进行更改,而不是在全部项目类中进行更改。

本质上:将数据和做用于数据上的某种/些特定操做分离开来。

访客与迭代器

迭代器:行为模式,用于顺序访问汇集,而不暴露其基础表示。 因此你能够在一个Iterator后面隐藏一个List或数组或相似的聚合。
迭代器:以遍历的方式访问集合数据而无需暴露其内部表示,将“遍历”这项功能委托到外部的迭代器对象。

访问者:行为模式,用于对元素结构执行操做而不改变元素自己的实现。
在特定ADT上执行某种特定操做,但该操做不在ADT内部实现,而是委托到独立的访客对象,客户端可灵活扩展/改变访问者的操做算法,而不影响ADT

策略vs访客

访客:行为模式
策略:行为模式

两者都是经过受权创建两个对象的动态联系

  • 可是Visitor强调是的外部定义某种对ADT的操做,该操做于ADT自身关系不大(只是访问ADT),故ADT内部只须要开放accept 访客)便可,客户端经过它设定访客操做并在外部调用。
  • 而策略是强调是对ADT内部某些要实现的功能的相应算法的灵活替换。这些算法是ADT功能的重要组成部分,只不过是委托到外部战略类而已。

区别:访客是站在外部客户的角度,灵活增长对ADT的各类不一样操做(哪怕ADT没实现该操做),策略是站在内部ADT的角度,灵活变化对其内部功能的不一样配置。

(3) Mediator

中介模式

使用中央控制器是中介模式的关键方面。

  • 机场控制塔照看能够起飞的航班,而且全部的通讯都是从飞机到控制塔完成的,而不是进行飞机与飞机之间的通讯。 多个对象之间要进行交互,不是直接交互,而是经过mediator,实现消息的广播,从而将对象之间松散耦合,利于变化

经过封装不一样对象集相互交互和通讯的方式,容许松耦合。

  • 容许每一个对象的操做彼此独立地变化。

观察者模式与中介模式

观察者模式:定义对象之间的一对多依赖关系,以便当一个对象改变状态时,它的全部依赖项都会被自动通知和更新。
一组对象对另外一个对象B的状态变化感兴趣(1对多),因此对其进行观察.B维持着一个对本身感兴趣的对象列表,一旦本身发生变化,就通知这些对象。并不对等,一方只是“通知”,另外一方接到通知后执行特定行为。

中介模式:定义一个封装一组对象如何交互的对象。介体经过让对象明确地互相引用来促进松散耦合,而且可让您独立地改变它们的相互做用。
一组同类型的对象,彼此之间相互发/收消息(多对多),不存在谁观察谁的问题,全部对象都对等。每一个对象都维持一个中介,将通信的任务委托给它,本身只负责发送和接收便可,无需了解其余物体,实现了“解耦”。

观察者模式:本身来广播,其余对象接收;
中介模式:第三方中介负责广播(相似于“邮件列表”)。

(4) Command

命令

意图

  • 将请求封装为一个对象,从而让您用不一样的请求参数化客户端,排队或记录请求,并支持可撤销操做。

将“指令”封装为对象,指令的全部细节对客户隐藏,在指令内对具体的ADT发出动做(调用ADT的细节操做)

  • 将“调用对象的方法”提高为彻底对象状态
  • 面向对象的回调

问题

  • 须要向对象发出请求,而不知道请求的操做或请求的接收者。

客户端但愿执行指令,但不想知道指令的细节,也不想知道指令的具体做用对象

命令将调用操做的对象与知道如何执行操做的对象分离。

  • 为了实现这种分离,设计人员建立了一个抽象基类,它将接收者(对象)与动做(指向成员函数的指针)进行映射。 基类包含一个execute()方法,它只是简单地调用接收方的动做。

每当客户端须要对象的“服务”时,Command对象的全部客户端都经过简单地调用对象的虚拟execute()方法将每一个对象视为“黑盒子”。
将全部对客户提供的指令与内部执行的ADT操做完全分开,指令对外看来是“黑盒” - 进一步“变本加厉”的封装!

一个Command类包含如下的一些子集:

  • 一个对象,一个应用于该对象的方法,以及该方法应用时传递的参数。
  • 命令的“执行”方法会致使碎片汇集在一块儿。

外观与命令

外观:结构模式
命令:行为模式

均强调对某个复杂系统内部提供的功能的“封装”,对外提供简单的调用接口,简化客户端的使用,“隐藏”细节。
命令:强调将指令封装为了“对象”,提供了统一的对外接口(执行)。
外观:没有显式的“对象”,仍然经过类的方法加以调用。

(5) Chain of responsibility

责任链模式

问题

  • 可能有多个“处理程序”或“处理元素”或“节点”对象,以及必须处理的请求流。

针对一个请求,可能有多个处理模块

  • 须要高效地处理请求,而无需硬连线处理程序关系和优先级或请求处处理程序映射。

各类不肯定状况存在,不能以“硬编码”的方式指明按何种次序调用处理模块
意图

  • 经过给予多个对象机会来处理请求,避免将请求的发送者耦合到它的接收者。
  • 连接接收对象并沿着链传递请求,直到对象处理它为止。 使用包含许多可能的处理程序的单个处理管道启动和离开请求。 具备递归遍历的面向对象的链表。

避免在请求方和各处理者之间的紧耦合:构造流水线,请求在其上传,直到被处理为止。

将处理元素封装在“管道”抽象中; 并让客户在管道入口处“启动并离开”他们的请求。
客户端只需在流水线的入口发出请求便可,请求自动在流水线上传递,直到被处理。

该模式将接收对象连接在一块儿,而后将任何请求消息从对象传递到对象,直到它到达可以处理消息的对象。

  • 处理程序对象的数量和类型事先是未知的,它们能够动态配置。

处理器对象的数据和类型事先都不肯定,须要动态配置

  • 连接机制使用递归组合来容许无限数量的处理程序进行连接。

使用递归组合的方式,将多个处理程序连成“职责链”
责任链简化了对象互连。

  • 发件人和收件人不是保留对全部候选收件人的引用,而是每一个发件人对链的头部保持一个单一的引用,而且每一个收件人对链中的其直接后继者保持单一引用。

请求的发起者无需维护全部可能的处理程序对象,只需记录职责链的入口;每一个处理程序只须要维护“下一个”处理程序便可,从而简化了各个处理程序之间的链接关系。

访客与责任链

不是像传统类设计中将操做与数据捆绑在一块儿造成ADT,这两个设计模式都是将“数据”与“做用于数据上的客户端定制操做”分离开来
缘由:操做能够灵活增长,运行时使用的操做能够动态配置,多个操做的执行次序能够动态变化

区别1:visitor只定义了一个操做,chain of responsibility定义了一组操做及其之间的次序
区别2:访客中,客户建立访客以后传入ADT,ADT再将执行权委托到visitor;责任链中,控制权彻底在各个处理程序之间流转,ADT(请求对象)彻底感觉不到。

可重用性和可维护性设计模式的高层考虑

使用设计模式的线索(1)

文本:“独立于制造商”,“独立于设备”,“必须支持一系列产品”
=>抽象工厂模式
文本:“必须与现有对象接口”
=>适配器模式
文本:“必须与多个系统接口,其中一些系统将在将来开发”,“必须展现早期的原型”
=>桥模式
文本:“必须与现有的一组对象接口”
=>外墙模式

使用设计模式的线索(2)

文本:“复杂结构”,“必须具备可变的深度和宽度”
=>复合模式
文本:“必须是位置透明的”
=>代理模式
文本:“必须可扩展”,“必须可扩展”
=>观察者模式
文本:“必须提供独立于该机制的政策”
=>策略模式

总结

组合,适配器,桥接,包装,代理(结构模式)

  • 重点:组成物体造成更大的结构
    •从旧功能中实现新功能,
    •提供灵活性和可扩展性

命令,观察者,策略,模板(行为模式)

  • 重点:算法和对象的责任分配
    •避免与特定解决方案紧密耦合

抽象工厂,生成器(创造性模式)

  • 重点:建立复杂的对象
    •隐藏复杂对象的建立和组合

总结

创造性模式

  • 工厂方法,抽象工厂,Builder

结构模式

  • 桥接,代理,复合

行为模式

  • 中介,观察员,访问者,责任链,命令

对可重用性和可维护性设计模式的高层考虑

相关文章
相关标签/搜索