猿取向的规划设计模式 ——GoF《设计模式》阅读摘要(零)

 

这个话题是很奇怪,设计模式是引导程序的设计不是模仿什么软件?呃。我的意思是,这是 面“对象”相对的设计模式。

我曾见过有人写《给妻子解释设计模式》,这样的把计算机中的思想与生活相结合固然非常好。人类解决这个问题的思想是相通的,计算机中的非常多思想比方分治、递归、贪心等都能在生活中找到相似的样例。可是。不恰当的样例反而会误导人的思维。造成理解偏差。设计模式本就是类与类之间怎样组织的一套软件设计经验,我们拿软件开发中的实例来解说,会更加准确。

 

写在前面:


为什么要学设计模式

非常赞同《大话设计模式》一书作者所言:重要的不是你将来会不会用到这些模式,而是通过这些模式让你找到“封装变化”、“对象间松散耦合”、“针对接口编程”的感觉,从而设计出易维护、易扩展、易复用、灵活性好的程序。

 

曾经读过《HeadFirst 设计模式》这本书对于小白入门非常好,可是当中非常多样例不符合国人的习惯。有些地方读起来非常别扭(比方:木鸭子,披萨连锁店--这个样例太拖沓了。等)当然也有些非常绝妙的样例(比方:星巴克咖啡)

还有秦小波的《设计模式之禅》,我认为此书是国内原创技术书中较好的,但也有些样例不太合适(比方:女娲造人)。

这些设计模式的书为了通俗易懂,尽量以我们生活中的样例为例来讲设计模式,但有时候不恰当的样例会造成我们的理解偏差。

还曾借阅过《大话设计模式》,此书的写作风格轻快(但我一点也不认为风趣幽默),个人非常不喜欢,并且不少样例比較牵强。

(尤其是"学雷锋"的样例。还有"姚明NBA""计划生育"等例)

对于有一定编程经验的程序猿来说。大可不必如此。以编程中的实际问题为例,更加准确恰当。

GoF的《设计模式》就是设计模式的始祖、经典教科书。

其描写叙述严谨。举例恰当。分类组织。是应该重复细读的经典。

 

网上写设计模式的文章已经非常多了,可是针对GoF《设计模式》的笔记总结,写的认真、精炼的非常少。

此系列文章。是GoF《设计模式》的读书笔记,从中提炼出其精华,掺杂了一些个人的理解和其它书中恰当的样例,可供熟悉设计模式的朋友查阅复习。

欢迎大家一起探讨。

 

 

第0部分

 

面向对象的软件开发过程 找到相关的对象。以适当的粒度将它们归类,再定义类的接口和继承层次,建立对象之间的基本关系

你的设计应该对手头的问题有针对性。同一时候对将来的问题和需求也要有足够的通用性。

拥抱变化

避免反复设计或尽可能少做反复设计。

 

什么是设计模式?


设计模式是对被用来在特定场景下解决一般设计问题的类和相互通信的对象的描写叙述。(也就是怎样组织类之间的关系)

设计模式确定了所包括的类和实例,它们的角色协作方式以及职责分配

 

客户请求(即调用对象的方法)是使对象运行操作的唯一方法,操作又是对象改变内部数据的唯一方法。(类与类之间的交互是通过调用对方的方法来实现的。

 

面向对象设计最困难的部分是将系统分解成对象集合

由于要考虑很多因素:封装、粒度、依赖关系、灵活性、性能、演化、复用等,它们都影响着系统的分解。而且这些因素通常还是互相冲突的。

 

设计的很多对象来源于现实世界的分析模型。可是,设计结果所得到的类通常在现实世界中并不存在。比如,描写叙述过程或算法的对象现实中并不存在,但它们却是设计的关键部分。

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

 


接口

在面向对象系统中,接口是主要的组成部分。对象仅仅有通过它们的接口才干与外部交流,假设不通过对象的接口就无法知道对象的不论什么事情,也无法请求对象做不论什么事情。

对象接口与其功能实现是全然分离的,不同对象能够对请求做不同的实现。也就是说,两个有同样接口的对象能够有全然不同的实现。

(面向过程的语言是接口与功能是绑定的,一个接口(函数名)相应一种功能。

 

当给对象发送请求时。所引起的详细操作既与请求本身有关又与接受对象有关

动态绑定同意你在执行时刻彼此替换有同样接口的对象。这样的可替换性就称为多态(polymorphism)。

 

类指定了对象的内部数据和表示,也定义了对象所能完毕的操作。

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

 

1、类继承与接口继承的比較

理解对象的(class)与对象的类型(type)之间的区别很重要。
一个对象的定义了对象是如何实现的,同一时候也定义了对象的内部状态和操作的实现。

可是对象的类型仅仅与它的接口有关,接口即对象能响应的请求的集合。

一个对象能够有多个类型(有多个接口)。不同类的对象能够有同样的类型(有同样的基类)。

当然。对象的类和类型是有紧密关系的。由于类定义了对象所能运行的操作。也定义了对象的类型。

C++语言的类既指定对象的类型又指定对象的实现。(不像Java有专门的)

 

理解类继承接口继承之间的区别也十分重要。

类继承依据一个对象的实现定义了还有一个对象的实现。它是代码和表示的共享机制。

接口继承描写叙述了一个对象什么时候能被用来替代还有一个对象。(在C++中。类继承与接口继承在语法上的差别就是:基类是不是虚基类)

 

由于很多语言并不显式地区分这两个概念,所以easy被混淆。

在C++中,继承既指接口的继承又指实现的继承。C++中接口继承的标准方法是公有继承一个含(纯)虚成员函数的类。C++中纯接口继承接近于公有继承纯抽象类,纯类继承接近于私有继承。

 

虽然大部分语言并不区分接口继承和实现继承的区别。但使用中人们还是分别对待它们的。

非常多设计模式依赖于这样的区别。比如,责任链模式中的对象必须有一个公共的类型。但普通情况下它们不具有公共的实现。

 

2、对接口编程而不是对实现编程

类继承是一个通过复用父类功能而扩展应用功能的基本机制。

它同意你依据旧对象高速定义新对象。

 

当继承被恰当使用时,全部从抽象类导出的类将共享该抽象类的接口。这意味着子类只加入或重定义操作。而没有隐藏父类的操作。这时,全部的子类都能响应抽象类接口中的请求

仅仅依据抽象类中定义的接口来操纵对象有下面优点:

1、 客户无需知道他们使用对象的特定类型,仅仅须对象有客户所期望的接口。

2、 客户无需知道他们使用对象是用什么来实现的。他们仅仅须知道定义接口的抽象类。

这将及大地降低子系统实现之间的互相依赖关系。

 

面向对象设计的原则:针对接口编程,而不是针对实现编程

不将变量声明为某个特定的详细类的实例对象。而是让它遵从抽象类所定义的接口

 

当你不得不在系统的某个地方实例化详细的类(即指定一个特定的实现)时,创建型模式(AbstractFactory, Builder, Factory Method, Prototype, Singleton)能够帮你。

通过抽象对象的创建过程,这些模式提供不同方式以在实例化时建立接口和实现的透明连接。

创建型模式确保你的系统是採用针对接口的方式书写的。而不是针对实现而书写的

 


运用复用机制

理解对象、接口、类和继承之类的概念对大多数人来说并不难,问题的关键在于如何运用它们写出灵活的可复用的软件。

设计模式将告诉你如何去做。

 

1、继承和组合的比較

面向对象系统中功能复用的两种最经常使用技术是类继承对象组合

类继承

同意你依据其它类的实现来定义一个类的实现。

(此被称为“白箱复用”,在继承方式中,父类的内部细节对子类可见。破坏了封装性)

类继承在编译时刻就定义了,所以无法在执行时刻改变从父类继承的实现细节。

假设继承下来的实现不适合解决新的问题,则父类必须重写或被其它更合适的类替换。

且子类中的实现与它的父类有如此紧密的依赖关系,以至于父类实现中的不论什么变化必定导致子类发生变化。

这样的依赖关系限制了灵活性并终于限制了复用性。

一个可用的解决方式是仅仅继承抽象类(接口继承),由于抽象类通常提供较少的实现。


对象组合

新的更复杂的功能能够通过组装或组合对象来获得。(此被称为“黑箱复用”,对象的内部细节是不可见的。)

对象组合是通过获得对其它对象的引用而在执行时刻动态定义的。由于对象仅仅能通过接口訪问,所以我们并不破坏封装性。

仅仅要类型一致。执行时刻还能够用一个对象来替代还有一个对象。

 

优先使用对象组合有助于保持每一个类被封装,并被集中在单个任务上。且系统的行为将依赖于对象间的关系而不是被定义在某个类中。

 

面向对象设计的第二个原则:优先使用对象组合,而不是类继承。

 

2、托付

托付(delegation)是一种组合方法,它使组合具有与继承相同的复用能力。

在托付方式下,有两个对象參与处理一个请求,接受请求的对象将操作托付给它的代理者

这类似于子类将请求交给它的父类处理。使用继承时,被继承的操作总能引用接受请求的对象。

(即用父类的方法来处理子类对象)

托付方式为了得到相同的效果,接受请求的对象将自己传给代理人,使代理人的操作能够引用接受请求的对象

 

【例】

我们能够在窗体类中保存一个矩形类的实例变量来代理矩形类的特定操作,这样窗体类能够复用矩形类的操作,而不必像继承时那样定义成矩形类的子类。一个窗体拥有一个矩形,而不是一个窗体就是一个矩形。

窗体如今必须显式的将请求转发给它的矩形实例。而不是像曾经它必须继承矩形的操作。

 

以下的图显示了窗体类Window将它的Area操作托付给一个矩形实例:

 


(箭头线表示一个类对还有一个类实例的引用关系。引用名是可选的。本例为"rectangle")

 

托付的长处是:它便于执行时刻组合对象操作以及改变这些操作的组合方式。

(假定矩形对象和圆对象有同样类型。我们仅仅需简单的用圆对象替换矩形对象,则得到的窗体就是圆形的。

)

 

有一些模式使用了托付,比方Stata、Strategy和Visitor。

在Strategy模式中,一个对象将一个特定的请求托付给一个描写叙述运行策略的对象,它是通过改变代理者来改变对象的行为

 

托付是对象组合的特例(用到了转发)。

它告诉你对象组合作为一个代码复用机制能够替代继承

 

3、继承和參数化类型(模板)的比較

还有一种功能复用技术(并不是严格的面向对象)是參数化类型(即模板)。

它同意你在定义一个类时,并不指定该类所用到的其它全部类型。

未指定的类型在使用时以參数形式提供。

模板给我们提供了类继承和对象组合外的第三种方法来组合面向对象系统中的行为

很多设计能够使用这三种技术中的不论什么一种来实现。

模板同意你改变所用到的类型,但继承和模板都不能在执行时刻改变。对象组合技术能够在执行时刻改变组合行为,可是它存在间接性。比較低效。

 

--------------

【附】依赖、关联、聚合和组合之间的差别

  • 依赖(Dependency) 关系是类与类之间的联接。依赖关系表示一个类依赖于还有一个类的定义。比如,一个人(Person)能够买车(car)和房子(House),Person类依赖于Car类和House类的定义,由于Person类引用了Car和House。

    与关联不同的是,Person类里并没有Car和House类型的属性,Car和House的实例是以參量的方式传入到buy()方法中去的。一般而言,依赖关系在Java语言中体现为局域变量、方法的形參,或者对静态方法的调用


  • 关联(Association)关系是类与类之间的联接,它使一个类知道还有一个类的属性和方法。关联能够是双向的。也能够是单向的。

    在Java语言中。关联关系一般使用成员变量来实现。


  • 聚合(Aggregation) 关系是关联关系的一种,是强的关联关系。聚合是总体和个体之间的关系。比如。汽车类与引擎类、轮胎类,以及其他的零件类之间的关系便总体和个体的关系。与关联关系一样,聚合关系也是通过实例变量实现的。

    可是关联关系所涉及的两个类是处在同一层次上的,而在聚合关系中,两个类是处在不平等层次上的,一个代表总体,还有一个代表部分。


  • 组合(Composition) 关系是关联关系的一种,是比聚合关系强的关系。它要求普通的聚合关系中代表总体的对象负责代表部分对象的生命周期,组合关系是不能共享的。代表总体的对象须要负责保持部分对象和存活,在一些情况下将负责代表部分的对象湮灭掉。代表总体的对象能够将代表部分的对象传递给还有一个对象,由后者负责此对象的生命周期。换言之,代表部分的对象在每个时刻仅仅能与一个对象发生组合关系,由后者排他地负责生命周期。部分和总体的生命周期一样。(比方:人与心脏的关系,心脏没了。人也就没了。by-yang)

——摘自《Java面向对象编程》

UML中对象的关系都是在对应的软件环境或实际场景下定义的,这里差别聚合和组合的关系,关键还是在于它们之中总体和部分的关系强、弱,以及它们之间的依附关系。

在C++中,在语法层面。关联、聚合、组合是没有什么差别的,它们是由你的意图而不是由显式的语言机制决定的。

 

--------------

 

 

组织编目

设计模式在粒度和抽象层次上各不同样。因为存在众多的设计模式。我们希望用一种方式将它们组织起来。

 

范围\目的

Creational (创建型)

Structural (结构型)

  Behavioural (行为型)

Simple Factory

Adapter (class)

Interpreter

Factory Method

 

Template method

对象

Abstract Factory

Adapter (object)

Chain of Responsibility

Builder

Bridge

Command

Prototype

Composite

Iterator

Singleton

Decorator

Mediator

 

Facade

Memento

 

Flyweight

Observer

 

Proxy

State

 

 

Strategy

 

 

Visitor

 

我们在两个维度上对模式进行分类。

第一是目的准则。即模式是用来完毕什么工作的。模式根据其目的可分为创建型结构型行为型

创建型模式与对象的创建有关;结构型模式处理类或对象的组合行为型模式对类或对象如何交互和如何分配职责进行描写叙述。

 

第二是范围准则。即模式主要是用于类还是用于对象类模式处理类和子类之间的关系。这些关系通过继承建立。是静态的,在编译时刻便确定下来了。

对象模式处理对象间的关系,这些关系在执行时刻是能够变化的,更具动态性。

 

创建型类模式,将对象的部分创建工作延迟到子类,而创建型对象模式则将它延迟到还有一个对象中。

结构型类模式,使用继承机制来组合类。而结构型对象模式则描写叙述了对象的组装方式。

行为型类模式。使用继承描写叙述算法和控制流,而行为型对象模式则描写叙述一组对象如何协作完毕单个对象所无法完毕的任务。

 

封装变化。是很多设计模式的主题。

 

 

 

 





版权声明:本文博客原创文章,博客,未经同意,不得转载。

posted @ 2015-08-10 21:56  hrhguanli  阅读(212)  评论(0编辑  收藏  举报