1. 什么是设计模式

对用来在特定场景下解决一般设计问题的类和相互通信的对象的描述。

设计模式命名、抽象和确定了一个通用设计结构的主要方面,这些设计结构可以用来构造可复用的面向对象设计。

2. Smalltalk MVC中的设计模式

在SmalltalkMVC-80, 类的模型/试图/控制器三元组(MVC)被用来构建用户界面。MVC通过建立一个“订阅/通知”协议来分离试图和模型,试图必须保证正确显示反映了模型的状态。一旦模型数据发生变化,模型 将通知有关的试图,每个试图响应得到了刷新自己的
机会。

MVC 的主要关系是由Observer、Composite和Strategy三个设计模式给出

3. 23个设计模式

按照第一条”目的“准则,即模型用来完成什么工作的,可分为三种:
创建型:Factory Method、Abstract Factory、Builder、Prototype、Singleton
结构型:Adapter(类)、Adapter(对象)、Bridge、Composite、Decorator、Facade、Flyweight、Proxy
行为型:Interpreter、Template Mthod、Chain of Reponsibility、Command、Iterator、Mediator、Momento、Observer、State、Strategy、Visitor
创建型模式与对象的创建有关,结构型模式处理类或对象的组合,行为型模式对类或对象怎样交互和怎么分配职责进行描述

Abstract Facory: 提供了一个创建一系列相关或者相互依赖的接口,无需指定具体的类
Adapter: 将一个类的接口转换成客户希望的另一个接口。Adaptor使得原本由于接口不兼容而不能一起工作的那些类可以一起工作
Birdge: 将抽象部分与它的实现部分分离,是它们都可以独立的变化
Builder:将一个复杂对象的构建与它的表示分离,使得同样的构建可以创建不同的表示
Chain of Responsibility: 接触请求发送者和接收者之间的耦合,使多个对象都有机会处理这个请求。将这些对象连成一条链,并沿着这条链传递该请求,知道有一个对象来处理它
Command: 将一个请求封装成一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以支持可取消的草操作
Composite: 将对象组合成树形结构以表示“部分-整体”的层次结构。使得客户对单个对象和组合对象的使用具有一致性
Decorator: 动态的给一个对象添加一些额外的职责。及扩展功能而言,Decorator模式比生成子类方式更为灵活
Facade: 为子系统的一组接口提供一个一致的界面,Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用
Factory Method: 定义一个用于创建对象的接口,让子类决定将哪一个类实例化。Facotry Method是一个子类的示例化延迟到其子类。
Flywight: 运用共享技术有效地支持大量细粒度的对象
Interpreter: 给定一个语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子
Mediator: 用一个中介对象来封装一系列的对象交互。中介者使用对象不用显式的相互引用,从而使其耦合松散。而且可以独立地改变他们之间的交互
Mementor: 在不破坏封装性的前提下,获得一个对象的内部状态,并在该对象之外保存这么状态。这样可以将对象恢复到保存的状态。
Observer: 定义一个对象间的一对多的依赖关系,以便当一个对象的状态发生改变时,所有依赖与它的对象都得通知并自动刷新
Prototype: 用原型实例指定创建对象的种类,并且通过拷贝这个原型来创建新的对象

4.设计模型怎样解决设计问题

设计模式采用过多种解决面向对象设计者经常碰到的问题。

1.寻找合适的对象
面向对象程序由对象组成,对象包括数据和对数据操作的过程,过程通常称为方法或操作。对象在收到客户的请求(或消息)后,执行响应的操作,客户请求时是使用对象执行操作的唯一方法,操作又是对象改变内部数据的唯一方法,这些限制导致对象内部状态是被封装的,不能被直接访问。
面向对象最困难的部分是将系统分解成对象集合。需要考虑很多因素:封装、粒度、依赖关系、灵活性、性能、演化、复用等,这些因素通常又是互相冲突的
Composite模式引入了同意对待现实世界并不存在的对象的抽象方方法。Strategy模式描述了怎样实现可互换的算法族,State模式将实体的每一个状态描述为一个对象

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

2.决定对象的粒度
对象在大小和数目上变化极大,怎样决定对象应该是什么呢?设计模式很好的讲述了这个问题。Facade模式描述了怎样用对象表示完整的子系统,Flyweight模式描述了如何支持大量的最小粒度对象。其他一些设计模式描述将一个对象分解成许多小对象的特定方法。Abstract FactoryBuilder 产生专门负责生成其他对象的对象.VisitorCommand 生成的对象专门负责实现其他对象或对象组的请求

3.指定对象的接口
对象声明的每一个操作指定的操作名、作为参数的对象和返回值,这就是作为操作的型构(Signature)。对象操作所定义的所有操作型构的集合被称之为该对象的接口(interface)。对象接口描述了该对象所能接受的所有请求的集合。类型(type)是用来标识特定接口的名字。当一个接口包含另一个接口时,我们就说它是零i一个类型的子类型(subtype),而称另一个类型为它的超类型(supertype)。面向对象中接口时基本组成部分,只有接口才能与外部交流,对象接口和功能实现是分离的,发送给对象的请求和它响应操作在运行时的链接就称为动态绑定(dynamic binding。动态绑定允许在运行时彼此替换具有相同接口的对象,这种替换性称之为多态(polymorphism)

设计模式通过接口的主要组成成分及经接口发送的数据类型来帮助你定义接口

Momento模式描述了怎样封装和保存对象的内部状态,以便一段时间后对象能够恢复到这一状态。它规定了Momentod对象必须定义两个接口:一个允许客户保存和复制的限制接口,一个只有原对象才能使用的用来存储和提取状态的特殊接口。
设计模式也指定了接口之间的关系,特别是它们经常要求一些类具有相似的接口,或他们对一些类的接口做了限制。例如 Decorator Proxy 模式分别要求Decorator 和 Proxy 对象的接口被修饰的对象和受委托的对象一致

4.描述对象的实现
我们很少提及实际上怎样区顶一个对象,对象的实现是由他的类决定的,类指定了对象的内部数据和表示,也定义了操作。
对象通过实例化类来创建,此对象被称作该类的当实例化类时,要给对象的内部数据(由实例变量组成)分配存储空间,并将操作与这些数据联系起来。
新的类可以由已存在的类通过类继承(class inheritance)来定义,当子类继承父类时,也继承了父类定义的所有数据和操作。

抽象类(abstract class)的主要目的是为它的子类定义公共接口

抽象类将它的部分或者全部操作延迟到子类中,因此抽象类不能被实例化。在抽象类中定义却没有实现的操作称为抽象操作(abstract opration)。非抽象类称为具体类(concrete class)
子类能够重新定义父类的操作,具体说是类能够重定义(override)父类的定义
混入类(mixin class)是给其他类提供可选择的接口或者功能的类,混入类要求多继承
可复用的面向对象设计有如下原则:

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

类继承根据一个对象的实现定义了另一个对象的实现,他是代码和表示的共享机制。接口继承描述了一个对象什么时候能被被用来代替另一个对象。

5.运用复用机制

对象系中共功能复用两种最常见的技术时类继承和对象组合这种通过生成子类的复用被称为白箱复用(white-box reuse),即父类的内部细节对子类可见。还有一种是通过组装或组合对象来获得,对象组合要求被组合的对象具有良好定义的接口。这种风格被称之为黑箱复用(black-box reuse),对象的内部细节是不可变的

继承在编译时就定义了,所以无法在运行时改变从父类继承的实现,更糟糕的是,父类通常定义了部分子类的具体实现,所以继承常被认为 “破坏了封装性”

父类和子类有紧密的依赖关系,限制了灵活性并最终限制了复用性,一个可用的解决方法是只继承抽象类,抽象类通常提供较少的实现。

对象组合是通过获得其他对象的引用而在运行时动态绑定的,组合要求对象遵守彼此的接口约定

所有不怕破坏封装性,实现上存在较少的依赖关系,
这导致了我们面向对象的第二个设计原则:

优先使用对象组合,而不是类继承

委托(delegation)是一种组合方法,它使组合具有与继承同样的复用能力,在委托方式下,有两个对象参与处理一个请求,接受请求的对象将操作委托给他的代理者(detegate),类似于子类将请求交给它的父类处理。
有一些模式使用了委托,如State、Strategy和Visitor。在State模式中,一个对象将请求委托给一个描述当前状态的State对象来处理。在Strategy模式中,一个对象将一个特定的请求委托给一个描述请求执行策略的对象,一个对象只有一个状态,但他们对不同的请求可以有很多策略。在Visitor中,对象结构的每个元素上的操作总是被委托到Visitor对象。
其他模式没有这么多用到委托,Mediator引进了一个作为其他对象通信的中介的对象。Chain of Reponsibility通过将请求沿着对象链传递来传递请求有时这个请求本身带有一个接受请求对象的引用,这时该模式就使用到了委托,Bridge 将实现个抽象分离开,如果抽象和一个特定实现非常匹配,那么这个实现可以代理抽象的操作。
委托是对象组合的特例

6.关联运行时和编译时的结构

一个面向对象程序运行时的机构通常与它的代码结构相差较大。考虑对象聚合(aggregation)和 相识(acquaintance)的差别以及他们编译时和运行时的表示有多么不同。聚合意味着一个对象拥有另一个对象或对另一个对象负责,相识意味着一个对象仅仅知道另一个对象,有时相识也被称为“关联”或“引用”关系,是一种松耦合的关系。
许多设计模式显示地记述了编译时和运行时结构的差别。CompositeDecorator 对于创建复杂的运行时结构特别有用。Observe也和运行时有关。Chain of Responsibility也产生了继承所无法展现的通信模式
只有理解了设计模式,才能清楚代码中的运行时结构

7.设计应支持变化

获得最大限度复用的关键在于对新需求和已知需求发生变化的预见性,要求你的系统设计能够相应的改进。
为了设计适应这种变化且具有健壮性的系统,必须考虑系统在它的生命周期内会发生怎样的变化。

下面阐述了一些导致重新设计的一般原因,以及解决这些问题的设计模式:
1 通过显示的指定一个类来创建对象
在创建对象时指定类名,将使你受特定实现的约束而不是特定接口的约束。这会使未来的变化更复杂,要避免这种情况,应该间接的创建对象。

设计模式:Abstract Factory, Factory Method, Protorype

2 对特殊操作依赖
当你的请求指定一个特殊的操作时,完成该请求的方式就固定下来了。为避免把请求代码写死,你将可以在编译时或运行时很方便的改变响应请求的方法。

设计模式:Chain of Responsibility, Command

3 对硬件和软件平台的依赖
外部的操作系统和应用编程接口在不同的软硬件平台上是不同的。依赖特定平台软件很难移植到其他平台,甚至很难跟上本地平台的更新。所以设计系统时限制其平台相关性就很重要。

设计模式:Abstract Factory, Bridge

4 对对象表示和实现的依赖
知道对象怎样表示、保存、定位或实现的客户在对象发生变化时可能也需要变化。对客户隐藏这些信息能阻止连锁反应。

设计模式:Abstract Facory, Bridge, Memento, proxy

5 算法依赖
算法在开发和复用时常常被扩展、优化和代替。依赖于某个特定算法的对象在算法发生变化时不得不变化。因此算法应该被孤立起来。

设计模式:Builder,Iterator,Strategy,Template Method,Visitor

6 紧耦合
紧耦合的类很难被复用,因为他们是相互依赖的。紧耦合产生单块的系统,要改变或者删除一个类,你必须理解和删除其他很多类。这样的系统是一个很难学习、一直和维护的密集体。设计模式使用抽象耦合和分层技术来提高系统的松散耦合性

设计模式:Abstract Factory,command , Facade, Mediator, Observe, Chain of Responsibility

7 通过生成子类来扩展功能
通常很难通过定义子类来定制对象。每一个新类都有固定的实现开销(初始化、终止处理等),定义子类还需要对父类有较深入的了解
一般的组合技术和具体的委托技术,是继承之外组合对象行为的另一种灵活方法,新的功能可以通过新的方式组合已有对象,而不是通过定义已存在类的子类的方式加到应用中去。过多使用对象组合会使设计难以理解。许多设计模式产生的设计中,可以定义一个子类,且将它的实例和已存在的实例进行组合来引入定制的功能。

设计模式:Bridge,Chain of Responsibility, Composite, Decoreator , Observe, Strategy

8 不能方便的对类进行修改
有时你不得不改变一个难以修改的类,也许你需要源代码而又没有,或者可能对类的任何改变会要求修改许多已存在的其他子类

设计模式:Adapter, Decorator, Visitor

这些例子反映了设计模式有助于则更加软件的灵活性。