软件设计笔记

发布时间 2023-12-08 22:35:38作者: xiaomamm

软件设计考完有几天了,然而之前的ppt整理并没有完成   不过已经考完没有心劲再搞了,所以直接发出来,主要参照合工大   徐本柱的ppt   同时也包括对应教材里的内容       

有ppt原件和教材的pdf     但是不方便直接发,博客园不知道能不能私信,

看了下复制到这的连图片都没有   很多内容还是有图片比较好一点(类图什么的)

笔记   如下

程序设计与体系结构

前言

l  复习笔记(引用主要为ppt内容,还包括软件架构与模式这本书)

l  还有几天时间,

l  大家重点复习oo的原理和规则,

l  另外就是那些设计模式和最后的架构模式,理解和运用,包括例子的应用       

l  5大oo原则,

l  常用的模式,如适配器模式,桥接模式,装饰模式,组合模式,代理模式,命令模式,观察者模式,策略模式,状态模式,访问者模式,工厂方法,抽象工厂,单例模式等常用的设计模式,

l  以及组合应用,还有常用的架构模式,都需要重点掌握

l  上述是老师群里发的重点参考

l  这不就是全部么

 

 

 

 

目录

程序设计与体系结构... 1

前言... 1

目录... 2

OO面向对象编程... 2

软件架构... 2

软件设计的模式... 5

设计模式类型... 7

设计模式-结构型... 11

设计模式-行为型... 38

行为型模式概述... 38

TEMPLATE METHOD(模板方法) ——类行为型模式... 39

COMMAND(命令) ——对象行为型模式... 43

OBSERVER(观察者)——对象行为型模式... 46

STRATEGY(策略)——对象行为型模式... 51

MEDIATOR(中介者)——对象行为型模式... 55

STATE( 状态)——对象行为型模式... 60

VISITOR( 访问者)——对象行为型模式... 64

ITERATOR(迭代器)——对象行为型模式... 67

(职责链)——对象行为型模式... 70

设计模式-创建型模式... 73

架构模式... 73

 

 

 

 

 

 

 

 

 

OO面向对象编程

l  OO面向对象编程

n  软件系统难以维护的原因

u  过于僵硬  难以加入新性能  如果要做必要的扩展,就要修改预期相关联的模块

u  过于脆弱  很难改动,与过于僵硬同时存在,牵一发动全身

u  复用率低  类似的算法函数模块,由于已有的代码依赖于很多其他的东西,很难分离最好的复用方法成了cv      一改就出错,改一个别的也出错

u  黏度过高  一个改动有两种方式

l  软件设计黏度  说明软件的可扩展能力  修改之后需要大量的工作则黏度高

l  环境黏度   开发环境等,避免环境对系统性能的限制

l  A  保存原始设计意图和原始设计构架

l  B  权宜之计,解决短期问题,牺牲长期利益

l  如果一个系统总是后一种方法更容易,就称之为黏度过高

u  面向对象设计的规则就是帮助开发者实现这些目标(构造正确的可读的可扩展的系统)

面向对象设计原则概述

对一个类的面向对象设计规则

封装,抽象和信息隐藏

关注点分离和单一职责原则

接口隔离原则

对多个类的面向对象设计规则

松耦合

里氏替换原则

契约式设计

开闭原则

依赖倒置原则

程序可读性正确性和可扩展性

u  可读性

l  封装,抽象和信息隐藏

n  封装的原则是把对象的数据和方法封装在类中,对象通过对外的接口抽象地展示自己的功能。

n  没有通过接口对外提供的功能方法和数据被隐藏起来。

l  关注点分离和单一职责原则

n  要求一个类专门完成一个任务,降低和其他类的耦合,把相关功能都包含在内实现高内聚,类和类分离就提高了程序的清晰性。

u  正确性

l   

u   

n  面向对象设计原则

u   单个类设计原则

l   抽象、封装和信息隐藏

l   关注点分离和单一职责原则

l   接口隔离原则ISP

u   多个合作类

l   松耦合

l   开闭原则OCP

l   Liskov 替换原则LSP

l   依赖倒置原则DIP

l   契约式设计

n  程序的可读性、正确性和可扩展性

u   可读性

l   封装、抽象和信息隐藏

l   关注点分离和单一职责原则

u   正确性

l   里氏替换原则

l   契约式设计原则

l   多态程序的正确性

u   可扩展性

l   接口隔离原则

l   松耦合

l   里氏替换原则

l   开闭原则

l   依赖倒置原则

n  封装、抽象和信息隐藏

u  封装

l   方法和数据封装成一个对象

l   实现信息隐藏、接口对外

u  抽象

l   方法的接口使对象功能抽象

l     对象实现接口定义方法

u  信息隐藏

l   封装内部属性

l   通过接口访问(外部属性)

l   保密原则

n  开放-闭合原则OCP

u   

u  软件实体可以指一个软件模块、一个由多个 类组成的局部结构或一个独立的类。

u  设计目标

l  可扩展性(Extensibility)

n  ——与过于僵硬的相反

l  灵活性(Flexibility)

n  ——与过于脆弱相反,允许代码修改平稳发生,不波及 其他模块

l  可插入性(Pluggability)

n  ——黏度过高的反面

u   1. 可扩展,即“对扩展是开放的”(Open For Extension)

u   2. 不可更改,即“对更改是封闭的”(Closed for Modification)

u  示例

l  抽象成一个类  两个子类继承shape

u  选择性封闭

l  没有任何一个大的程序可以做到完全封闭,无论模块多么封闭,都会存在一些 无法对之封闭的变化

l  必须对设计的模块应该对何种变化封闭做出选择,设计的模块应对某些变化

l  比如在前面的例子中增加一个条件,要求所有的圆必须 在矩形之前画出,那么Listing 2 中的DrawAllShapes 函数 无法对这种变化做到封闭

u  使用抽象获得显式封闭

l  抽象才能封闭

l  需要一个“顺序抽象体”,具体的顺序定义了一种shape 类型必须在另一种shape 类型之前画出。

u  提供了一种对Shape 对象排序的方法,也使得各个Shape 对象可以按 照一定顺序输出到图形用户界面。但是我们至此还没有一个很好的对 于顺序的抽象体。按照我们的设计,Shape对象应该重定义Precedes 方法来指定顺序。我们应该在Circle::Precedes 成员函数中写一些什么 代码来保证圆形一定会被先于矩形画出呢?

u  显然这个函数并不符合开放―封闭(open-closed)原则,因为没有办 法使得这个函数对于Shape类的派生类封闭,每次创建一个新的shape 派生类,这个函数就得改写

u  结论

u  从多种意义上来讲,这个原则是面向对 象设计的核心

u  遵循这个原则带来的好处就是面向对象 技术所声称的优点:可重用性和可维护 性

u  并不是说仅仅使用一种面向对象编程语 言就是遵循这个原则,它依赖于设计者 对程序中他认为可能发生变化的部分做 出合理的设计上的抽象

n  Liskov 替换原则LSP

u  里氏替换原则lsp

l  问题

n  滥用继承是一个相当普遍的现象,以为会用 继承,会用多态,便很OO

n  在滥用继承是亵渎OO的精髓,知道ISA关系, 但还需知道ISA真正的含义

n  是什么设计规则在支配着这种特殊的继承用 法?最佳的继承层次的特征?怎样的情况又 让我们容易掉进不符合OCP原则的陷阱中

l  里需要如下的替换性质:若对于每一个类型S的对象o1, 都存在一个类型T的对象o2,使得在所有针对T编写的程 序P中,用o1替换o2后,程序P的行为功能不变,则S是T 的子类型

l  违背该原则的后果

n  如果某个函数使用了指向基类的指针或引用,却违背LSP 原则,那么这个函数必须了解该基类的所有派生类,显 然违背开放-封闭原则OCP

l  直接变成子类是更好的选择()

l  显然DrawShape函数的设计非常不合理,它必 须知道Shape类所有的派生类,每构建一个从 Shape类派生的新类它都必须作更改

l  我们经常说继承是ISA关系,显然,从一般的 意义上讲,一个正方形就是一个(ISA)矩形

n  首先引起我们注意的是Square并不同时需要成员 变量itsHeight和itsWidth,但还是会继承它们

n  其次Square会继承SetWidth和SetHeight函数,对 正方形来说完全不适合,注意到正方形的长和宽 相等,可以按下面的方式改写SetWidth和 SetHeigh

l  没有改变lenth只改变了width

l  用子类对象替代了基类的对象,基类方法的调用者不会察觉到,所以覆写的方法必须和基类中的对应的方法具有相同的行为。以同样的方法可以调用基类的方法和子类的方法。它们的返回值也应该是同类的。

l  程序遵循了以上条件就可以毫无风险的使用多态对象。里氏替换原则就是该条件。

l  将函数置虚

l  ?

u  一个正方形是一个矩形,但是一个Square对象并非一个Rectangle对象

u  原因:Square对象的行为功能(behavior)与Rectangle对象的行为功能并不相容,行为功能才是软件所关注的问题

u  LSP原则清楚地指出,OOD中ISA关系是就行为功能而言,行为功能是外在、公开的,是客户程序所依赖的

u  按照LSP和OCP原则,所有派生类的行为功能必须和客户程序对其基类所期望的保持一致

u  结论

l  OCP原则是OOD中很多说法的核心,它 可以让应用程序更易维护、更易重用及 更健壮。LSP原则是符合OCP原则应用程 序的一项重要特性

l  仅当派生类能完全替换基类时,才能放 心地重用那些使用基类的函数和修改派生类型

n  依赖倒置原则DIP

u  这个应用程序中总共有三个模块,或称子程序, “Copy”模块调用其他两个模块

u  两个底层模块具有很好的可重用性

u  而“Copy”模块在一个不涉及键盘和打印机的程 序中就无法重用

u  设想一个新的程序用来实现拷贝键盘输入的字符 到磁盘文件,自然就想到重用“Copy”模块,因 为“Copy”模块封装了我们所需要的高层的策略, 它知道如何从一个字符源拷贝字符到接收器中

u  不幸的是,“Copy”模块依赖“Write Printer”模块

u  在拷贝策略中加上“if”语句以通过标志变量来选择 “Write Printer”模块或“Write Disk”模块,多的设备就会 加入拷贝程序中,“Copy”模块就会充满if/else 的语句, 也意味着依赖很多低层的模块。这最终将使“Copy”模块 变得僵化而脆弱

u  问题是包含高层策略的模块依 赖于它所控制的低层模块

u  给出两个接口来解决问题

u  A. 高层模块不应该依赖于低层模块。二者都应该依赖于 抽象。

u  B. 抽象不应该依赖于细节。细节应该依赖于抽象。

u  Dip的含义

l  很多传统的软件开发方法,比如结构化分析和设计,总 是倾向于创建高层模块依赖于低层模块、抽象则依赖于 细节的软件结构

l  一个设计良好的面向对象的程序的依赖关系结构相对于 传统过程式方法设计的通常的结构而言就是被“倒置” 了

u  层次化

l  Booch曾经说过:“所有的结构良好的面向对象 架构都具有非常清晰的层次,每一个层次通过一 个被很好地定义和控制的接口向外提供了一系列 相互内聚的服务。

l  这个陈述的简单理解可能会致使一个设计者设计 出类似下图的结构,在图中高层的策略类调用了 低层的机制层;而机制层又调用更具体的工具类

l  存在一个隐伏的特性,那就是:Policy Layer对于 其下层次一直到Utility Layer的改动都是非常敏感 的,这意味着依赖是可传递的

l  依赖没有消失一直传递下去了

l  提供抽象接口,下一层通过接口连接依赖上一层完成功能

u  结论

l  依赖倒置原则是实现面向对象所声称的 诸多优点的一个重要原则

l  这个原则的正确应用对于创建可重用框架是必需的(确保高层可重用无低层依赖)

l  对于构建具有高弹性的代码同样是至关 重要的,当抽象和具体细节被分离以后, 代码的维护工作就变得容易多了

n  接口隔离原则ISP

u  接口不应该添加不必要的职责,应该分开污染接口,使用一个功能时不需要调用接口里其他无用的内容。使用多个专门的接口比使用单一的总接口要好。一个类对另外一个类的依赖性应当是建立在最小 的接口上的

u  接口污染

l  n 接口污染就是为接口添加了不必要的职责

n  p 在接口中加一个新方法只是为了减少类的数目

n  p 持续这样做,接口就被不断污染,变胖

l  n 实际上,类的数目不是问题,

l  n 接口污染会带来维护和重用方面的问题

l  n 最常见的问题是

n  p 为了重用被污染的接口,被迫实现并维护不必要的方法。

u  分析

l  .(并非所有门需要计时)

l  (最初门接口和计时没有任何关系)

l  (计时无关的门需要实现nil timeout方法)

l  (要包含提timerclient头文件)

l  (门接口被污染)

u  使用多个专门的接口比使用单一的总接口要好

u  一个类对另外一个类的依赖性应当是建立在最小 的接口上的

u  ?

u  (胖接口不利于高内聚)

u  (适配器模式 、多继承)

u  方式   继承,委托,接口

u  ?

n  Srp单一职责原理

u  如果一个列包含多个职责,要修改其中一个职责,则可能会影响该类里其他职责的实现。

u  这个原则的核心含意是:

l  一个类应该有且仅有一个职责

l  一个类的职责是指引起该类变化的原因

l  并非极端为一个类只设定一个方法,而是一组方法只能有一个变化轴线

l  最好有一个概念统构该组方法

u  关注点分离   要求对每一个功能对应一个单独的任务,每一个功能都要在一个独立的模块中实现

u  分析

l  一个类(或者大到模块,小到方法)承担的职责越多,它被复用 的可能性越小。而且如果一个类承担的职责过多,就相当于将这 些职责耦合在一起,当其中一个职责变化时,可能会影响其他职 责的运作。

l  单一职责原则:是实现高内聚、低耦合的指导方针,在很多代码 重构手法中都能找到它的存在,它是最简单但又最难运用的原则 ,需要设计人员发现类的不同职责并将其分离,而发现类的多重 职责(单一职责的界定)需要设计人员具有较强的分析设计能力 和相关重构经验

u  Draw和area功能分离

u  ?

n  松耦合

u  功能耦合:子系统之间互相影响的程度

u  内聚性:内部互相依赖程度或 子系统内部相互合作的程度

u  高内聚、低耦合 ——设计松耦合的子系统, 实现每个子系统高内聚

u  松耦合——增加子系统间连接的灵活性

n  契约式设计

u  客户类和服务器类

l  ;

u  契约式设计c-s关系(客户端和服务端)

l  v 具有形式上的一致性和精确的定义

l  v 什么情况下执行哪种正确的程序流程

u  断言(方法的契约和类的契约)

l   前置条件:对调用者提出要求,被调用者的权益

l   后置条件:被调用者的责任,调用者的权益

l   不变式:与类有关的断言,对所有操作有效

u  ?

n  小结

u  对于面向对象的软件系统设计来说,在支持可维护性的同时,需 要提高系统的可复用性

u  软件的复用可以提高软件的开发效率,提高软件质量,节约开发 成本,恰当的复用还可以改善系统的可维护性

u  单一职责原则rsp   要求在软件系统中,一个类只负责一个功能领域中 的相应职责

u  开闭原则ocp   要求一个软件实体应当对扩展开放,对修改关闭,即在 不修改源代码的基础上扩展一个系统的行为。

u  里氏代换原则lsp   可以通俗表述为在软件中如果能够使用基类对象, 那么一定能够使用其子类对象

u  依赖倒转原则dip    要求抽象不应该依赖于细节,细节应该依赖于 抽象;要针对接口编程,不要针对实现编程。

u  接口隔离原则isp   要求客户端不应该依赖那些它不需要的接口, 即将一些大的接口细化成一些小的接口供客户端使用。

软件架构

l  软件架构

n  软件架构的概念

n  软件架构的质量

n  参考架构、架构模式和设计模式

n  软件架构概念的任务和前景

n  软件架构师对项目的意义

n   

n  引言

u  软件架构涉及设计模式和架构模式

u  模式:对系统的组件进行标准化组合

l   可理解性

l   简单化

l   可扩充性

u  架构是对结构的作用和系统的行为进行建模,主要负 责系统的非功能性质量

u  架构和功能是完全不同的概念,功能在架构下运行

u  软件架构是系统设计阶段的初步结果,连接系统的需求和技术上的解决方案

l  架构在需求程序中间   需求分析-架构-程序

u  o 架构是抽象化实现系统的“蓝图”

l  降低复杂度

l  规定系统的结构和实施流程

u  软件架构

l  高层次对编程进行规定

l  编程经验应用到构架优化

l  发现合适架构过程循环往复

n  软件架构概念

u  系统架构包括:

l   把系统分解成物理上的组成部分(组件)

l   分布式系统中每个组件分配到计算机(部署)

l   描述所有组件动态的相互作用

l   描述架构的策略,即架构的静态和动态的功能

u   目标是使系统能够生成所有要求的功能

u   分解是静态的,涉及结构,相互作用是动态的

u   系统的功能以结构为基础,不能与之分离

u   设计阶段—必须组件,通过接口合作

l  组件具有高内聚性,相互间低耦合性

n  软件架构的资料费

n  参考架构,架构模式和设计模式

u  参考架构

l  包含可以服用的部分

l  为了完成一定业务目标而应用的软件架构的部分内容

u  架构模式

l   二者都是软件结构的模板

l   但不包含涉及解决业务领域问题部分

u  设计模式

l   体现了系统中可扩展的类具有特殊的作用

l   通过他们的合作可以解决相关的问题

n  三者之间的关系

u   都可以包含组件

u   参考架构可以包含各种结构模式和业务组件

l   以参考架构为基础的系统不需要进行全部系统设计

l   组件可以复用

l   具有一定扩展性

l   缺点:不一定完全适用,可能不完全符合要求

u  架构模式的目标:

l   所关注的系统按一定策略分解为可扩展的子系统

l   可扩展子系统之间的合作

u  架构模式可以包含多个设计模式,参考模式内容更广泛, 可以包含不同架构模式,还可以包括独立开发部分

n  软件架构概念的任务和前景

u  系统分为多种类型:硬实时系统、软实时系统、非实时系统

u   

u  系统中的分析任务

l   明确系统的目的和主要任务

l   系统应用流程符合客户的组织结构和流程结构

l   组织系统接口,可联系外部系统、使用者

l   解决系统接口与外部环境交流上的技术问题

l   在逻辑层上确定系统的结构和流程

u  系统中的结构设计任务

l  整体系统的硬件架构

l   

l   分布式系统:不同功能分配到不同计算机

n   软件在每台单机上的层次结构(静态)

n   系统整体架构策略(系统的工作流程,动态)

n   如何测试

n   得到什么(准备的范围)

l  单机软件:系统整体方案®单机软件设计

n   计算机的外部接口

n   系统划分为组件,结构组织(接口及组件间静态关系)

n   对流程进行建模和转化(工作内容),注意组件的功能和协作流程(组件间的动态关系)

u  观察软件架构的角度

l   各利益相关者的角度

l   分析系统所处的环境(黑盒观点)

l   分析系统的逻辑模型(白盒观点)

l   系统开发进程

l   业务系统的组建和系统管理员方案的建设

l   确定各种方案的依据文

u  软件架构的原型

l   水平原型

l   垂直原型

l   可行性原型

n  软件架构师的意义

u  协调委托人、管理人员和开发团队,引导程序员,团队纪 律,要求分析能力、设计能力、沟通能力、方案评估能力

l   技术能力

l   沟通能力

l   构造软件架构过程中的决定(分析能力,方案评估能力)

软件设计的模式

l  软件设计的模式

n  模式的使用

n  模式的属性和设计

n  架构模式、设计模式、惯用法的界限

n  设计模式、架构模式模板

l  模式促进软件开发的发展

n   不依靠任何平台

n   不限制于固定编程语言

l  模式是一个抽象类或接口的符号

n   真实类是派生类,模式不知道,由用户定义

n   编译时或运行时替代模式中的抽象类

l  模式的使用

n  软件设计中模式的主要目标

u   通过再次使用已获得的经验

u   提高架构的灵活性和可扩展性

n  委托原则,委托一个用于接収信息的对象,消 息继续传递给(优先使用组合/委托)

u  接受信息对象说狙击的部分对象

u  或运行时实现的一个抽象(接口/抽象类)的对象

n  模式

u   提高了系统的扩展性

u   降低了系统的性能

u   增加了架构的复杂度

l  模式的属性和设计

n  可理解性:通过简易性达到,提供结构化的全面文档

n  可扩展性:

u   静态继承

u   使用聚合接口或者抽象类(针对接口/抽象编程)

n  面向对象模式遵循的设计原则

u   松耦合系统

u   抽象

u   信息隐藏

u   明确的职责

u   依赖倒置原则

l  结构模式、设计模式、惯用法的界限

n  区别就是抽象的程度

u   架构模式关注系统架构

u   系统模式通常在子系统中解决特定的问题

u   设计模式原则上不影响系统的架构

u   惯用法是特定程序语言中的模式

n  架构模式、设计模式的关系

u  系统的架构可以包含多个架构模式

l   分层架构

l   MVC结构

u  架构模式可以包含多个设计模式

l   不是必要条件

l   MVC包含观察者模式、组合模式、策略模式

u  设计模式:经过验证的适用原则,微架构

l   其结构和机制决定把子系统拆分成各部分并确定相互间的合作

l   OO开发中,类的作用就是通过相互合作解决一定的问题

u  设计模式描述的是细致的模式,架构模式是粗略的模式

n  描述模式的模板

u  1.名称/别名

u  2.问题

u  3.解决方法

l  • 参与者

l  • 类图(金台关系)

l  • 动态关系

l  • 程序举例

u  4.评价(优缺点)

u  5.适用范围

u  6.类似的模式(总结一下)

设计模式类型

l  模式的分类

l  模式的概述

l  各种设计模式

l  ,

l  什么是设计模式?

n  设计模式:一定是针对有一定复杂度的软件系统;

n  设计模式:描述复杂软件系统中普遍存在、重复出现 的结构、模式、套路,用来解决在一定的场景下,具 有一般性的设计问题

n  设计模式:这种结构设计的好坏、直接影响到软件的: 可读性、可维护性、可扩展性、效率;

n  OOP程序设计:是软件设计中的“初等数学”,是工 具;像:加、减、乘、除

n  设计模式:软件系统(特别是复杂软件系统)中的 “高等数学”。

n  一个复杂问题:我们首先要研究采用什么数学模型、 数学方法,然后才用基本的初等数学(加、减、乘、 除)去实现

n  ,

n  起源

u  。。。。。。。

n  目的

u  、、、、、

n  核心思想

u  广义——软件设计模式是可解决一类软件问题并能重 复使用的软件设计方案

u  狭义——设计模式是对被用来在特定场景下解决一般 设计问题的类和相互通信的对象的描述

l  是在类和对象的层次描述的可重复使用的软件设计问题的解 决方案

u  核心思想

l  增加抽象层

l  分离出变化部分(从不变部分中)

n  分类

u  按目的分类

l   创建型:与对象的产生相关

l   结构型:处理类或对象的组合

l   行为型:对象之间如何交互,怎样分配职责。

u  按范围分类

l   类模式:类之间的静态继承、编译时已经确定

l   对象模式:也用继承,但对象的类型在运行时可以发生变化,通过接口/抽象基类实现。

u  结合上述两种标准,将设计模式划分为6类:

l   创建型类模式

n   将对象的创建工作延迟到子类

l   创建型对象模式

n   将对象的创建工作延迟到另一个对象中

l   结构型类模式

n   使用继承机制组合类

l   结构型对象模式

n   描述了对象的组装方式

l   行为型类模式

n   使用继承描述算法或控制流

l   行为型对象模式

n   对象之间如何协作来完成单个对象无法完成的任务

n  设计模式怎样解决设计问题

u  o 寻找合适的对象

l  n OOA

n  o 通过用例描述寻找领域对象

l  n OOD

n   OOD最困难的部分是将系统分解成对象集合

n   通过用例设计发现抽象对象

n   所谓抽象,是指与领域对象比较而言,如算法类

n   设计模式帮你确定并不明显的抽象和描述这些抽象的对象

u  o 决定对象的粒度

l   许多模式涉及到对象的分解问题

l   怎样决定一个对象应该是什么

u  设计模式很好地讲述了这个问题

l   Facade描述了怎样用对象表示完整的子系统

l   Flyweight描述了如何支持大量的最小粒度的对象

l   …

u  o 指定对象的接口

l   类型是用来标示特定接口的一个名字

l   对象是封闭的,只有通过接口才能与外界交流

l   动态绑定:运行时刻才受你的具体的实现的约束

l   设计模式通过确定接口的主要组成部分来帮助定义接口

l   设计模式也指定了接口之间的关系

u  o 描述对象的实现

l  类继承与接口继承的比较

类(class)与类型(type)

n   类定义了对象的实现——具体类

n   类型用来标示接口——接口或抽象类

l  针对接口编程,而不是针对实现编程

n  o 客户类不用知道他们使用的对象是何种具体类, 只需知道是何种类型,降低了相互间的耦合度

l  不将变量声明为某个具体类的实例对象

n  遵从抽象类定义的接口,设计模式中的常用办法。

u  o 运用复用机制

继承和组合的比较

l   继承——白箱复用

l   组合——黑箱复用

n  允许你在运行时刻改变被组合的行为

l   委托(代理)

n   对象组合的特例

l   继承和参数化类型的比较

n   模板类:允许在定义一个类型时并不指定该类型所用到的其它类型。

u  允许你改变类所用到的类型

u  o 运行时刻和编译时刻的结构

l  运行时刻和编译时刻的结构

n  差别很大

u   编译时刻:继承关系固定的类组成

u   运行时刻:由快速变化的对象网络组成

l  聚合和关联的比较

n   表示区别

n   实现方法相同

n   聚合还是相识是由意图而不是语言机制决定

u  o 设计应支持变化

l  导致重新设计的常见原因

n   显式地创建一个对象;

n   对特殊操作的依赖;

n   对硬件和软件平台的依赖;

n   对对象表示或实现的依赖;

n   算法依赖;

n   紧耦合

n   通过生成子类来扩充功能;

n   不能方便地对类进行修改

l  设计模式在开发3类主要应用中的作用

n  应用程序

u   优先考虑内部复用性、可维护性和可扩充性

u   设计模式通过减少依赖性来提高内部复用性。

u   通过显示怎样扩展类层次结构和使用对象复合,增强可扩充性。

n  工具箱

u   是一组相关的、可复用的类的集合,提供通用的功能。

u   避免假设和依赖

n  框架

u   规定了应用的体系结构。

u   松散耦合更重要

n  设计模式和框架的区别

u   设计模式比框架更抽象

u   设计模式是比框架更小的体系结构元素

u   框架比设计模式更加特例化

n  设计模式的概述

u  o 结构模式

l  适配器模式、桥梁模式、装饰模式、外观模式 组合模式、代理模式等

u  o 行为模式

l  模板方法模式、命令模式、观察者模式、策略 模式、状态模式、访问者模式、迭代器模式等

u  o 创建型模式

l  工厂方法、抽象工厂、单例模式

设计模式-结构型

l  结构型模式

n  n 如何将类或对象结合在一起形成更大的结构

n  n 就像搭积木,可以通过简单积木的组合形成 复杂的、功能更为强大的结构

n  结构型模式分类

u  类结构型模式和对象结构型模式

l  类结构型模式关心类的组合,由多个类可以 组合成一个更大的系统,在类结构型模式中 一般只存在继承关系和实现关系

l  对象结构型模式关心类与对象的组合,通过 关联关系使得在一个类中定义另一个类的实 例对象,然后通过该对象调用其方法。根据 “合成复用原则”,在系统中尽量使用关联 关系来替代继承关系,因此大部分结构型模 式都是对象结构型模式

l  适配器模式

n  Adapter(适配器)模式

n  由来

u  一个team要为外界提供S类服务,但team里面没有能够 完成此项任务的member,只有team外的A可以完成这项 服务。为保证对外服务类别的一致性(提供S服务)

l   n 将A招安到team内,负责提供S类服务

l  n A不准备接受招安,可安排B去完成这项任务,并让B做好A的 工作,让B工作的时候向A请教,此时,B是一个复合体(提供 S服务,是A的继承弟子)

u  将一个类的接口,转换成客户期望的另一个接口,适配 器让原本接口不兼容的类可以一起工作

u  别名:包装器 Wrapper(后面还有一个一样的)

n  意图:

u  将一个类的接口转换成客户希望的另一个接口

n  动机:

u  n 现有类可满足客户类的功能需要,但接口不匹配

n  适用场合:

u  n 使用一个已存在的类,而它的接口不符合要求

u  创建一个可以复用的类,该类可以与其他不相关的类或不可预 见的类(即那些接口可能不一定兼容的类)协同工作

u  使用一些已经存在的子类,但不可能通过子类化以匹配各自接 口。对象适配器可以适配它的父类接口

n  结构  类适配器

u  用一个具体的Adapter类对Adaptee和Target进行匹配,Adapter类多重继承 Adaptee和Target类

u  Adapter可重定义Adaptee的部分行为,因为Adapter是Adaptee的一个子类

n  示例  类适配器

n  结构——对象适配器

u  , 允许一个Adapter与多个Adaptee同时工作,即Adaptee本身以及它 的所有子类(如果有子类的话)同时工作。Adapter可以一次给所有 的Adaptee添加功能

u  使用组合,不仅可以适配某个类,也可以适配该类的任何子类

n  参与者

u   Client

l   特定领域相关,与符合Target接口的对象协同

u   Target

l   定义Client使用的与特定领域相关的接口

u   Adaptee

l   定义一个已经存在的接口,需要适配的接口类

u   Adapter

l   负责Adaptee的接口与Target接口进行适配

n  效果

u  总体

l   将目标类和适配者类解耦

l   增加了类的透明性和复用性

l   灵活性和扩展性好

u  类适配器模式(继承)

l  属于静态结构 ,由于只能单继承

u  对象适配器(委托)

l   可以把多个不同的适配者适配到同一个目标

l   实现过程较为复杂

l  4.4 BRIDGE(桥梁)—对象结构型模式

n  别名:Handle / Body

n  ,

n  由来

u  例子:相同模块的跨平台使用

l   设计模块A和B

l   希望模块A和B能应用在X操作系统上,让A和B继承X操作系统的接口

l   希望模块A和B能应用在Y操作系统上,让A和B继承Y操作系统的接口,以此类推……

u  问题  模块A和B缺乏复用性

u  解决

l   抽象出平台无关接口,

l   抽象和实现部分分别放在独立的类层次结构中

l   通过桥接两个抽象模块,消除模块间的继承耦合

l                  

u  传统地,当一个抽象可能有多个实现时, 通常用继承来协调它们

l   抽象类定义该抽象的接口,

l   具体的子类则用不同的方式加以实现。

u  但是此方法有时不够灵活

l  继承机制将抽象部分与它的实现部分绑定

l  难以对抽象部分和实现部分独立地进行修改、扩充和重用

n  意图

u   桥接模式将抽象部分与实现部分分离

u   使它可以独立的变化

n  适用的情况

u   不希望在抽象和它的实现部分之间有一个固定的绑定关系

u   类的抽象以及它的实现都应该可以通过生成子类的方法加以扩充

u   实现部分的修改应对客户不产生影响

u   想对客户完全隐藏抽象的实现部分

n  参与者

u   Abstraction

l   定义抽象类的接口

l   维护一个指向Implementor类型对象的指针

u   RefinedAbstraction

l   扩充由Abstraction定义的接口

u   Implementor

l   定义实现类的接口,不一定要与Abstraction的接口完全一致,甚至可以完全不同

u   ConcreteImplementor

l   实现Implementor接口并定义它的具体实现

l   协作:Abstraction将Client的请求转给Implementor对象

n  效果

u  分离接口及其实现部分

l   抽象类的实现可以在运行时刻进行配置

l   一个对象甚至可以在运行时刻改变它的实现

u  提高可扩充性

l  抽象与实现两部分可以单独扩充

u  实现细节对客户透明

l  可以对客户隐藏实现细节

n  桥梁模式实例

n  意义

u  脱耦:抽象和实现之间的耦合解开,强关联改成弱关联

l  强关联是指在编译时期已经确定的,无法在运行时 期动态改变的关联

l  弱关联是可以动态确定并可在运行时刻动态改变的 关联

u  继承关系是强关联,聚合关系是弱关联

l   将两个角色之间的继承关系修改为聚合关系 就是将他们之间的强关联变换成弱关联。

l   桥接模式中的所谓脱耦就是指在一个软件系统的抽象和实现之间使用组合/聚合关系而不是继承关系

l   从而可以使两者可以相对独立的变化

l  4.5 DECORATOR(装饰)——对象结构型模式

n  由来

u  动态给对象添加额外职责。比如:

l   画可以挂在墙上,可加画框,画是被装饰者

l   可被蒙上玻璃,装进画框,玻璃画框就是装饰

l   装饰模式就像穿衣服

u  装饰模式以对客户透明的方式动态地给一个 对象附加上更多的责任

u  不改变接口,但加入责任

l   Decorator提供了一种给类增加职责的方法

l   不是通过继承,而是通过组合实现

n  意图

u   动态地给一个对象添加一些额外的职责

u   就增加功能来说,装饰模式比子类更为灵活

n  别名:包装器(Wrapper)(好像有一个别名一样的)

n  动机

u   希望给某个对象而不是整个类添加一些功能

u   继承机制一种有效途径,但不够灵活

u   较为灵活的方式是将组件嵌入另一个对象中

u   嵌入的对象为装饰,与所装饰的组件接口一致

n  适用场合

u  在不影响其他对象的情况下,以动态、透明 的方式给单个对象添加职责

u  处理那些可以撤消的职责

u  当不能采用生成子类的方法进行扩充时。

l  一种情况是,可能有大量独立扩展,每一种组 合将产生大量的子类,子类数目呈爆炸性增长

l  另一种情况是因为类定义被隐藏,或类定义不能用于生成子类

n  结构、角色和协作

n  效果

u  o 比静态继承更灵活

u  o 避免在层次结构高层的类有太多的特征

l   提供了一种“即用即付”的方法来添加职责

l   应用程序不必为不需要的特征付出代价

u  Decorator与Component不一样

l   Decorator是一个透明的包装

l   使用装饰时不应该依赖对象标识

u  有许多小对象,学习和debug困难

n  对比适配器模式和装饰模式

l  4.6 FACADE(外观)——对象结构型模式

n  由来

n  设计原则:迪米特法则

u  – 软件设计师就是软件系统的统治者

u  – 应当使得软件的不同对象彼此之间尽量“老死不相往来”,降低系统维护成本

n  意图

u   提供了一个统一的接口,用来访问子系统中的一群接口

u   Facade定义了一个高层接口,让子系统更容易使用

u   

n  动机

u   将一个系统划分成为若干个子系统有利于降低系统的复杂性

u   设计目标是使子系统间的通信和相互依赖关系达到最小

u   途径之一是就是引入一个 外观对象,

u   它为子系统中较一般的设施提供了一个单一而简单的界面

n  适用性

u  为复杂子系统提供一个简单接口,定义一个 高层接口,使子系统更加容易使用

u  解除客户程序与抽象类具体实现部分的依赖 性,提高子系统的独立性和可移植性

u  当需要构建层次结构的子系统时,使用 Facade模式定义每层的入口点。如果子系 统间相互依赖,他们仅通过Facade进行通 讯,简化了它们之间的依赖关系

n  结构和参与者

u  门面角色 (Facade )

l  —知道哪些子系统类负责处理请求

l  —并将客户的请求代理给适当的子系统对象

u  子系统角色 (Subsystem )

l  实现子系统的功能

l  处理由Facade 对象指派的任务

l  没有Facade的任何相关信息

n  效果

u  p对客户端屏蔽子系统组件,减少客户端使用 对象数目

u  p实现了子系统与客户之间松耦合的关系,使 得子系统组件的变化不会影响到客户

u  p不限制客户应用子系统类

n  示例

n  优缺点

u   优点

l   子系统的类不识别外观

l   使用子系统的功能更简单

l   客户类和子系统实现松耦合

u   缺点

l   增加了附加方法调用

l   子系统中类的功能受到外观的限制

l   封装组件接口改变导致外观跟着改变

l   依然允许直接调用

l  4.7 COMPOSITE( 组合)——对象结构型模式

n  由来

u  组合模式有时候又叫做部分-整体模式

u  在树型结构的问题中

l   模糊了简单元素和复杂元素的概念,

l   客户程序可以向处理简单元素一样来处理复杂元素

u  使得客户程序与复杂元素的内部结构解耦

u  抽象

l  单一对象(Leaf):键盘、鼠标、显示器、硬盘、电 源、CPU、显卡、网卡

l  组合对象(Composite):计算机、机箱、主板

l  部件(Component):单一对象与组合对象的统称

u  组合对象既可以包括单一对象,也可以包括 组合对象

n  意图

u   将对象组合成树形结构——“部分/整体”层次结构

u   使用户对单一对象和组合对象使用具有一致性接口

n  动机

u   容器对象和叶子对象进行递归组合

u   用户可以一致地对待容器对象和叶子对象

n  适用情形

u   表示对象的部分-整体层次结构

u   忽略总体对象与单一对象的不同

u   用户将统一使用组合结构的所有对象

n  结构

n  核心实现代码

n  参与者

u   Component

l   为组合对象声明接口

l   声明一个接口用于访问和管理Component的子组件

u   Leaf

l   表示叶节点对象,没有子节点

l   定义图元对象的行为

u   Composite

l   定义复合部件的行为

l   存储子部件

u   Client

l   通过Component接口操纵组合部件的对象

n  效果

u  定义了包含基本对象和组合对象的类层次结构

u  简化客户代码,一致使用组合对象和单个对象

u  容易增加新类型的组件

u  用户使用Component类接口与组合结构中的 对象进行交互

l   如果接收者是叶节点,直接处理请求;

l   如果接收者是Composite,将请求发给它的子部件

l   在转发请求之前和/或之后可能执行一些辅助操作

n  实现

u  o 显式的父对象的引用

l   在子对象中给出父对象的引用

l   可以很容易地遍历所有父对象

u  o 最大化Component接口(透明使用)

u  o 声明管理子部件的操作(安全性与透明性)

u  o 存储组件的数据结构(链表,树,Hash)

n  类似的模型

u  类图结构上

l   类似装饰模式

l   一定程度上装饰模式是组合模式的一个特例

l   装饰对象只能聚集一个对象

u  区别

l   装饰模式动态构建功能

l   组合模式把对象组装为一个层次结构

l  4.8 PROXY( 代理)——对象结构型模式

n  由来

u  o 客户端不能直接操作到某个对象,但又必须和那 个对象有所互动

l  如果对象是一个大图片,需要花费很长时间才能显示 出来,此时需要做个图片Proxy来代替真正的图片

l  如果对象在某远端服务器上,直接操作这个对象因为 网络速度原因可能比较慢,那我们可以先用Proxy 来代替那个对象

u  如何应对这种变化?如何提供一种机制让原本 交互起来比较困难的两个对象实现畅通无阻地 交流呢?如何保持系统的结构不随着需求改变 而轻易改变?这就是代理模式

n  意图

u  为其他对象提供一种代理以控制对这个对象的访问

n  适用性

u  远程(Remote)代理:为一个对象在不同地址空 间提供局部代表

u  虚拟(Virtual)代理:在需要创建开销很大对象时 缓存对象信息

u  保护(Protection)代理:控制对原始对象的访问

u  智能引用(Smart Reference)代理:当一个对象 被引用时,提供一些额外的操作,例如记录访问的 流量和次数等

n  结构、参与者和协作

n  效果

u  Protect Proxy

l   控制对象的访问,可给不同用户提供不同级别的使用权限

l   可以在运行时对用户权限进行检查

u  Cache Proxy

l  为目标操作提供临时存储空间,以便多客户端共享

u  Firewall Proxy

l  保护目标,防止恶意用户接近

u  Synchronization Proxy

l  使多用户能同时使用一个对象而没有冲突

n  类似的模式

u  o 代理模式、装饰模式、适配器模式

l   都引入一个新的对象,含指向实际对象的引用

l   请求和命令委托给实际对象执行

u  区别

l   代理使用实际对象相同接口,适配器转换接口

l   装饰模式目的是动态添加功能,代理模式也可

l   代理模式一个任务“门卫”

l   装饰模式动态扩展实际对象的功能

l  补充:FLYWEIGHT( 享元)——对象结构型模式

n  由来

u  用Car类创建若干个同型号的轿车

l   要求这些轿车的height, width, length值相同

l   color, power可以是不同

n  意图

u   运用共享技术有效地支持大量细粒度的对象

u   避免大量拥有相同内容的小类的开销

u   使大家共享一个对象

n  动机

u   OO技术很好地解决一些灵活性或可扩展性问题

u   某些情况对象数量太多,导致运行时代价巨大

u   Flyweight是一个共享对象

u   享元模式通过共享技术实现相同/相似对象重用

n  关键

u   关键概念是内部状态和外部状态

u   内部状态存储于Flyweight,包含独立于场景的信息(共享)

u   外部状态取决于 Flyweight场景,根据场景而变化(非共享)

n  适用性

u   系统中有大量的相似的对象,这些对象耗费大量的内存。

u   细粒度的对象都具备较接近的外部状态,而内部状态与环境无关,即对象没有特定身份

u   需要缓冲池的场景

n  结构

n  协作

u  Flyweight执行时所需的状态必定是内部或外部的

l   内部状态存储在ConcreteFlyweight中,

l   外部对象则由Client对象存储或计算。当用户调用flyweight对象操作时,将该状态传递给它

u  用户不应直接对ConcreteFlyweight类进行实例化

l   只能从FlyweightFactory对象得到ConcreteFlyweight对象

l   以保证对它们适当地进行共享

n  效果

u  Flyweight模式的核心

l   把大量共享的对象收集在一起

l   使用简单工厂模式进行管理

l   避免由大量的小对象导致系统的内存过渡消耗

u  当重复对象较多时

l   Flyweight模式具有较好的空间性能

l   但在查找搜索上消耗了时间复杂度

l  小结

n  代理模式(Proxy)为其他对象提供一种代理以控制对该对象的访问

n  装饰模式(Decorator)动态地给一个对象添加一些额外的职责,就 增加功能来说,装饰模式比生成子类更为灵活

n  适配器模式(Adapter)将一个类的接口变换成客户端所期待的另一 接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一 起工作

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

n  桥梁模式(Bridge)也叫桥接模式,将抽象和实现解耦,使得两者 可以独立的变化

n  外观模式(Facade)也叫门面模式,要求一个子系统的外部与其内 部的通信必须通过一个统一的对象进行,外观模式提供一个高层次的 接口,使得子系统更易于使用

n  享元模式(Flyweight)是池技术的重要实现方式,使用共享对象可 有效地支持大量的细粒度的对象

设计模式-行为型

行为型模式概述

l  行为型模式概述

n  行为型模式(Behavioral Pattern)是对不同对象之间 划分责任和算法的抽象化

n  行为型模式不仅描述类和对象的结构,而且重点关注 它们之间的交互和通信

n  刻划运行时复杂的控制流,将注意力从控制流转到对 象间的联系方式上

n  通过行为型模式,可以更加清晰地划分类与对象的职 责,并研究系统在运行时实例对象之间的交互

n  系统运行时,对象并不是孤立的,它们可以通过相互 通信与协作完成某些复杂功能

l  分类

n  类行为型模式和对象行为型模式

n  类行为型模式:

u   使用继承机制在类间分派行为

u   通过多态等方式来分配父类与子类的职责

n  对象行为型模式:

u   使用对象复合而不是继承分配行为

u   通过对象关联等方式来分配多个类的职责

n  “合成复用原则”

u   系统中要尽量使用关联关系来取代继承关系

u   大部分行为型设计模式都属于对象行为型设计模式

TEMPLATE METHOD(模板方法) ——类行为型模式

l  由来

n  行为型设计模式关注的焦点之一就是类对象的职责分配

n  模板方法实现父类和子类对象之间职责的划分

n  本科生培养,教育部制定纲领性政策

u   军训->学文化课->做毕业设计->毕业

u   教育部的工作完成

u   各学校负责具体环节

n  引入到软件设计中

u   使用继承机制使得父类和子类之间达到分工合作的目的

u   基类中确定算法框架(不变),子类实现具体工作(可变)

l  意图

n   定义一个操作中的算法的骨架,而将一些步骤延迟到子类中

n   子类不需要改变算法结构即可重定义算法的某些步骤

l  动机

n   将相同的代码放在父类中,

n   不同的方法实现放在不同的子类中

l  适用情形

n   具有统一的操作步骤或操作过程

n   具有不同的操作细节

n   即存在多个具有同样操作步骤的应用场景,但某些具体的操作细节却各不相同

l  结构和参与者

l  效果

n  模板方法是一种代码复用技术,提取了子类的公共行为

n  模板方法导致一种反向的控制结构(依赖倒置原则)

u   “你别来找我,让我去找你”——好莱坞法则

u   即:一个父类调用子类的操作,而不是相反

n  通过在抽象模板定义模板方法给出成熟算法步骤,同时 不限制步骤细节,具体算法实现细节不改变算法骨架

n  抽象模板模式中,可以通过钩子方法对某些步骤进行挂 钩,具体模板通过钩子可以选择算法骨架中的某些步骤

l  模板方法模式实例

COMMAND(命令) ——对象行为型模式  

l  由来

n  简单的文本编辑器,没有使用命令模式的实现

u  Application包含一个编辑菜单Menu,Menu中又包含了若干菜 单项MenuItem,如Copy和Paste

u  增加Redo和Undo功能,命令发出者MenuItem直接调用接收 者Document的方法,无相关数据结构,命令的操作难以保存

n  解决办法:命令抽象成一个对象,利用备忘录模式对命 令进行保存,实现Redo和Undo操作

l  o 意图

n   将请求封装为对象,用不同请求对客户参数化

n   对请求排队或记录请求日志,支持可取消的操作

l  动机

n   软件设计中,经常需要向某些对象发送请求

n   但不知道请求的接收者,也不知道被请求的操作

n   只需在程序运行时指定具体的请求接收者即可

n   使用命令模式,请求发送者与请求接收者解耦

l  适用性

n  系统需要将请求调用者和请求接收者解耦, 使得调用者和接收者不直接交互

n  系统需要在不同的时间指定请求、将请求排 队和执行请求

n  系统需要支持命令的撤销(Undo)操作和恢 复(Redo)操作

n  系统需要将一组操作组合起来,即支持宏命

l  结构和参与者

l  协作

n  效果

u  请求者不直接与接收者交互,即请求者不包 接收者的引用,彻底消除了彼此之间的耦合

u  Command对象可像其他的对象一样被操纵 和扩展

u  可将多个命令装配成一个复合命令,复合命 令是Composite模式的一个实例。

u  满足OCP,增加新的Command很容易,因 为这无需改变已有的类

OBSERVER(观察者)——对象行为型模式

l  由来

n  OO开发的一个主导原则是给对象正确地分配职责

n  一个对象只应做一件事,而且要将它做好

n  提高重用性和可维护性

n  正确划分任务重要的领域是用户 界面和基础业务逻辑间的交互

n  更改用户界面不会对应用程序的 其他部分产生连带影响

n  业务要求也可能会发生变化 这一切与用户界面无关

n  两组要求都会发生变化

n  自GUI 出现以后开发的很多面向对象的框架均支持将用 户界面从应用程序的其他部分中划分出来

n  将系统分割成一系列相互协作的类有也存在一些不足

u   需要维护相关对象间的一致性

u   为维持一致性而使各类紧密耦合,可能降低其可重用性

n  没有理由限定依赖于某数据对象的对象数目, 对相同 的数据对象可以有任意数目的不同用户界面

l  意图

n  定义对象间的一种一对多的依赖关系

n  当一个对象的状态发生改变时,所有依赖于它的对象都得到通 知并被自动更新

l  动机

n  建立一种对象与对象之间的依赖关系

n  一个对象发生改变时将自动通知其他对象

n  其他对象将相应做出反应

l  两个概念,观察者和被观察者

n   一个对象可以被多个“观察者”所观察

n   “被观察者”与“观察者”是一个一对多的关系

n   “被观察者”中会有一个“观察者”的集合,注册“观察者”

l  适用性

n  当一个抽象模型有两个方面, 一方面依赖于另一 方面。将二者封装在独立对象中以使它们可以 独立被改变和复用

n  一个对象的改变需要同时改变其它对象, 但不知 道具体有多少对象有待改变

n  当一个对象必须通知其它对象,而它又不能假 定其它对象是谁。换言之, 不希望这些对象紧密 耦合

l  效果

n  Observer模式允许你独立的改变目标和观察者

n  优点

u  Subject和Observer之间松偶合,可以各自独立改变

u  Subject在发送广播通知时,无须指定具体的Observer,

u  Observer可以自己决定是否要订阅Subject的通知

u  高内聚、低偶合

n  缺点

u  松偶合导致代码关系不明显,有时可能难以理解

u  一个Subject被大量订阅,广播通知时可能会有效率问题

l  ,

STRATEGY(策略)——对象行为型模式

l  由来

n  对于平面上的点坐标数组进行排序,根据不同的应用场 合可能有三种算法:

u   按照点的X坐标大小排序

u   按照点的Y坐标大小排序

u   按照点到原点的距离大小排序

n  o 首先想到的方案

u   用三个函数分别实现这三种算法

u   条件判断语句根据情形选择其中一个算法实现

n  o 算法硬编码进使用它们的类,缺点

u   使用算法的类变得复杂而难于维护

u   当需要支持多种算法且每种算法都很复杂时问题会更加严重

l  意图

n   定义一系列算法,并进行封装,并使其可相互替换

n   策略模式使得算法可独立于使用它的客户而变化

l  动机

n   多种不同的方式完成一项任务,每种方式为一个策略

n   根据环境条件的不同选择不同的策略来完成该项任务

n   软件开发中也常常遇到实现某一个功能有多个途径情况

l  适用场合

n  许多相关类仅仅行为有异。“策略”提供了一种用多个行为中 的一个行为来配置一个类的方法

n  需要使用一个算法的不同变体

n  代替这些条件语句

l  结构与参与者

l  评价

n  相关算法系列

u  Strategy类层次为Context定义了一系列可供重用的算法

u   环境类依赖接口,不依赖具体实现(DIP)

n  o 一个替代继承的方法

u   继承提供了另一种支持多种算法或行为的方法

u   算法和使用算法的对象相互分离,客户程序可以在运行时动态选择算法,代码复用性好,便于修改和维护

u   将算法封装在独立的Strategy类中,可以独立于其Context改变它,使它易于切换、易于理解、易于扩展

n  消除了一些条件语句

u   提供了用条件语句选择所需的行为以外的另一种选择

u   将行为封装在一个个独立的Strategy类中消除了这些条件语句

n  缺点

u   Client必须知道多个不同策略,以便配置

u   很多小程序

u   与常规环境类实现相比,信息流费用过高

l  相关模式

n  策略模式和装饰模式

u   策略实现相同功能,更换策略不改变总体功能

u   装饰模式动态增加(变更)功能

n  模板方法

u   父类静态定义算法基本框架

u   子类只允许改变算法中的步骤

n  状态模式

u   类图一致、实现方法一致

u   目的不同,各有各自的含义

MEDIATOR(中介者)——对象行为型模式

l  由来

n  系统存在如下问题

u  系统结构复杂:

l   对象之间存在大量的相互关联和调用,若有一个对象发生变化,

l   则需要跟踪和该对象关联的其他所有对象,并进行适当处理。

l

u  对象可重用性差:

l   由于一个对象和其他对象具有很强的关联,

l   若没有其他对象的支持,一个对象很难被另一个系统或模块重用,

l   这些对象表现出来更像一个不可分割的整体,职责较为混乱。

u  系统扩展性低:

l   增加一个新的对象需要在原有相关对象上增加引用,

l   增加新的引用关系也需要调整原有对象,

l   系统耦合度很高,对象操作很不灵活,扩展性差

l  意图

n   用一个中介对象来封装一系列的对象交互。

n   中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互

l  动机

n   OO软件设计与开发中,应该尽量将对象细化,使其只负责或呈现单一的职责(单一职责原则),对象种类、个数增多

n   一个模块由很多对象构成,对象之间可能存在相互的引用

n   为了减少对象两两之间复杂的引用关系

n   使之成为一个松耦合的系统,需要使用中介者模式(动机)

l  适用性

n  o 软件构建过程中

u   常出现多个对象互相关联交互的情况

u   对象之间常常会维持一种复杂的引用关系。

n  o 遇到需求更改,直接引用关系将面临不断的变化

u   可使用一个“中介对象”来管理对象之间的关联关系

u   避免相互交互的对象之间的紧耦合引用关系

u   从而更好地抵御变化

n  o 下列情况下使用中介者模式 :

u   一组对象以定义良好但是复杂的方式进行通信。产生的相互依赖关系结构混乱且难以理解

u   一个对象引用很多对象并直接通信, 导致难以复用该对象

u   想定制一个分布在多个类中的行为,而又不想生成太多的子类

l  结构与参与者

l  协作

n   同事向一个中介者对象发送和接收请求

n   中介者在同事间转发请求,实现协作行为

l  评价

n  减少了子类生成

u   Mediator将原本分布于多个对象间的行为集中在一起

u   改变这些行为只需生成Meditator的子类即可

u   这样各个Colleague类可被重用

n  各个Colleague松耦合

u   中介者模式将各Colleague对象解耦,各对象之间松耦合

u   可以独立改变和复用各Colleague类

n  简化了对象间关联

u  用Mediator和Colleague间一对多交互来代替各Colleague间多对多交互,一对多的关系更易于理解、维护和扩展

n  封装变化

u   中介者在封装交互的同时也将变化封装起来,交互关系的变化只影响Mediator类的代码,各Colleague类的代码无需修改

u   增加或删除Colleague对象也只需要修改Mediator类的代码。

u   封装变化简化了系统的维护工作

n  控制集中化

u   将交互的复杂性变为中介者的复杂性。

u   中介者封装了所有交互,可能变成一个难于维护的庞大对象

u   中介者只处理对象间交互,不负责对象能独立处理的部分

n  中介者难复用

u   中介者需要和每个Colleague对象交互,处理系统中所有交互

u   和系统的耦合度较大,难以在系统间复用

l   

STATE( 状态)——对象行为型模式

l  由来

n  一个对象有多种状态,在不同状态下,对象 可以有不同行为

l  意图

n   允许一个对象在其内部状态改变时改变它的行为

n   对象看起来似乎修改了它的类

l  动机

n   一个对象的行为取决于一个或多个动态变化的属性

n   该属性叫做状态,这个对象是有状态的

n   与外部事件交互,内部状态改变,其行为也随之发生变化

l  适用性

n  两种情况下均可使用State模式

u  对象行为取决于其状态

l   必须在运行时根据状态改变行为

u  一个操作中含有庞大的多分支条件语句

l   分支依赖于对象的状态

l   State模式将每一个条件分支放入一个独立类中

l   可将对象状态作为一个对象

l   该对象可不依赖于其他对象独立变化

l   维护这些独立的类就不再影响到系统的其他部分

l  结构和参与者

l  效果

n   使用State模式,动作完成后可以自动切换到下一个状 态,客户无须知道具体切换逻辑。每个ConcreteState 实例只需知道本状态后可能过渡到哪些状态及相应条件

n  实现了与状态相关的行为的局部化

n   使用状态模式将状态的切换逻辑放到State的派生类中, 动作的实现供派生类进行调用,实现逻辑和动作的解耦

l  示例

n   

VISITOR( 访问者)——对象行为型模式

l  由来

n   如何扩展一个现有的类层次结构来实现新行为?

u   一般的方法是给类添加新方法

u   万一新行为和现有对象模型不兼容?

n   类层次结构设计时无法预知未来将会需要哪些功能

n   如果已有类层次结构代码不允许修改,怎么扩展行为功能

l  意图

n  表示一个作用于某对象结构中的各元素的操作,可在不改变这些对象本身的情况下,定义作用于这些对象的新操作

l  动机

n   同一集合对象的操作不唯一,可能存在多种不同的操作方式

n   这些操作方式不稳定,需要增加新操作,以满足新的业务需求

n   访问者模式的目的是

u   封装一些施加于某种数据结构元素之上的操作,若这些操作需要修改

u   接受这个操作的数据结构可以保持不变。

u   为不同类型的元素提供多种访问操作方式,且可以在不修改原有系统的情况下增加新的操作方式,这就是访问者模式的模式动机

l  适用性

n  一个对象结构包含很多类对象,它们有不同的接口,想 对这些对象实施一些依赖于其具体类的操作

n  需要对一个对象结构中的对象进行很多不同的并且不相 关的操作,想避免让这些操作“污染”这些对象的类

n  当该对象结构被很多应用共享时,用Visitor模式让每个 应用仅包含需要用到的操作

n  定义对象结构的类很少改变,但经常需要在此结构上定 义新的操作

l  参与者

n   Visitor

u   为对象结构中的具体元素提供一个访问操作接口。该操作接口的名字和参数标识了要访问的具体元素角色。访问者可以通过该元素角色的特定接口直接访问它

n   ConcreteVisitor

u   实现Visitor接口的操作

n   Element

u   该接口定义一个accept操作接受具体的访问者

n   ConcreteElement

u   实现Element的accept操作

n   ObjectStructure

u   能枚举元素;可提供一个高层接口以允许访问者访问其元素;可以是一个复合(组合模式)或是一个集合

l  效果

n  优点

u  不修改具体元素类,就可以增加新操作。主要是 通过元素类的accept方法接受一个visitor对象 来实现的

n  缺点

u  不易频繁增加元素类,每增加一个元素类,就要 在Visitor接口中写一个针对该元素的方法,还要 修改Visitor子类

ITERATOR(迭代器)——对象行为型模式

l  由来

n  以集合为例

u   是一个管理和组织数据对象的数据结构

u   一个基本属性就是集合能够存储数据

u   包括存储数据类型;存储空间的大小和分配;存储方式和顺序

u   这些属性是集合对象与身俱来的,是其密不可分的职责

n  必须提供访问其内部数据的方式,即一种遍历机制

u  不同情形不同实现,如顺序、逆序遍历;二叉树前中后序遍历

n  集合对象拥有两个职责:

u  一是存储内部数据;二是遍历内部数据

u  前者为对象的根本属性,后者既可变化,又是可分离

u  可将遍历行为分离出来,抽象为一个迭代器,专门提供遍历集 合内部数据对象行为

l  意图

n  提供一种可顺序访问聚合对象中各个元素的方法

n  但不暴露该对象内部表示

l  动机

n  提供外部对象使用的迭代器,对聚合访问和遍历

n  迭代器定义了一个访问该聚合元素的接口

n  跟踪当前遍历的元素,了解元素遍历的状态

l  适用场合

n  访问一个聚合对象的内容而无需暴露其内部表示

n  支持对聚合对象的多种遍历

n  为遍历不同的聚合结构提供一个统一接口 (多态迭代)

l  效果

n  支持以不同的方式遍历一个聚合

u   复杂的聚集可用多种方式进行遍历

u   用一个不同的迭代器的实例代替原先的实例即可

n  迭代器简化了聚集的接口

u  n 迭代器具备遍历接口,聚集不必具备遍历接口

n  在同一个聚集上可以有多个遍历

u  每个迭代器保持它自己的遍历状态

u  可以同时进行多个遍历

n  Client和聚集解耦

u  客户端不必知道聚集对象的类型

u  聚集内部数据发生变化不影响客户端程序

(职责链)——对象行为型模式

l  由来

n   员工请假

u   经理可以批2天以下请假,如果没权利,向总监上报

u   总监可以批5天以下请假,如果无权,向总经理上报

n   请求加薪

u   只有总经理有权处理

l  意图

n  避免请求的发送者和接收者之间的耦合关系

n  多个对象都有机会处理一个请求

n  对象连成一条链,沿链传递该请求,直到有对象 处理

l  动机

n  n 链上的每一个对象都是请求处理者

n  n 客户端无须关心请求的处理细节以及请求的传递

n  只需将请求发送到链上即可

n  将请求的发送者和请求的处理者解耦

l  适用性

n  多个的对象可以处理一个请求,哪个对象处 理该请求运行时刻自动确定

n  处理请求的对象集合、在链表中的顺序,由 客户端根据当前应用状态运行时动态决定

n  一个处理请求的对象也可以根据应用状态和 请求类型,把请求传递给不同的处理对象

n  客户和处理链表中的对象都不需要知道到底 用哪个对象处理请求

n  不同请求类型,可以拥有各自不同的处理请 求的对象集合

l  效果

n  降低耦合度

u  请求的发送者无需知道该请求会被哪个对象处 理

u  链中的对象不需知道链的结构

n  增强了给对象分配职责的灵活性

u  可以在运行时刻对职责链进行动态的增加或修 改

u  增加或改变请求处理的职责

n  不保证被接受

u  一个请求没有明确的接收者

u  消息传递和处理不当会出现消息的循环重复执 行

l  v

设计模式-创建型模式

l  简单工厂模式

l  工厂模式

l  抽象工厂模式

l  Builder模式

l  单例模式

 

l  创建型模式概述

n  抽象了实例化的过程

n  帮助系统独立于如何创建、组合和表示对象

u   一个类创建型模式使用继承改变被实例化的类

u   类创建型模式使用继承改变被实例化的类

u   对象创建型模式将实例化委托给另一个对象

n  系统演化越来越依赖于对象复合而非类继承

u   从对一组固定行为的硬编码

u   定义一个较小的基本行为集

u   可以被组合成任意数目的更复杂的行为

n  两个不断出现的主旋律

u   一、将系统使用哪些具体的类的信息封装起来

u   二、隐藏这些类的实例是如何被创建和放在一起的

n  创建型模式的灵活性

u   什么被创建, 谁创建它、怎样被创建、何时创建

u   允许用结构和功能差别很大的“产品”对象配置一个系统

n  符合单一职责原则

u   能够将软件模块中对象的创建和对象的使用分离

u   为使软件结构更加清晰,外界只需要知道对象共同的接口

u   无需清楚其具体的实现细节

l  工厂模式:

n  专门负责实例化有共同父类(接口)的类的实例;工 厂模式可以动态决定实例化哪一个子类对象,不必事先知道要实 例化哪个类;

n  工厂模式分为以下几种形态

u  1. 简单工厂模式:

u  2. 工厂方法模式:

u  3. 抽象工厂模式

l  解释

l  简单工厂模式

n  模式动机    只需要知道水果的名字则可得到相应的水果

n  考虑一个简单的软件应用场景,一个软件系统可以提供 多个外观不同的按钮(如圆形按钮、矩形按钮、菱形按 钮等),这些按钮都源自同一个基类,不过在继承基类 后不同的子类修改了部分属性从而使得它们可以呈现不 同的外观,如果我们希望在使用这些按钮时,不需要知 道这些具体按钮类的名字,只需要知道表示该按钮类的 一个参数,并提供一个调用方便的方法,把该参数传入 方法即可返回一个相应的按钮对象,此时,就可以使用 简单工厂模式

n  模式定义

u  简单工厂模式(Simple Factory Pattern):又称 为静态工厂方法(Static Factory Method)模式, 它属于类创建型模式。在简单工厂模式中,可以 根据参数的不同返回不同类的实例。简单工厂模 式专门定义一个类来负责创建其他类的实例,被 创建的实例通常都具有共同的父类

n  o 模式结构

u  简单工厂模式包含如下角色:

l  • Factory:工厂角色

l  • Product:抽象产品角色

l  • ConcreteProduct:具体产品角色

n  o 模式分析

u  将对象的创建和对象本身业务处理分离可以降低系统的耦合度,使得两 者修改起来都相对容易。

u  在调用工厂类的工厂方法时,由于工厂方法是静态方法,使用起来很方 便,可通过类名直接调用,而且只需要传入一个简单的参数即可,在实 际开发中,还可以在调用时将所传入的参数保存在XML等格式的配置文 件中,修改参数时无须修改任何Java源代码。

u  简单工厂模式最大的问题在于工厂类的职责相对过重,增加新的产品需 要修改工厂类的判断逻辑,这一点与开闭原则是相违背的。

u  简单工厂模式的要点在于:当你需要什么,只需要传入一个正确的参数, 就可以获取你所需要的对象,而无须知道其创建细节

n  模式优缺点

u  简单工厂模式的优点

l  工厂类含有必要的判断逻辑,可以决定在什么时候创建哪一个产 品类的实例,客户端可以免除直接创建产品对象的责任,而仅仅 “消费”产品;简单工厂模式通过这种做法实现了对责任的分割, 它提供了专门的工厂类用于创建对象。

l  客户端无须知道所创建的具体产品类的类名,只需要知道具体产 品类所对应的参数即可,对于一些复杂的类名,通过简单工厂模 式可以减少使用者的记忆量。

l  通过引入配置文件,可以在不修改任何客户端代码的情况下更换 和增加新的具体产品类,在一定程度上提高了系统的灵活性

l  不改客户改自己(让自己难受让客户满意)

u  简单工厂模式的缺点

l  由于工厂类集中了所有产品创建逻辑,一旦不能正常工作,整个 系统都要受到影响。

l  使用简单工厂模式将会增加系统中类的个数,在一定程序上增加 了系统的复杂度和理解难度。

l  系统扩展困难,一旦添加新产品就不得不修改工厂逻辑,在产品 类型较多时,有可能造成工厂逻辑过于复杂,不利于系统的扩展 和维护

n  模式适用环境

u  工厂类负责创建的对象比较少:由于创建的对象较 少,不会造成工厂方法中的业务逻辑太过复杂。

u  客户端只知道传入工厂类的参数,对于如何创建对 象不关心:客户端既不需要关心创建细节,甚至连 类名都不需要记住,只需要知道类型所对应的参数。

n  模式应用

u  在JDK类库中广泛使用了简单工厂模式,如工具类 java.text.DateFormat,它用于格式化一个本地日期或者时间。

u  只需要知道传入传出的是啥就行

n  模式扩展

u  简单工厂模式的简化

l  在有些情况下工厂类可以由抽象产品角色扮演,一 个抽象产品类同时也是子类的工厂,也就是说把静 态工厂方法写到抽象产品类中

l  ?

n  简单工厂模式示例

u  业务需求变化时,客户端代码尽可能不变!

u  加一层工厂类

n  简单工厂模式总结:

u  1. 将具体对象的生成,移植到服务端代码的一个对象生成工厂中(即将子类对象的生成逻辑移到服务端实现);

u  2. 当有需求中要增加新的子类对象时,客户端的代码不再需要修改;

u  3. 服务端需要修改:(1)增加子类;(2)修改、扩充工厂类;

u  有点违背这个原则,扩展要修改工厂类

l  工厂模式(动态工厂)

n  解决问题(简单工厂违反开-闭原则)的办法:增加工厂类的动态性;

n  采用动态工厂模式

n  工厂模式的由来

u  OOP中 常用new直接生成对象实例,但有时带来问题

u  (1)创建对象之前必须清楚所要创建对象的类信息, 但个别情况下无法达到此要求

l  譬如打开一个视频文件需要一个播放器对象,但是用户可能不 知道具体播放器叫什么名字,需要系统分派给这个视频文件一 个合适的播放器,这种情况下用new运算符并不合适

u  (2)许多类型对象的创造需要一系列步骤

l   需要计算或取得对象的初始设置

l   需要选择生成哪个子对象实例

l   在生成需要对象之前必须先生成一些辅助功能对象

u  新对象的建立就是一个 “过程”,不仅仅是一个操作

u  为方便地完成复杂的对象创建工作,可引入工厂模式

n  工厂模式的定义

u  别名:虚拟构造器(Virtual Constructor)模式 或者多态工厂(Polymorphic Factory)模式

u  属于类创建型模式。

l  在工厂方法模式中,工厂父类负责定义创建产 品对象的公共接口,而工厂子类则负责生成具体的 产品对象,这样做的目的是将产品类的实例化操作 延迟到工厂子类中完成,即通过工厂子类来确定究 竟应该实例化哪一个具体产品类

u  意图:定义一个用户创建对象的接口,让子类 决定实例化哪一个类。Factory Method使一个 类的实例化延迟到其子类。

u  问题

l   有多个记录日志的类(如FileLog和DBLog),对它们进行抽象,形成其抽象父类Log

l   客户必须通过它们的子类来记录日志(记录到文件或数据库)

n  工厂方法模式实例

u  如果客户负责具体子类(FileLog或DBLog)对象 的创建和使用,使得客户代码直接依赖于具体子 类,而导致可扩展性降低。例如需要增加一个 EventLog类(它将日志放在windows的事件记录 中)

u  增加日志类分隔用户和具体子类对象

u  时序图

n  工厂方法模式的参与者及其职责

u  Product:定义工厂方法所创建的对象的接口

u  ConcreteProduct:实现Product接口

u  Creator

l  声明返回Product类型对象的工厂方法

l  可定义工厂方法的缺省实现(返回缺省的ConcreteProduct对象)

l  可调用工厂方法创建一个Product对象

u  ConcreteCreator:重新定义工厂方法以返回一个 ConcerteProduct对象

n  o 工厂方法模式的协作

u  Creator依赖于其子类来定义工厂方法

u  通过子类的工厂方法返回合适的ConcreteProduct 实例

n  效果

u  为子类提供挂钩:用工厂方法在一个类的内部创建 对象一般较直接创建对象更为灵活。

u  连接平行的类层次:具体的工厂类与具体的产品类 往往具有平行的等级结构,它们之间一一对应

n  工厂模式总结

u  定义一个创建业务对象的工厂接口,将实际创建工作推迟到子类当 中。核心工厂类不再负责产品的创建,这样核心类成为一个抽象工 厂角色,仅负责具体工厂子类必须实现的接口,这样进一步抽象化 的好处是使得工厂方法模式可以使系统在不修改具体工厂角色的情 况下引进新的产品

u  把真正实现对象创建工作推到子类中,核心类抽象变成提供实现工厂子类必须实现的接口的类

n  性质

u  工厂方法模式是简单工厂模式的衍生、扩展, 解决了许多简单工厂模式的问题。首先完全实 现‘开-闭 原则’,实现了可扩展。其次更复 杂的层次结构,可以应用于产品结构复杂的场 合

n  小结

u  工厂方法模式是简单工厂模式的进一步抽象和推广。由于使用了面向对象 的多态性,工厂方法模式保持了简单工厂模式的优点,而且克服了它的缺 点。在工厂方法模式中,核心的工厂类不再负责所有产品的创建,而是将 具体创建工作交给子类去做。这个核心类仅仅负责给出具体工厂必须实现 的接口,而不负责产品类被实例化这种细节,这使得工厂方法模式可以允 许系统在不修改工厂角色的情况下引进新产品。

u  工厂方法模式的主要优点是增加新的产品类时无须修改现有系统,并封装 了产品对象的创建细节,系统具有良好的灵活性和可扩展性;其缺点在于 增加新产品的同时需要增加新的工厂,导致系统类的个数成对增加,在一 定程度上增加了系统的复杂性。

u  工厂方法模式适用情况包括:一个类不知道它所需要的对象的类;一个类 通过其子类来指定创建哪个对象;将创建对象的任务委托给多个工厂子类 中的某一个,客户端在使用时可以无须关心是哪一个工厂子类创建产品子 类,需要时再动态指定

l  抽象工厂模式

n  由来

u  在软件系统中,经常面临“一系列相互依赖对 象”的创建工作

l  n 由于需求变化,这“一系列相互依赖的对象”也要 改变,如何应对这种变化呢?

l  n 如何像工厂模式一样绕过常规的”new”,提供一种 “封装机制”来避免客户程序和这种“多系列具体 对象创建工作”的紧耦合?

u  一种说法:可以将这些对象一个个通过工厂模 式来创建。但是,既然是一系列相互依赖的对 象,它们是有联系的,每个对象都这样解决, 如何保证他们的联系呢?

u  实例

l  Windows桌面主题,当更换一个桌面主题的时 候,系统的开始按钮、任务栏、菜单栏、工具 栏等都变了,而且是一起变的

l  他们的色调都很一致,类似问题如何解决?

l  应用抽象工厂模式,是一种有效的解决途径

n  意图

u  提供一个创建一系列相关或相互依赖对象的接口, 而无需指定他们具体的类

n  别名:Kit

n  o 应用场合(动机):

u   系统独立于其产品创建、组合和表示时

u   一个系统由多个产品系列中的一个来配置时

u   强调一系列相关产品对象的设计以便进行联合时

u   提供一个产品类库,只想显示其接口而非实现时

n  模式动机

u  产品族与产品等级结构示意图

u  动机

u  当系统所提供的工厂所需生产的具体产品并不是一个简单的对象 ,而是多个位于不同产品等级结构中属于不同类型的具体产品时 需要使用抽象工厂模式。

u  抽象工厂模式是所有形式的工厂模式中最为抽象和最具一般性的 一种形态。

u  抽象工厂模式与工厂方法模式最大的区别在于,工厂方法模式针 对的是一个产品等级结构,而抽象工厂模式则需要面对多个产品 等级结构。一个工厂等级结构可以负责多个不同产品等级结构中 的产品对象的创建 。当一个工厂等级结构可以创建出分属于不同 产品等级结构的一个产品族中的所有对象时,抽象工厂模式比工 厂方法模式更为简单、有效率。

n  o 结构

n  o 参与者

u  抽象工厂模式包含如下角色

l   AbstractFactory:抽象工厂

声明创建抽象产品对象的操作接口

l   ConcreteFactory:具体工厂

实现创建具体对象的操作

l   AbstractProduct:抽象产品

为一类产品对象声明一个接口

l   Product:具体产品

定义一个被具体工厂创建的产品对象

l   Client:客户类

n  模式分析

u  n 抽象工厂类的典型代码如下:

u  具体工厂类的典型代码如下:

n  抽象工厂模式优缺点

u  优点

l  抽象工厂模式隔离了具体类的生成,使得客户并不需要知道什 么被创建。由于这种隔离,更换一个具体工厂就变得相对容易 。所有的具体工厂都实现了抽象工厂中定义的那些公共接口, 因此只需改变具体工厂的实例,就可以在某种程度上改变整个 软件系统的行为。另外,应用抽象工厂模式可以实现高内聚低 耦合的设计目的,因此抽象工厂模式得到了广泛的应用。

l  当一个产品族中的多个对象被设计成一起工作时,它能够保证 客户端始终只使用同一个产品族中的对象。这对一些需要根据 当前环境来决定其行为的软件系统来说,是一种非常实用的设 计模式。

l  增加新的具体工厂和产品族很方便,无需修改已有系统,符合 “开闭原则”。

u  缺点

l  在添加新的产品对象时,难以扩展抽象工厂以便生产 新种类的产品,这是因为在抽象工厂角色中规定了所 有可能被创建的产品集合,要支持新种类的产品就意 味着要对该接口进行扩展,而这将涉及到对抽象工厂 角色及其所有子类的修改,显然会带来较大的不便

l  开闭原则的倾斜性(增加新的工厂和产品族容易,增 加新的产品等级结构麻烦)

l  适用产品族产品等级相对稳定的系统

n  模式适用环境

u  一个系统不应当依赖于产品类实例如何被创建、组合 和表达的细节,这对于所有类型的工厂模式都是重要的

u  系统中有多于一个的产品族,而每次只使用其中某一 产品族。

u  属于同一个产品族的产品将在一起使用,这一约束必 须在系统的设计中体现出来。

u  系统提供一个产品类的库,所有的产品以同样的接口 出现,从而使客户端不依赖于具体实现。

n  案例

l  单件对象创建型模型SINGLETON

n  由来

u  单件模式的实例较为普遍:

l   系统中只能有一个窗口管理器

l   系统中只能有一个文件系统

l   一个数字滤波器只能有一个A/D转换器

l   一个会计系统只能专用于一个公司

u  如何才能保证一个类只有一个实例并且这个 实例易于被访问呢?

l  一个全局变量使得一个对象可以被访问,但它 不能防止你实例化多个对象

u  o 一个更好的办法是,让类自身负责保存它的 唯一实例。

l  这个类可以保证没有其他实例可以被创建,并 且它可以提供一个访问该实例的方法

n  o 意图:

u  n 保证一个类仅有一个实例,提供一个全局访问点

n  o 适用场合:

u  当类只能有一个实例而且用户可以从一个众所 周知的访问点访问它时

u  当该唯一实例是通过子类化可扩展的,并且客 户应该无需更改代码就能使用一个扩展实例时

n  Singleton 被调用的单件对象

u  定义一个Instance操作,允许客户访问它的唯一实例。Instance是一个类 操作(一个静态成员函数)

u  可能负责创建它自己的唯一实例

n  应用示例

n  效果分析

u  对唯一实例的受控访问:因为Singleton类封装唯一实 例,所以它可以严格的控制客户怎样以及何时访问它

u  o 缩小名空间:Singleton模式是对全局变量的一种改进 。它避免了那些存储唯一实例的全局变量污染名空间

u  允许对操作和表示的精化:Singleton类可以有子类, 而且用这个扩展类的实例来配置一个应用是很容易的。 可以用所需要的类的实例在运行时刻配置应用

l  BUILDER( 生成器)

n  当系统准备为用户提供一个内部结构复杂的对 象时,就可以使用Buider模式

n  使用该模式可以逐步地构造对象,使得对象的 创建更具弹性

n  Buider模式的关键是将一个包含有多个组件对 象的创建分成若干个步骤,并将这些步骤封装 在一个称作Buider的接口中

n  ,

n  由来

u  在软件系统中,有时面临着“一个复杂对象” 的创建工作,该复杂对象通常由各个部分的子 对象用一定的算法构成

u  这个复杂对象的各个部分经常面临着剧烈变化 ,但是将它们组合在一起的算法却相对稳定

u  如何应对这种变化?如何提供一种“封装机制 ”来隔离出“复杂对象的各个部分”的变化, 从而保持系统中的“稳定构建算法”不随着需 求改变而改变?

u  ,

n  意图

u  将一个复杂的构建与其表示相分离,使得同样 的构建过程可以创建不同的表示

n  适用性

u  需要生成的产品对象有复杂的内部结构

u  创建复杂对象的算法稳定,或Buider 模式可以强迫生成一定的顺序

u  当构造过程允许被构造的对象有不同的 表示时

n  参与者

u   Builder:

 为创建一个Product对象的各部件指定抽象接口

u   ConcreteBuilder:

 实现Builder接口来构造和装配产品各个部件,提供一个检索产品的接口

u   Director:

 构造一个使用Builder接口的对象

u   Product:

 表示被构造的复杂对象

n  示例

n  模式分析

u  使得产品的内部表象可以独立的变化

l  使用Buider模式可以使客户端不必知道产品内部组成的细节

u   每一个Builder都相对独立,而与其它Builder无关

u   可对构造过程更加精细控制

u   将构建代码和表示代码分开

u   Buider模式的缺点在于难于应付“分步骤构建算法”的需求变动

l  创建型模式的讨论

n  创建型模式,就是创建对象的模式,抽象了实例化的过程。它帮助一个系统独立 于如何创建、组合和表示它的那些对象。

n  创建型模式关注的是对象的创建,创建型模式将创建对象的过程进行了抽象,也 可以理解为将创建对象的过程进行了封装,作为客户程序仅仅需要去使用对象, 而不再关心创建对象过程中的逻辑。

n  所有的创建型模式都有两个永恒的主旋律:

l  为什么需要创建型模式

n  主要有以下几个方面的原因

n  在实际的系统中使用创建型模式创建对象时需要考虑到以下几点

u  动态地确定创建哪些对象。

u  是谁创建这些对象,即是外部的对象,还是本身对象提供,或者静 态的类继承。

u  动态决定怎样创建对象,是新建对象或是使用Cache,还是复制某个 对象。

u  决定怎样组合或者表示这些对象。

架构模式

l  分层架构模式

l  管道/过滤器架构模式

l  插件架构模式

l  Borker架构模式

l  MVC架构模式

l  SOA架构模式

l  ,

 

 

 

l  几种典型的架构模式

n  系统软件

u  ◼ 分层(Layer)

u  ◼ 管道和过滤器(Pipes and Filters)

u  ◼ 黑板(Blackboard)

n  开发分布式软件

u  ◼ 经纪人(Broker)

u  ◼ 客户/服务器(Client/Server)

u  ◼ 点对点(Peer to Peer)

n  交互软件

u  ◼ 模型-视图-控制器(Model-View-Controller)

u  ◼ 显示-抽象-控制(Presentation-Abstraction-COntrol)

l  4+1视图

n  架构设计的多重视图

u  从根本上来说是因为需求种类的复杂性所致。

u  比如一个媒体发布系统:

l  功能需求:用户可以通过浏览器浏览媒体的发布。 据此初步设计出采用浏览器插件的方案;

l  约束条件:不能影响用户浏览器的安全性;细化设 计方案,需要对插件进行认证,自动判别客户端是 否存在,及版本比较;自动下载注册等。

l  使用期质量属性:为保证浏览的流畅,应减少中间 等待的时间,因此应对下一步需使用的媒体做预测 等。

l  o制作发布期的质量保证:保证在遇到较大的媒体时 能保持浏览的流畅,应在发布时将视频等流式化

n  什么是软件架构视图

u  一个架构视图是对于从某一视角或某一点上看 到的系统所做的简化描述,描述中涵盖了系统 的某一特定方面,而省略了于此方面无关的实 体。

u  架构要涵盖的内容和决策太多了,超过了人脑“ 一蹴而就”的能力范围,因此采用“分而治之”的 办法从不同视角分别设计;同时,也为软件架 构的理解、交流和归档提供了方便。

u  多视图方法是软件架构归档的方法,更是指导 我们进行架构设计的思维方法。

u  逻辑架构

l  逻辑架构关注功能。其设计着重考虑功能需求

u  开发架构

l  开发架构关注程序包。其设计着重考虑开发期质量属性,如可 扩展性、可重用性、可移植性、易理解性和易测试性等

u  运行架构

l  运行架构关注进程、线程、对象等运行时概念,以及相关的并 发、同步、通信等问题。

l  其设计着重考虑运行期质量属性,例如性能、可伸缩性、持续 可用性和安全性等。

u  物理架构

l  物理架构关注软件系统最终如何安装或部署到物理机器。其设 计着重考虑“安装和部署需求”。以及如何部署机器和网络来 配合软件系统的可靠性、可伸缩性等要求。

u  数据架构

l  数据架构关注持久化数据的存储方案。设计着重考虑“数据需 求”。

n  4+1视图——关系

u  逻辑视图    逻辑视图不仅关注用户可见的功能,还包括为实现用 户功能而必须提供的“辅助功能模块”;它们可能是逻辑层、功能模块等。

u  开发视图    开发视图关注程序包,不仅包括要编写的源程序,还 包括可以直接使用的第三方SDK和现成框架、类库,以及开发的 系统将运行于其上的系统软件或中间件。开发视图和逻辑视图之 间可能存在一定的映射关系:比如逻辑层一般会映射到多个程序 包等。

u  运行试图    和开发视图的关系:开发视图一般偏重程序包在编译 时期的静态依赖关系,而这些程序运行起来之后会表现为对象、 线程、进程,运行视图比较关注的正是这些运行时单元的交互问 题。

u  物理视图    和运行视图的关系:运行视图特别关注目标程序的动 态执行情况,而物理视图重视目标程序的静态位置问题;物理视 图是综合考虑软件系统和整个IT系统相互影响的架构视图

l  5.1 分层架构模式

n  层次模型、分层

n  在分层模式中,系统将划分为一个层次结构

n  各层之间的关系是客户/服务器

n  每一层都具有高度的内聚性,包含抽象程度 一致的各种构件,支持信息隐藏

n  分层有助于将复杂系统划分为独立的模块, 从而简化程序的设计和实现

n  通过分解,可以将系统功能划分为一些具有 明确定义的层,较高层是面向特定应用问题 的,较低层更具有一般性

n  每层都为上层提供服务,同时又利用了下层 的逻辑功能

n  每层只对相邻层可见,层次之间的连接件是 协议和过程调用,用以实现各层之间的交互

n  上层通过下层提供的接口来使用下层的功能 ,而下层却不能使用上层的功能

n  良好的层次结构将有助于对逻辑功能实施灵 活的增加、删除和修改

n  利用接口,将下层实现细节隐藏起来,从而 有助于抽象设计,形成松散耦合的结构模型

n  ,

n  分层架构规则

n  分层系统的实现

u  ◼ 为分层定义抽象准则,定义抽象层次;

u  ◼ 给每一层命名并指定它们的任务和提供的服务

u  ◼ 为每个层定义接口、实现独立的层;

u  ◼ 确定相邻层通信、降低相邻层的耦合程度;

u  ◼ 设计错误处理策略。

n  优点

u  ◼ 复杂系统分解为层次结构,抽象化每层,易于理解

u  ◼ 层是稳定的,可以被标准化

u  ◼ 减少依赖程度,变更影响的层少

u  ◼ 确定接口,并行开发,易于集成和测试

u  ◼ 支持扩展。每一个层至多和相邻的上下层交互,因 此功能的改变最多影响相邻的上下层。

u  ◼ 支持重用。定义一组标准接口允许各种不同的实现 方法

n  缺点

u  ◼ 层次的划分不太容易

u  ◼ 很难找到一个合适的、正确的层次抽象方法

u  ◼ 并非所有系统都能够按照层次来进行划分,

u  ◼ 即使一个系统的逻辑结构是层次化的,但是出于对 系统性能的考虑,需要把不同抽象程度的功能合并 到一层,破坏了逻辑独立性

u  ◼ 很难找到一种合适和正确的层次划分方法,其应用 范围受到限制

u  ◼ 在传输数据时,需要经过多个层次,导致了系统性 能下降

u  ◼ 多层结构难以调试,往往需要通过一系列的跨层次 调用来实现

n  使用范围

u  o 基于微内核构建的Windows NT系统:

l  ◼ 系统服务层:子系统与NT执行程序间的接 口层;

l  ◼ 资源管理层:包括对象管理、安全引用监 视、进程管理、I/O管理、虚拟存储管理、 局部过程调用等模块;

l  ◼ 内核:负责基本功能,如中断和异常处理 ,多处理器同步,线程调度;

l  ◼ 硬件抽象层:隐藏不同处理器系列机器间 硬件的差异;

l  ◼ 硬件层;

u  o 非严格分层结构,内核和I/O管理需要 直接访问硬件

u  Iso计算机网络国际标准化组织

l  5.2 管道/过滤器架构模式

n  管道/过滤器,简称过滤器模式

n  问题

u  面向数据流系统

l  数据加工过程不应该是一个整体

l  需要易于扩展

l  尽量并行工作

n  解决方法

u  o 根据不同处理阶段,分解为单一小任务

u  o 每个加工步骤通过一个过滤器实现

u  o 过滤器间通过管道连接

u  o 过滤器读取数据,加工转换,变成输出数据

n  过滤器、管道作用

u  o 过滤器对数据加工:

l  ◼ 提取部分数据

l  ◼ 补充部分数据。如计算和附加

l  ◼ 修改部分数据

u  管道

l  ◼ 传输和缓存数据

l  ◼ 被动元素,体现数据流的实现

n  主要包括过滤器和管道两种元素

n  构件被称为过滤器,负责对数据进行加工处 理

u  每个过滤器都有一组输入端口和输出端口,

u  从输入端口接收数据,经过内部加工处理之后, 传送到输出端口上

n  连接件称为管道

u  数据通过相邻过滤器之间的连接件进行传输,

u  连接件看作输入数据流和输出数据流之间的导管

n  管道/过滤器结构

u  将数据流处理分为几个顺序的步骤来进行

u  一个步骤的输出是下一个步骤的输入

u  每个处理步骤由一个过滤器来实现

n  每个过滤器独立,是独立的实体

u  过滤器之间无需共享状态,无需知道其他过滤器

u  完成自己的任务,不同过滤器之间不需要进行交 互

u  数据输出的最终结果与各个过滤器执行的顺序无 关

n  结构

u  o 通用结构:

l  ◼ 管线(Pipelines):

n  限制系统的拓扑结构只能是过滤器的线性序列

l  ◼ 有界管线(Bounded Pipes)

n  限制了在管道中能容纳的数据量

l  类型定义管道(Type Pipes)

n  明确定义在两个过滤器间的数据类型

n  示例

u  管道和过滤器风格的例子

l  ◼ UNIX的shell程序,如命令:

l   cat file | grep xyz | sort | uniq > out 在文件file中查找包括xyz的行 → 排序 → 去掉重行 → 输出到 out文件中;

l  编译器:词法分析 → 句法分析 → 语义分析 → 代码生成。

u  传统的编译器是另一个实例

n  管道和过滤器的实现

u  ◼ 将系统任务分成几个独立的处理阶段;

u  ◼ 定义每个管道传输的数据格式;

u  ◼ 决定管道的连接;

u  ◼ 设计和实现过滤器;

u  ◼ 设计出错处理;

u  ◼ 建立处理流水线

n  评价

u  o 管道/过滤器风格具有以下优点:

l  简单性:允许将系统的输入和输出看作是各个过 滤器行为的简单组合,独立的过滤器能够减小构 件之间的耦合程度

l  重用性:支持功能模块的重用;过滤器并不知道 它的上游和下游的过滤器的特性;它的设计和实 现不会对与它相连的过滤器加以限制

l  系统容易维护和扩展:新过滤器可加入,旧过滤 器可替代

l  并发性

u  管道/过滤器风格也存在着一定的问题:

l  系统处理过程是批处理方式,过滤器具有很强的 独立性,对于每一个过滤器,设计者必须考虑从 输入到输出的转换过程,这种方式会造成过滤器 对输入数据的批量转换处理

l  不适合用来设计交互式应用系统

l  兼容性相对弱;维持两个相对独立但又存在某种 关系的数据流之间的通信可能比较困难

l  有名管道:在过滤器之间通过有名的管道来进行 数据传送,增加了解析或反馈工作,从而降低系 统的效率

l  5.3 插件架构模式

n  插件,plugin

n  问题

u  体现一个适配系统,方便第三方扩展程序

u  软件应保持稳定

u  需要具有扩展性,以便长期使用

u  现有软件不允许修改,同时具有灵活性

u  符合开闭原则

n  使用插件的意义

u  支持特性扩展

u  支持第三方开发

u  降低应用程序大小

u  插件不是简单的功能扩展!

n  解决方法

u  按插件架构模式设计并实现

l  扩展点用接口表示

l  插件实现这些接口

l  安装在接口位置

l  没有添加插件进行扩展时,也可正常运行

u  插件

l  为系统提供了新功能,扩展现有功能

l  不能独立运行

l  不依赖应用程序内部流程

n  插件的概念

u  o 是一个软件的组件,用于扩展新功能

u  o 实现用于扩展的接口

u  o 接口的定义被标记为扩展点

u  o 插件管理者负责运行时加载插件

u  ,

u  特定应用与插件实例解耦 插件管理者负责插件实例化

n  类图与参与者

u  应用:功能可扩展的组件

u  插件管理者:据插件接口识别所有插件,找到指定类型插件

u  插件接口:具体插件的抽象

u  具体插件:插件接口的具体实现,管理者负责插件的实例化 和使用

n  动态行为

n  实现

u   宿主程序(host)

u   插件(plug-in)

u   契约(contract)

n  评价

u  优点

l  每个插件有自己的职责范围(关注点分离)

l  便于第三方扩展

l  保持系统的精简

l  支持多版本同时运行

l  插件的独立测试

u  缺点

l  初始过程消耗较高

l  执行过程中管理开销不断提高

l  扩展时必须通过接口,插件和接口有依赖关系

n  类似模式

u  对比 装饰模式、策略模式

l  没有固定的、公开的用于第三方在运行时扩展 系统功能的接口

l  Broker模式

n  分布式系统架构模式

n  问题

u  o 大型软件系统必须可以“成长”

u  o 单机系统很快达到极限

u  o 组件必须分配到多台计算机

u  o 系统不需要重新设计

u  o 系统架构必须易于扩展,适应性能变化

u  o 系统分解为组件(静态),相互协作(动态)

u  o 必须考虑组件间的解耦

u  o 组件间需要相互通信,不可能完全没有依赖

n  解决方法

u  o 服务器组件

u  o 客户组件

u  o 角色不是静态不变,既是服务器,又是客户

u  o 服务器提供的服务在Broker登记

u  o 客户需要服务,向Broker询问,转交服务器

u  o 提供服务的地方对客户透明

u  o 优点是客户/服务器不需要相互了解物理属性

u  o 序列化和反序列化

u  ,

u  Broker在客户和服务器间建立一个连接,对客户隐藏 服务器的物理位置。Broker必须了解服务器,服务器 提供的服务必须向它登记

n  参与者

u   Client/客户:实现需求,调用服务

u   Client-side Proxy/客户端代理:

l  ◼ 对于客户,代表服务器

l  ◼ 接收客户请求,序列化,传递到Broker

u   Borker/经纪人:

l  ◼ 负责C/S间的通信       (客户端和服务器cs)

l  ◼ 管理服务提供者和服务之间的对应关系

u   Server-side Proxy/服务端代理:

l  ◼ 对于服务器,代表客户

l  ◼ 客户端代理请求反序列化,生成服务方法调用信息

u   Server/服务器:提供服务的类或组件

n  评价

u  优点

l  ◼ C/S的通信和功能分析(关注点分离)

l  ◼ 服务接口不变,更改服务器的实现

l  ◼ 客户无需知道服务器的物理地址

l  ◼ 通过服务器的逻辑名称,实现了隐藏

l  ◼ 不依赖平台,可由不同编程语言实现

l  ◼ 可把十分大型系统分布在多台计算机上

u  缺点

l  ◼ 本地中介为中心查询处,容错性

l  ◼ 间接调用增加了调用的通信开销

l  ◼ 中介可能形成拼接

l  ◼ 代理依赖中介,更换中介必须重新适应

n  使用范围

u  o 客户组件和服务器组件互相解耦

u  o 通过逻辑名称调用服务,不需要物理地址

u  o 运行时没有服务被调用,组件可以修改

u  o 服务的实现对客户组件隐藏

n  o 实例

u  ◼ Internet / DNS

u   ◼ CORBA

n  类似模式

u  Mediator模式(中介者)

l  同事通过Mediator通信,处在同一系统,

l  发送和接收1:n关系

u  Broker模式

l  Broker负责分布式系统中确定接收组件位置

l  发送和接收1:1关系

u  SOA模式

l  Broker隐藏了实现服务的物理地址

l  SOA原则上是公开的

l  SOA架构模式

n  面向服务的架构模式

n  问题

u  基于业务流程,

l  ◼ 一个应用场景代表了系统的一个功能

l  ◼ 一个业务流程可以只包含一个应用场景

l  ◼ 但通常包含多个应用场景

l  ◼ 一个应用场景是一个业务流程(或一部分)

u  o 业务流程会影响系统的整体架构

l  业务流程的修改(很大程度上)引起计算机系 统的改变

l  系统难以维护

l  程序的不稳定,成本急剧增加

n  解决方法

u  面向服务的架构模式(SOA)

l  把一个业务流程直接构建为一个计算机系统

l  应用场景被分配给设计的组件,易于修改

u  中心概念就是服务

l  一个应用场景的目标就是实现一个服务

l  一个业务流程会使用这个服务(应用服务)

n  对服务的要求

u   分布式(distribution)

u   完整性(component appearance)

u   无状态(stateless)

u   松耦合(loosely coupled)

u   互换性(exchangeability)

u   位置透明性(location transparency)

u   平台独立性(platform independence)

u   接口(interface)

u   服务目录(service directory)

n  服务的层次

u  业务流程→应用场景→基本服务

n  角色合作

n  评价

u  优点

l  ◼ 根据业务流程构成服务组件,总体了解服务和接口

l  ◼ 分解服务组件和基本服务组件,减小系统复杂性

l  ◼ 可复用

l  ◼ 接口不变,服务的实现可动态更换

u  缺点

l  过于细分服务组成复杂的结构

l  多个层次之间通信增加额外开销

l  网络传输速率要求高

l  业务流程需要明确定义和做好文档工作

n  使用范围

u  所有C/S系统,特别是复杂系统,SOA简化

u  遗留系统通过封装

u  供应商和客户通过SOA实现业务流程对接

n  类似模式与实现

u  Broker模式对比

u  实现

l  ◼ 基于XML的网络服务

l  ◼ RESTful

l  ◼ CORBA

l  ◼ OSGi

l  MVC结构模式

n  模型-视图-控制器,交互系统

n  Model-View-Controller(MVC)

n  问题

u   系统用户接口(界面)经常改变

u   不需要更改其余程序

u   界面上统一信息可以以不同形式展示

u   数据更改同时体现到期所有展示形式上

n  解决方法

u  o MVC架构模式,交互系统的重要事项

u  o 相互分离的组件中实现:

l  ◼ 数据管理和数据加工(Model)

l  ◼ 表示逻辑(显示)(View)

l  ◼ 用户输入(Controller)

u  程序开发总体趋势(关注点分离)

n  参与者

u  模型

l  核心功能,封装系统数据和对数据的加工

u  视图

l  把数据展示给用户,含有模型要展示的数据

u  控制器

l  负责接收用户的输入

l  对输入的内容进行解释

n  模型

u  任务

l  ◼ 就是执行业务流程

l  ◼ 不包括输入输出

l  ◼ 存储所有业务数据

u  其实现不需要考虑视图和控制器的实现

l  ◼ 提高架构灵活性

l  ◼ 封装系统基本数据

l  ◼ 模型中的反馈可以调用存储的数据,加工和处理

l  ◼ 控制器调用模型中的方法

u  o 通知视图

l  ◼ 被动模式(控制器通知视图数据的改动)

l  ◼ 主动模式(推和拉)

n  视图

u   展示模型中的数据

u   通常包含交互系统的控制元素

u   不对时间做解释,转交控制器

n  控制器

u  控制模型,根据用户输入控制视图状态

n  任务

u  ◼ 视图接收的事件和用户输入的数据转化为模型方法的调用

u  ◼ 对用户行为进行解释,决定调用哪个模型方法

u  ◼ 控制模型状态变化和数据改变

n  动态行为

n  MVC中的观察者模式

n  MVC中的组合模式

n  评价

u  优点

l  ◼ 模型类和用户界面分别设计

l  ◼ 修改界面无需更改模型

l  ◼ 模型单独测试

l  ◼ 同一应用设计不同用户界面

u  缺点

l  ◼ 经常更新影响系统性能

l  ◼ 小的应用系统,细分多个类增加实现开销

n  使用范围

u  交互式软件