资源描述
1 名词解释:
1.1 设计的5个准则
1.2 4+1 View
1.3 OO协作与协作设计
1.4 OO职责与职责分配
1.5 GRASP模式(或其中之一)
2 软件设计的审美标准
有哪些?列举已知的软件设计方法与技术(至少5种)并说明它们促进了哪些审美标准的达成?
3 设计的层次性
高层设计、中层设计和低层设计各自的出发点、主要关注因素(即哪些审美要素)、主要方法与技术和最终制品
4 软件体系结构风格
(1) 描述或比较相关风格
(2) 对给定场景,判断需要使用的风格
4.1.1 模块级别
n 调用返回风格
Ø 主程序子路径:部件从其父部件当中得到控制信息,绝不向其同级或上级发出调用信息。
部件:过程、功能和模块。
连接键:程序调用。
约束:控制流总是由顶端延层次结构开始向下传递。
Ø 面向对象式:对象能够帮助进行封装内部的secrets,只有通过方法才能够访问对象。
部件:对象或模块
连接键:方法调用
约束:数据的表示对其他对象来说是透明的;每个对象的数据完整性自行维护,每个对象是独立的。
Ø 管道过滤器:每个Filter都能处理数据然后传递给下一个Filter,Filter可以在任意处处理数据,各个Filter之间无数据共享,通过Pipe交互。
部件:Filter
连接键:Pipe
约束:Filter之间不共享数据;Filter并不了解上下Filter;单个Filter的正确性并不依赖上下的Filter
Ø 层次风格:描述层与层之间的调用结构。
部件:过程或对象的集合
连接键:过程调用或方法调用
约束:系统要被严格组织成层次结构,每一层为其上层提供服务,并调用下一层,不允许跨层调用。
Ø 比较
n 主程序和子路径中算法和数据结构式绑定的,修改算法——差;如果要修改数据,则需要所有的模块进行更改——差;增加功能到系统中,要修改一个部件——居中;空间性能很好,数据时共享的,时间性能差,不存在并发处理;重用性不好,所有模块都与数据进行绑定
优点:处理过程清晰,易理解;能够保证正确性
缺点:不利于更改和复用;处理不当会形成公共耦合
适用:顺序处理系统;正确性要求较高的系统
n 面向对象式在算法修改——居中,只要不涉及接口可以随意进行修改;数据表示修改——优,数据在接口中隐藏了;增加新功能到系统中——居中,需要对现有模块进行修改;空间性能很好,在方法调用中可以共享数据,时间性能差,不存在并发;可复用性居中,接口依赖。
优点:只要不修改接口,模块之间的可修改性很好;系统被分解为许多独立的部分
缺点:对象之间交互必须了解对方的接口;对象可能产生副作用
适用:能够把系统分解为算法+数据的结构,以进行封装
n 管道过滤器在算法修改——优,Filter之间完全独立;更改数据表示方式——居中,只会更改存在的Pipe;在增加新功能——优,通过增加新的Filter实现;空间性能——差,Filter之间没有共享数据,需要对数据进行拷贝,时间性能——优,存在并发,但是拷贝数据可能会花费时间;可复用性——优。
优点:易于理解,支持复用,易于维护和扩展,对于特殊的要求可以满足,支持并发。
缺点:不能很好的处理交互问题,传输数据需要额外的空间,可能丢失额外的性能并增加复杂度
适用:系统可分解为多个可并行的任务
n 层次结构
优点:基于增加抽象层次设计;易于修改;易用重用
缺点:很多系统无法简单的划分层次;必须按层次调用,增加高层和低层实现之间的耦合
适用:系统可以按照功能划分为不同的层次
n 隐式调用风格(基于事件)
u 数据封装,所有调用通过事件完成
部件:agent
连接键:事件处理
约束:抛出事件的部件不了解被影响的部件有哪些;对事件的接收顺序也不能有假设;不能假设事件抛出一定有部件进行处理
u 不需要共享数据,算法修改——优;共享数据进行封装,但是仍然存在,数据表示更改——居中;添加新功能——居中,需要修改现有的模块;空间性能——优,数据是共享的,时间性能优,存在并发性;可复用性——优,只要是处理特定的事件都可以进行复用。
优点:可重用性好,可修改性好
缺点:正确性得不到保障;调试测试会很困难
适用:通常适用于能把系统分解为松散耦合的系统
n 黑板风格:
u 各个客户端读写同一块存储区域
部件:一个中心数据结构表示系统状态,一系列独立部件含有在中心数据结构上的操作。
连接键:过程调用或直接内存访问
约束:所有的agent都是独立的;每个agent都依赖共享数据;agent对数据进行操作。
u 优点:可以充分存储大量数据,空间性能优越;减少复杂数据的复制
缺点:必须能够建立稳定的中心数据区;Blackboard可能会成为瓶颈;数据的演化代价很高
适用:可以建立一个中心数据区,并且维护复杂的中心信息。
n MVC风格
u Model与Controller、View分离。
部件:Model部件用来维护领域信息,通知View的更改;View 部件用来给用户展示信息,发送用户请求到Controller;Controller部件用来更改Model状态,对用户请求响应。
连接键:系统调用、消息、时间、直接内存访问
u 优点:允许多态,在一个模型中有独立的View;Views能够被序列化;可以任意增加删除View和Controller
缺点:增加了复杂度;在View中不能有效获得数据访问;与现代用户界面工具不是特别兼容
适用:接口的更改很容易并且可能发生在运行时;用户界面的修改并不会影响逻辑代码
4.1.2 进程级别
n 点对点风格(Point to Point)
u 分布式系统中异步消息
Sender
Receiver
Message
Queue
Scheduler
u 可能多个应用程序需要接受同一个消息;发送者和接受者之间的松散耦合的;传输是基于事件接受的;事件存在层级结构
u 部件:发送者、接受者
连接键:事件路由
4.1.3 物理单元
n C/S风格
u 部件:Client、Server
连接键:基于RPC(远程过程协议)交互协议
n 三层结构风格
u 包括表示层、业务逻辑层和数据层
n 端对端结构风格(Peer to Peer)
u 部件:部件即作为Client也作为Server
连接键:同步异步消息;不共享内存
u 无法区分Client和Server,要了解所有的设备
n 分布式结构风格
网络上所有的物理节点都是平等的,使用时透明。
5 职责分配与协作设计
(3) 协作设计(控制风格)的比较和场景判定
(4) 对给定场景和要求的控制风格,根据GRASP模式,判断特定职责的分配
(5) 根据分析类图和体系结构模块接口,建立基本的设计类图
6 设计模式
6.1 普通Programming to Interfaces有哪些手段?
l 代理模式
问题:为另一个对象提供一个代理或占位符以控制对这个对象的访问
分类:远程代理为不同地址空间的对象提供了本地代表,有可能需要重编码请求
虚拟代理按需创建开销大的资源,may also cache
保护代理控制对原始对象的访问,当访问权限改变时比较有效
类图:
合作:
n 三个类: Subject, RealSubject, and Proxy.
n Client操作Subject中的接口
n RealSubject, and Proxy实现Subject的接口
n Proxy如果发现危险动作,如Writing collections动作,就会发布异常或者报警
n Proxy将安全的调用委派给 RealSubject.
n Client 始终使用 Proxy.
结果:
引入了操作对象的一种间接方法
q remote proxy可以隐藏地址空间差异
q virtual proxy 可以优化
q 允许附加的管理
l 原型模式
使用原型实例来实现创建对象,通过拷贝原型的方式
图示:
参与者:
• 原型
声明了一个克隆自己的接口
• 具体原型
实现了克隆自己的操作
• 客户
通过克隆原型创建对象
a) 迭代器模式:提供一种方式来访问一个集合,但不用了解其内部的数据结构。
i. 优点
1. 可以实现针对接口编程和信息隐藏
2. 可以实现集合的不同遍历方式
3. 迭代器简化了聚合的接口
4. 可以同时有多个迭代器的存在
ii. 迭代机制:内部迭代和外部迭代
b) 代理模式:为另外一个对象的控制访问提供一个替代,包括远程代理、虚拟代理、安全代理。
i. 远程代理能够隐藏对象在不同地址空间的事实
ii. 虚拟代理可以达到优化
iii. 安全代理允许额外的安全保障
c) 原型模式:当使用原型对象创建某个类型的对象时,通过返回这个原型的拷贝来创建新的对象。
LSP原则:所有的子类必须能够代替其父类的功能。
6.2 集合类型Programming to Interfaces有哪些手段?
l 迭代模式(主要用于集合)
定义:提供一种方法顺序访问一个聚合对象的各个元素,而不暴露其内部表示
*支持对集合对象的多种遍历方式;
*支持对不同的集合对象统一遍历(多态迭代)
效果:遵循设计规则,面向接口编程和信息隐藏
支持遍历一个集合的变化
迭代简化了一个集合的接口和实现,也让责任各得其所
对一个集合有多种遍历方法
两种方式:
外部迭代控制,按照客户的指示提供集合元素,客户控制元素的访问
内部迭代控制,内部控制的迭代机制接受操作,代表客户把他们应用于每个元素,迭代机制控制元素的访问,而不是由客户控制(更好)
6.3 OCP的手段有哪些?【提示:不仅仅是继承】
6.4 一个模块的信息隐藏有哪两种基本类型,各自有哪些典型的处理手段?
l 每个隐藏一个重要的设计决策的实现细节,只有该模块的元素才知道该细节
(看一下门面模式)
l 隐藏改变,对于预期的改变,把可能改变从模块中分离开来,安排到新的类,方法或者设计单元中,使得这个可能改变的设计单元不会影响其他部分
(看一下策略、桥接、OCP、装饰、适配器模式)
a) 门面模式(Façade Pattern):为模块或子系统提供一个或一系列统一的接口,定义高层的借口使得模块或子系统更加易于使用。
信息隐藏:对客户端和一系列组件组成的子系统解耦。
b) 策略模式(Strategy Pattern):将对象中的算法提取出来,作为一个对象封装。
提高了可更改性和可重用性,但理解上复杂。
c) 装饰模式(Decorator Pattern):不修改类的代码而扩展类的实例的功能。比静态的继承更加灵活。装饰器对接口来说是透明的。
i. 特点
1. 比静态集成更加灵活
2. 避免类层次结构上的高负载
3. 存在很多相似的小对象,很难理解和调试
4. 一个装饰器和它的组件并不唯一,识别对象可能产生问题
d) 适配器模式(Adaptor Pattern):将已有类的接口转换为客户端期待的接口,是由于接口不统一的类能够共同工作。
可以提高灵活性、重用性、鲁棒性。
6.5 实现共性与可变性有哪些手段?对给定的场景,给出共性与可变性的设计方案。
l 多态,父类——共性,子类——可变性
l 聚合,Whole——共性,Part——可变性
l 运行时注册,静态编译——共性,运行时注册——可变性
(涉及到的模式:strategy,bridge,state,observer,command,具体是怎样实现共性差异性的没说,大家讨论理解一下^^)下面列出PPT上的相关内容:
策略模式:
使用的情况:
ð 许多相关类只在行为上不同,策略提供了一种用不同的行为配置类的方法
ð 类定义了不同的行为,并且这些行为在操作上表现为不同的条件语句
ð 需要不同的算法
ð 算法使用的策略用户不应知道
Solution:
结果:
易修改性和可复用性、难于理解(用户必须知道所有策略然后再选择合适的)
注意:
If there are complex and vary algorithms, then there may be Strategy Pattern
If there is a changeable behavior in some fixed characteristics, then there may be Strategy Pattern
If there are complex conditional statements, then there may be Strategy Pattern
状态模式:
问题:对象的行为依赖于它的状态,在运行时根据状态改变
操作有多种选择结构——对每个状态有多种选择(每个选择为一个类)
结构:
协作:Context委托给state具体的request,让它分配给现有的具体状态对象
Context可以把自己作为参数传递给ConcreteState对象
Context是用户的接触接口,用户无需和State接触
Context或者ContextState对象可以决定ConcreteState对象的切换
结果:
状态具体行为本地化
状态转换明确
状态对象可以共享
观察者模式
Ø 观察者模式定义了对象之间的一对多关系
Ø 主题用一个共同的接口来更新观察者
Ø 观察者和可观察者之间用松散耦合方式结合、可观察者不知道观察者的细节、只知道观察者实现了观察者的接口
Ø 类图
Ø 结果:
灵活性、可改变性、可重用性、更加复杂
Ø 使用观察者模式的情况
1. 当一个对象改变时,需要改变其他对象,而且不知道多少对象需要改变
2. 当对象需要notify其他的对象,而且不知道这些对象是什么
命令模式
Ø 问题
根据表现的动作参数化对象
在不同的时候specify, queue, and execute requests
通过存储上下文信息支持undo
为了恢复支持变更日志
Ø 类图
Ø 效果
将“动作的请求者”和“动作的执行者”解耦
可将command存放在Stack或者Queque中
可扩展性强,易于扩展Command
很容易支持undo和redo
d) 多态(继承)解决1对N关系:父对象对应共性,子对象对应差异性。
e) 聚集关系,解决M对N关系:整体对应共性,部分对应差异性。
f) 运行时注册:解决1对N或M对N关系:静态编译时确定对应共性,运行时体现的特性对应差异性。
g) State Pattern:对象的行为依赖其状态。
i. 优点
0. 状态决定行为本地化
1. 明确状态转换
2. 状态对象能够被共享
h) 观察者模式(Observer Pattern):定义了对象之间一对多的依赖关系,当一个对象更改其状态,所有依赖它的对象都被通知,并且自动更新。
增加了灵活性,可更改性,可重用性。但是更加复杂。
i) 命令模式(Command Pattern):通常有Action可以指定,在不同的时间都可以执行请求,支持取消操作,为恢复支持更改日志
i. 优点:
0. 在发送和执行请求对象之间解耦
1. 可以在队列或栈之中存储队列
2. 很容易支持取消和redo
3. 很容易扩展命令
6.6 在解决De-Coupling时,常常使用哪些Indirection的手段?对给定的场景,给出Indirection的解决方案。
6.7 De-Coupling有哪些手段?
l 避免重复的代码,因为改变一个重复的代码,意味着要改变另一块
l DIP,为一个模块定义一个接口,具体必须依赖于抽象
l 继承,共性和变化
l 设计模式
Ø 调停者模式
结构:
参与者:
Mediator
q 定义一个和Colleague对象交互的接口
q 知道一些事件或者情况
ConcreteMediator
q 通过Colleagues间的协作实现合作
q 知道并且维护他的Colleagues
Colleague classes
q 每个 Colleague 类知道他的Mediator对象
q Colleagues之间的交互通过Mediator
q 给 mediator提供服务或者请求
q 有些请求对所有的Colleagues是通用的,也有各自不同的
结果:
定义了一个对象,它封装了一组对象间的交互
通过使得对象间显示引用降低了耦合
能够独立改变交互Lets you vary their interaction independently
集中控制Centralizes control
Ø 桥接模式
将抽象的类层次和实现的类层次分离,使用委托(delegation)作为两者间的桥接
· Abstraction –定义抽象接口
· RefinedAbstraction –继承上面定义的抽象接口
· Implementor – 定义了实现类的接口;可以和Abstraction的接口不同。典型情况下,Implementor 提供了基本操作,Abstraction定义了基于这些基本操作定义高层操作
· ConcreteImplementor 实现Implementor interface并且定义它的具体实现
结果:
将接口和实现解耦
提高了可扩充性
实现细节对Client隐藏
j) 避免重复:Do it once。有重复意味着有耦合。
k) 依赖倒置:为一个单独的模块定义接口是打破依赖,降低耦合的根本途径。
l) 继承:可以将多个耦合降低为一个耦合。
m) 设计模式:
i. 调停者模式(Mediator Pattern):
ii. 桥接模式:抽象和实现二者分离,适用于接口和实现都会发生修改的情况。
Ø 接口和实现部分解耦
Ø 提高了可扩展性
Ø 对客户端隐藏了实现细节
6.8 MVC与分层方式的区别【要具体到实现】
6.9 对象的创建有哪些常见解决方法?【提示:这里要求常见解决方法,不是设计模式】
a) 单键模式(Singleton Pattern):不是程序员来决定创建的次数而是由程序来决定。
构造函数私有,只能自身调用;声明一个静态私有变量,指向自身;使用getinstance()代替构造函数创建实例。
b) 工厂模式(Factory Pattern):适用于了解所有需要创建类型的情况
c) Factory Method Pattern:为创建对象定义接口,但是让子类决定实例的类型。
提高可修改性和可复用性,但增加了复杂度。
d) 抽象工厂模式(Abstract Factory):提供创建一系列相关或依赖对象的接口,通过这个接口不需要了解它们具体的类。
iii. 优点
0. 将具体类分离出来
1. 是更改产品更加容易
2. 各个产品之间的生产一致
iv. 缺点
0. 增加产品的类型很困难
1. 需要重新对抽象工厂和其子类进行编写
2. 在动态语言里表现不是很差
e) 原型模式(Prototype Pattern):在创建类的过程中,实例化和初始化的过程十分复杂。
优点:在运行时添加或删除原型,可以创建多种原型,伪动态加载。
l 简单的对象创建
--Creator Pattern
--Coupling Pattern
--Cohesion Pattern
l 复杂对象的创建
Scenario 1: only one instance permitted
Ø Singleton模式
结构:
Scenario 2: Limited instance permitted
Ø 在Singleton的基础上改进(看一下例子)
A More complex scenario: type variations
Ø 工厂方法
定义了一个创建对象的接口,但由子类决定要实例的类是哪一个,让类把实例化推迟到子类
结构:
参与者:
Product (Document)
*定义工厂方法创建的对象接口
ConcreteProduct (MyDocument)
* 实现产品接口
Creator (Application)
*声明工厂方法
*可能定义一个缺省的工厂方法实现,它返回一个缺省的ConcreteProduct对象
ConcreteCreator (MyApplication)
*重写工厂方法,返回实例ConcreteProduct
结果:
可改变性,可复用性,复杂
Ø 抽象工厂模式
提供一个接口,用于创建相关或者依赖对象的家族,而不需要明确具体的类
ð AbstractFactory
声明了一个用于创建抽象产品的接口
ð ConcreteFactory
实现了创建具体产品对象的操作:通常使用Singleton
ð AbstractProduct
为产品对象的类型声明一个接口,具体工厂生产这些产品
ð ConcreteProduct
定义了被相关具体工厂创建的产品对象
结果:
good---分离了具体类;产品组更换更容易;保持产品组的一致
bad--难支持新的产品种类;需要重新编码Abstract Factory和所有子类;但是在动态类型语言中不太差
Scenario 3: complex instantiation and initialization, such as:Runtime instantiation,Value varies in initialization
Ø 原型模式:
结果:
运行时增删原型
Specifying new objects by varying values
q 加一个原型实际上新增了一个类型
Pseudo Dynamic loading
7 设计模式部分所有的思考题
a) 编程实现: Iterator Pattern and Proxy Pattern
b) 用Mediator Pattern 实现 CardShark游戏
c) 实现前面Bridge Pattern的示例
d) 利用Decorator解决下图的新增功能
e) Adapt enumeration to iterator
f) 如果一个对象集的一个行为需要协作对象来完成,但是它们在协作对象上存在差异性,如何处理?
g) 如果一个对象集之间除共性外,有超过2个的差异行为,如何处理?
h) 如果一个对象集除了接口之外,全是差异行为,如何处理?
i) 从共性和差异性角度,如何解释Bridge模式?
j) 比较strategy 与 state
v. 1 of N or M of N?
vi. Who control the changing?
vii. How to change?
0. Fixed rules or table-driven (configuration files)
viii. Creating and destroying policy?
k) “Context decide changing of ConcreteState object ”与“ ConcreteState decide changing of ConcreteState object”有何不同?
l) Limited instance permitted,以singleton为基础,编写程序解决上述问题
m) 思考题,如果增加pizza类型t1,请基于Decorate模式完成对factory的修改
n) 如果有多个其他类实例的创建类型都需要子类来决定怎么办?
o) 如果多个其他类实例之间存在类型依赖该怎么办?
(2) Open/Close Principle
Software should be open for extension, but closed for modification.
模块应该有secret:修改。对一个软件先预测它的修改,然后把修改独立出来,然后对其进行封装,如果change发生了,我们希望这个change易于扩展而不要进行修改。
(3) MVC Pattern:
a) 将核心业务模型与表现、控制逻辑使用功能划分开,允许多视图共享一个商业数据模型,使得支持多客户端更加易于实现、测试和维护。
Strategy Pattern
Observer Pattern
展开阅读全文