设计面向对象软件比较困难,而设计可复用的面向对象软件就更加困难。你必须找到相关对象,以适当的粒度将它们归类,再定义类的接口和继承层次,建立对象之间的基本关系。你的设计应该对手头的问题有针对性,同时对将来的问题和需求也要有足够的通用性。
内行的设计者知道:不是解决任何问题都要从头做起。他们更愿意复用以前使用过的解决方案。
具有统一格式的、已分类编目的若干组设计模式
一个模式有四个基本要素
本书的设计模式是对被用来在特定场景下解决一般设计问题的类和互相通信的对象的描述。
模型/视图/控制器(Model/View/Controller)三元组被用来构建用户界面。
“订购/通知”协议来分离视图和模型。
更一般的设计被描述为Observer模式。
MVC的另一个特征是视图可以嵌套。
更一般的问题:将一些对象划为一组,并将该组对象当作一个对象来使用。这个设计被描述为Composite模式。
MVC允许你在不改变视图外观的情况下改变视图对用户输入的响应方式。
View-Controller关系是Strategy模式的一个例子。
MVC还使用了其他的设计模式,如:用来指定视图缺省控制器的Factory Method和用来增加视图滚动的Decorator。但是MVC的主要关系还是由Obsever、Composite和Strategy三个设计模式给出的。
模式名和分类
意图
别名
动机
适用性
结构:对象建模技术(OMT)和交互图
参与者
协作
效果
实现
代码示例:C++或Smalltalk
已知应用
相关模式
Abstract Factory(3.1):提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
Adapter(4.1):将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
Bridge(3.2):将抽象部分与它的实现部分分离,使它们都可以独立地变化。
Builder(3.2):将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
Chain Of Responsibility(5.1):为解除请求的发送者和接收者之间耦合,而使多个对象都有机会处理这个请求。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它。
Command(5.2):将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可取消的操作。
Composite(4.3):将对象组合成树形结构以表示“部分-整体”的层次结构。Composite使得客户对单个对象和复合对象的使用具有一致性。
Decorator(4.4):动态地给一个对象添加一些额外的职责。就扩展功能而言,Decorator模式比生成子类更为灵活。
Facade(4.5):为子系统中的一组接口提供一个一致的界面,Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
Factory Method(3.3):定义一个用于创建对象的接口,让子类决定将哪一个类实例化。Factory Method使一个类的实例化延迟到其子类。
Flyweight(4.6):运用共享技术有效地支持大量细粒度的对象。
Interpreter(5.3):给定一个语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。
Iterator(5.4):提供一种方法顺序访问一个聚合对象中各个元素,而又不需暴露该对象的内部表示。
Mediator(5.6):用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式的互相引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
Memento(5.6):在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样之后就可将该对象恢复到保存的状态。
Observe(5.7):定义对象间的一种一对多的依赖关系,以便当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动刷新。
Prototype(3.4):用原型实例指定创建对象的种类,并且通过拷贝这个原型来创建新的对象。
Proxy(4.7):为其他对象提供一个代理以控制对这个对象的访问。
Singleton(3.5):保证一个类仅有一个实例;并提供一个访问它的全局访问点。
State(5.8):允许一个对象在其内部状态时改变它的行为。对象看起来似乎修改了它所属的类。
Strategy(5.9):定义一系列的算法,把它们一个个封装起来,并且使它们相互可替换。本模式使得算法的变化可独立于使用它的客户。
Template Method(5.10):定义一个操作中的算法的框架,而将一些步骤延迟到子类中。Template Method使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
Visitor(5.11):表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
创建型 | 结构型 | 行为型 | |
---|---|---|---|
类 | Factory Method | Adapter(类) | Interpreter、Template Method |
对象 | Abstract Factory、Builder、Prototype、Singleton | Adapter(对象)、Bridge、Composite、Decorator、Facade、Flyweight、Proxy | Chain of Responsibility、Command、Iterator、Mediator、Memento、Observer、State、Strategy、Visitor |
目的准则:创建型(Creational)、结构型(Structural)、行为型(Behavioral)。创建型模式与对象的创建有关;结构型模式处理类或对象的组合;行为型模式对类或对象如何交互和怎样分配职责进行描述。
范围准则:指定模式主要是用于类还是用于对象。类模式处理类与子类之间的关系,这些关系通过继承建立,是静态的,在编译时刻便确定下来了。对象模式处理对象间的关系,这些关系在运行时刻是可以变化的,更具动态性。从某种意义上来说,几乎所有模式都使用继承机制,所以“类模式”只指那些集中于处理类间关系的模式,而大部分模式都属于对象模式的范畴。
创建型类模式将对象的部分创建工作延迟到子类,而创建型对象模式则将它延迟到另一个对象中。结构型类模式使用继承机制来组合类,而结构型对象模式则描述了对象的组装方式。行为型类模式使用继承描述算法和控制流,而行为型对象模式则描述一组对象怎样协作完成单个对象所无法完成的任务。
还有其他组织模式的方式。有些模式经常会被绑在一起使用,例如,Composite常和Iterator或Visitor一起使用;有些模式是可替代的,例如Prototype常用来代替Abstract Factory;有些模式尽管使用意图不同,但产生的设计结果是很相似的,例如,Composite和Decorator的结构图是相似的。
对象包括数据和对数据进行操作的过程,过程通常称为方法或操作。对象在收到客户的请求(或消息)后,执行相应的操作。
客户请求是对象执行操作的唯一方法,操作又是对象改变内部数据的唯一方法。
面向对象设计最困难的部分是将系统分解成对象集合。
设计模式帮你确定并不明显的抽象和描述这些抽象的对象。
对象在大小和数目上变化极大。
型构(signature) 接口(interface)
类型(type) 子类型(subtype) 超类型(supertype)
动态绑定(dynamic binding)
多态(polymorphism)
设计模式通过确定接口的主要组成成分及经接口发送的数据类型,来帮助你定义接口。
设计模式也指定了接口之间的关系。
类继承(class inheritance) 子类(subclass) 父类(parent class)
抽象类(abstract class) 抽象操作(abstract operation) 具体类(concrete class)
重定义(override)
混入类(mixin class)
聚合(aggregation)和相识(acquaintance) 聚合意味着一个对象拥有另一个对象或对另一个对象负责。 聚合意味着聚合对象和其所有者具有相同的生命周期。
相识意味着一个对象仅仅知道另一个对象。有时相识也被称为“关联”或“引用”关系。
设计模式可以确保系统能以特定方式变化,从而帮助你避免重新设计系统。
下面阐述了一些导致重新设计的一般原因,以及解决这些问题的设计模式:
1) 通过显式地指定了一个类来创建对象 要避免这种情况,应该间接地创建对象。 设计模式:Abstract Factory、Factory Method、Prototype
2)对特殊操作的依赖 为了避免把请求代码写死,你将可以在编译时刻或运行时刻很方便地改变相应请求的方法。 设计模式:Chain of Responsibility、Command
3)对硬件和软件的依赖 设计模式:Abstract Factory、Bridge
4)对对象表示或实现的依赖 设计模式:Abstract Factory、Bridge、Memento、Proxy
5)算法依赖 有可能发生变化的算法应该被孤立起来 设计模式:Builder、Iterator、Strategy、Template Method、Visitor
6)紧耦合 设计模式使用抽象耦合和分层技术来提高系统的松散耦合性。 设计模式:Abstract Factory、Command、Facade、Mediator、Observer、Chain of Responsibility
7)通过生成子类来扩充功能 子类方法会导致类爆炸,因为即使对于一个简单的扩充,你也不得不引入许多新的子类。 一般的对象组合技术和具体的委托技术,是继承之外组合对象行为的另一种灵活方法。 设计模式:Bridge、Chain of Responsibility、Composite、Decorator、Observer、Strategy
8)不能方便地对类进行修改 设计模式:Adapter、Decorator、Visitor
设计模式在开发如下三类主要软件中所起的作用:应用程序、工具箱和框架。
1.应用程序
设计模式通过减少依赖性来提高内部复用性。松散耦合也增强了一类对象与其他多个对象协作的可能性。 当设计模式被用来对系统分层和限制对平台的依赖性时,它们还会使一个应用更具可维护性。通过显示怎样扩展类层次结构和怎样使用对象复用,它们可增强系统的易扩充性。同时,耦合程度的降低也会增强可扩充性。如果一个类不过多地依赖其他类,扩充这个孤立的类还是很容易的
2.工具箱
工具箱是一组相关的、可复用的类的集合,这些类提供了通用的功能。 工具箱强调的是代码复用
3.框架
框架(Framework)是构成一类特定软件可复用设计的一组相互协作的类。
框架规定了你的应用的体系结构。它定义了整体结构,类和对象的分割,各部分的主要责任,类和对象怎么协作,以及控制流程。框架预定义了这些设计参数
这个层次的复用导致了应用和它基于的软件之间的反向控制(inversion of control)。当你使用框架时,你应该复用应用的主体,写主体调用的代码。
模型和框架的不同:
1)设计模式比框架更抽象
2)设计模式是比框架更小的体系结构元素
3)框架比设计模式更加特例化
目的 | 设计模式 | 可变的方面 |
---|---|---|
创建 | Abstract Factory Builder Factory Method Prototype Singleton |
产品对象家族 如何创建一个组合对象 被实例化的子类 被实例化的类 一个类的唯一实例 |
结构 | Adapter Bridge Composite Decorator Facade Flyweight Proxy |
对象的接口 对象的实现 一个对象的结构和组成 对象的职责,不生成子类 一个子系统的接口 对象的存储开销 如何访问一个对象;该对象的位置 |
行为 | Chain of Responsibility Command Interpreter Iterator Mediator Memento Observer State Strategy Template Method Visitor |
满足一个请求的对象 何时、怎样满足一个请求 一个语言的文法及解释 如何遍历、访问一个聚合的各元素 对象间怎样交互、和谁交互 一个对象中哪些私有信息存放在该对象之外,以及在什么时候进行存储 多个对象依赖于另外一个对象,而这些对象又如何保持一致 对象的状态 算法 算法中的某些步骤 某些可作用于一个(组)对象上的操作,但不修改这些对象的类 |
1)大致浏览一遍模式
2)回头研究结构部分、参与者部分和协作部分
3)看代码示例部分,看看这个模式代码形式的具体例子
4)选择模式参与者的名字,使它们在应用上下文中有意义
5)定义类
6)定义模式中专用于应用的操作名称
7)实现执行模式中责任和协作的操作