前言:
1,设计模式和面向对象程序设计承诺:要简化软件设计人员和开发人员的工作;
2,对象真正的威力不是来自继承,而是封装;
3,模式应该相互配合,共同解决问题;
4,从面向对角到模式再到真正的面向对象;
5,所有层次--分析、设计和实现都存在模式;
6,错就错在试图在问题域中创建类,然后再将它们结合起来形成一个系统,这正是非常糟糕的做法;
7,设计模式下隐藏着敏捷方法和极限编程的思想,与极限编程实践、测试驱动开发和Scrum相结合;
8,任何情况都可以使用一些指导性原则和策略“推导”出几个设计模式;
9, 提供一种面向对象软件设计开发的新视角;
10,模式已经事先告知会出现什么情况,能告知基本的对象是什么,什么应该特别注意的,能预知系统最重要的特性;
11,能帮助最终用户理解系统,因为它提供了系统的来龙去脉;
12,所有程序员都会遇到的挑战之一:不能过早进行实现,需要三思而后行;
13,优秀的程序员会寻找替代方案
第一部分:面向对象软件开发简介
第一章:面向对象范型
1,”在需求表中寻找名词,并实现为对象" 是很局限的观念:封装不仅是数据隐藏,对象不仅是含有数据并提供操作的一些东西;
2,功能分解的软件设计方法的问题:主程序因要控制各子程序的执行而显复杂(用委托解决);无法应对未来的变化;
3,许多bug都源自代码的修改;
4,对于阻止变化,我们无计可施。但对于变化本身,我们并非无能为力;
5,需求总在变化中。发生改变,从容应对;
6,第二次既能编写正确,那第一次也应该能编写正确;
7,模块化并不总能有助于代码应对所有可能遇到的变化,主要因为:低内聚和紧耦合;
8,内聚性:例程操作之间联系的紧密程度, 耦合性:两个例程之间的紧密程度;
9,软件开发的目标是:高内聚,松耦合
10,维护与调试的大多时间花在:寻找bug和避免不良bug出现上,改正bug只需很少时间;
11,只关注函数会导致一边串错误;
12,软件开发中的三个视角:概念(软件要负责什么)、规约(怎么使用软件)、实现(软件怎样履行自己的责任);
13,面向对象范型提倡在编写代码时围绕对象而非函数进行
14,对象应该自己负责自己;
15,与接口对比,抽象可以实现默认行为;
16,抽象可以充当其它类的占位符;
17,封装意味着各种隐藏。
第2章:UML
1,UML是一种用来创建程序模型(程序的图形表示)的图形语言;
2,各种阶段对应的UML图:
分析阶段 用例图(与系统交互的实体及功能特点)、活动图(问题领域的工作流)
观察对象的交互 交互图(说明特定对象是如何交互)
设计阶段 类图(详细描述类之间的关系)
观察对象所处状态不同时行为差异 状态图
配置阶段 部署图
3,类之间的常用关系:is-a(继承),has-a(包含),use-a(使用), new-a(创建)
4,“-”:私有 “+”:公开 “#”:保护 类名用斜体表示抽象类
5,组合和聚集都有“一个对象包含一个或多个对象”,对于被包者来说,前者是是包含对象的一部分,后者更多是一个子集 ;
第二部分:传统面向对象设计的局限
第三章:对代码灵活性要求很高的问题
1,没有必要的继承层次结构的结构往往是紧耦合和低内聚的
2,专家系统是一种采用人类专家总结出的规则进行自动化决策的计算机系统;
第四章:标准的面向对象解决方案
1,“死死盯住球”,不要因为次要的细节而分散注意力;
2,分析陷阱:过早过多地亲关注细节;
3,留意你的直觉;
第三部分:设计模式
第五章:设计模式简介
1,质量能够被客观评价,美可以达成共识;
2, 软件系统的质量可以客观度量;
3,在优秀的设计中具备而在劣质设计中不具备的是什么?
在劣质设计中具备而优秀设计中不具备的是什么?
4,观察解决相似问题的不同结构,可以缩小关注范围,从而看清优秀设计之间的相似之处;
5, 模式:在某一背景下某个问题的一种解决方案;
6, 模式都有一个名称和一个目标;
7,要解决的问题显而易见,但明确说明它们是非常重要的。
8,有时我们会因为太熟悉而看不到一些显而易见的事情;
9,虽然模式经常被认为是理论上的概念,但反映的过去不断出现的问题;
10,存在有一种事实上存在(非常重要),但却容易被忽略的因素;
11,模式四部分:模式的名称、模式的目的、实现方法、实现该模式约束因素
12,几乎所有都存在模式, 而且可以结合起来解决问题;
13,模式的关键特征:名称、意图、问题、解决方案、参与者与协作者、效果、实现、一般结构
14, 模式提供了观察问题、设计过程和面向对象的更高层次的视角;
15,学习设计模式的好处:改善团队的沟通和个人的学习、代码更易于维护和修改、巨型继承层次结构的替代方案;
16,面向对象程序设计的策略:按接口编程、尽量使用聚集替代继承、找出变化并封装之。
第六章:Facade模式
1,意图:一个一致的高层接口--为子系统的一组接口提供一个统一接口;
问题:只需要使用系统的一个子集或以一种特殊的方式与系统交互;
解决方案:Facade为原有的客户提供一个新的接口;
参与者与协作者:接口本身和各个子系统;
郊果:简化了所需子系统的使用过程;
实现:定义一个(或多个)具备所需接口的新类, 让新的类使用原有的系统;
2, 变体:减少客户必须处理的对象数目、一个“封装”层、用新的例程补充原有功能;
3,加多一个外观。
第七章:Adapter模式
1,意图:将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一同工作的类可以一起工作;
问题:系统的数据和行为都正确,但接口不符。通常用于从抽象类派生时;
解决方案:提供了具有所需接口的包装类;
参与者与协作者:Adapter改变了Adaptee的接口,使Adaptee与Adapter的基类Target匹配;
效果: 使有对象能够适应新的类结构,不受其接口的限制;
实现:将原有类包含在新的一个类中,让包含类与需要的接口匹配,调用被包容类的方法;
2, 考虑用对象将遗留系统包装起来,使其更容易使用;
3, Facade模式简化了接口,Adapter模式转换了接口的类。
第八章:开拓视野
1, 项目 传统看法 新的看法
对象 将对象视为数据和方法的简单集合 具有有责任的东西
封装 数据隐藏 隐藏一切东西(实现细节、派生类、设计细节、实例化规则 )的能力
2, 两个步骤构造软件:
1)做一个初步设计,不用操心实现的细节;
2) 实现该软件;
只关注对象的公开接口。
3,关注动机而非实现是设计模式反复提到的主题;
4,类型的封装是通过多态使用具有派生类的抽象类实现的;
5,使用继承来特化的问题: 可能导致弱内聚、减少复用的可能性、无法根据变化很好地伸缩;
6,考滤你的设计中哪些地方可能变化。这种变化与关注会导致重新设计的原因相反。
它不是考虑什么会迫使你的设计变化,而是考虑你怎样才能在不用重新设计的情况下进行变化。
这里的关键在于封装发生变化的概念,这是许多设计模式的主题。
7,很多设计模式都使用封装在对象之间创建层,有利两侧间松耦合;
8,当一个类处理越来越多的不同变化(比如通过开头变量)时,代码的内聚性会变得很差;
9,在真正面向对象的语言中,万事万物都是对象,即使是内置类型,如,int x, y -> x.addto(y)
10,通过使用包含对象来执行所需行为的策略;
11,共性分析:找到变化的地点, 寻找不可能随时间变化而改变的结构
可变性分析:找出如何变化, 找到可能变化的结构
12,抽象类与核心概念: 抽象类代表了将所有派生类联系起来的核心概念(定义了派生类的共性)
共性与要使用的抽象类: 共性定义了需要使用的抽象类
可变性与抽象类的派生类: 在共性中辨别出的可变性将成为抽象类的派生类
规约与抽象类的接口: 对应规约层次
13,抽象类: 需要用什么接口来处理这个 类的所有责任
派生类: 对于这个给定的特定实现,应该怎样根据给定的规约来实现它
14,设计模式提倡从问题域的一些主要概念着手,然后深入,逐步考虑更多的细节;
15,不同的代码品质是密切相关的,如,方法封装,即是解耦;
16,一次且仅一次规则:
1)系统(代码和测试的合称)必须能够表达要表达的一切;
2)系统必须不包含重复代码
17,如果事先考虑以后对代码的测试,确实能够改善设计
18,敏捷编程的品质:无冗余、可读性、可测试性
第九章:Strategy模式
1,灾难往往是因短期未臻最优决策,长期积累而成的;
2,假设变化将会出现,并预测发生的位置;
3,分支的流向开始模糊,每次增加新的功能时,程序员都要进行搜索,有时会遗漏掉某一项;
4,处理相同概念不同任务的实现时的选择:复制粘贴、分支语句、函数指针和委托、继承
5,特化技术最终总是会产生太深的继承层次,导致代码难理解;
6,使用的原则:对象都有责任、这些职责不同的具体实现是通过多态的使用完成、
概念相同的算法具有多个不同的实现,需要进行管理;
7,关键特征:
意图: 可以根据上下文,使用不同的规则或算法
问题: 对所需算法选择取决于发出请求的客户或者要处理的数据
解决方案: 将对算法的选择和对算法的实现分离
参与者与协作者:Strategy指定了如何使用不同的算法
各ConcreteStrategy实现了使用不同的算法
Context通过Strategy的引用类型使用具体的ConcreteStrategy
效果: 定义了一系列算法、可以不用使用switch、必须以相同的方式使用所有的算法;
实现: 让使用算法的类(Context)包含一个抽象类(Strategy),该抽象类有一个
抽象方法指定如何调用算法。每个派生类按需实现算法
第十章:Bridge模式
1,将抽象与其实现解耦,使它们可以独立变化;
2,过度使用继承;
3,寻找原设计的替代方案;
4,把注意力放在模式的上下文中;
5,当存在一个抽象有不同实现时Bridge最为有用,可以使抽象和实现相互独立地进行
6,关键特征:
意图: 将一组实现与另一组使用它们的对象分离
问题: 一个抽象类的派生类必须使用多个实现,但不能出现类数量爆炸性增长
解决方案: 为所有实现定义一个接口,供抽象类的所有派生类使用;
参与者与协作者: Abstraction 为要实现的对象定义接口
Implementor为具体的实现类定义接口
Abstraction的派生类使用Implement的派生类,却无需知道自已具体使用叧一个
ConcreteImplementor
效果: 实现与使用实现的对象解耦,提供了可扩展性,客户对象无需操心实现问题
实现: 将实现封装在一个抽象类中
在要实现的抽象的基类中包含一个实现的句柄
7,重构是一种修改软件系统的过程,它在不改变代码的外在行为的情况下,改进它的内在结构;
第十一章:Abstract Factory 模式
1,为创建一组相关或相互依赖的对象提供一组接口,而且无需指定它们的具体类;
2,关键特征:
意图: 需要为特定的客户(或情况)提供对象组
问题: 需要实例一组相关的对象
解决方案: 协调对象组的创建。提供一组方式,将如何执行对象实例化的规则
从使用这些对象的客户对象提取出来
参与者与协作者:AbstractFactory为如何创建对象组的每个成员定义接口。
一般每个组都由独立的ConcreteFactory进行创建
效果: 这个模式将“使用哪对象”的规则与“如使用这些对象”的逻辑分离开来
实现: 定义一个抽象类来定义指定创建哪些对象,然后为每个组实现一个具体类。
可以用表或文件来完成两样的任务
3,工厂是有职责的,而且是内聚的
第四部分:组合起来--用模式思考
第十二章:专家设计之道
1,着手设计时,应该怎样开始?从细节开始再组合一起?或从高层概念开始,逐一分解?
2,如果只早把预先形成的部分加起来,不可能形成有自然特征的任何东西;
3,每一个部分都因其存在于更大整体的背景中而被赋予特定的形式;
4,开始用最简单的术语来考虑问题(概念性层次),然后添加更多信息(以模式的形式);
5,模式定义了问题域中实体间的关系;
6,找出模式-->从背景模式开始-->然后,从背景模式中转向内部-->改进设计-->实现
第十三章:用模式解决CAD/CAM问题
第五部分:迈向新的设计方式
1,从模式中获得的经验还能够用于创造另一种设计应用程序架构的方式;
第十四章:设计模式的策略与原则
1,理解如何在两个层次更有效地运用设计模式;
2,理解设计模式的本质机制及其背后的原则与策略;
3,开闭原则:模块、方法和类应该对扩展开放,对修改封闭;
4,从背景设计原则:先创建总体概念;
5,依赖倒置原则:高层模块不应该依赖于低层模块, 抽象不应该依赖于细节;
6,Liskov原则:一个从基类派生的类应该支持基类的所有行为--让使用对象
甚至无法知道是否存在派生类;
7,封装变化原则
8,接口的关注点是:要使用这些派生/实现的对象。
9,“这些东西如果要以相同的方式使用,必须都有什么样的公共接口?”
10,理性怀疑原则:模式是一种用作思考的辅助手段,而不是解决问题的处方;
11,常见错误:浮一于表面、偏见、错选、误判、削足适履;
12,模式是被发现出来的,而不是被发明出来的。
第十五章:共性与可变性分析
1,使用CVA找到到系统中的变化;
2,开发人员需要一种方式:首先弄清楚是实体是什么,再弄清实体间的关系;
第十六章:分析矩阵
1,软件开发中最大之一:处理问题域上的变化;
2,方法步骤:
1)找到某种特定情况中最重要的特性,并用矩阵将它们组织起来。
用特性所表示的概念为每个特性标记;
2)继续处理其它情况,按需扩展这个矩阵。处理每一种情况时应独立于
其它情况;
3)用新的概念扩展该分析矩阵;
4)用行发现规则;
5)用列发现特定情况;
6)从这种分析中确定模式;
7)得到高层设计。
3,客户通常无法提供完整的需求,因为他们问题按正常情况考虑问题,
忽视异常情况(而我们却是必须要处理的);
4,客户很擅长回答具体的问题,但对于“还有别的什么吗?”这样的问题,
他们 一般不擅长;
5,客户的几点说明:
1)他们通常非常了解他们的问题;
2)一般情况下,他们不会像开发人员经常的那样在概念层次表示事情;
3)他们经常使用“总是”表达“经常”
4)他们经常使用“从不”表达“很少”
第十七章:Decorator模式
1,建议以需要的正确顺序将所需功能串联起来,进行控制。
2,动态地给一个对象添加一些额外的职责,就增加功能而言,比生成子类更灵活;
3,关键特征:
意图: 动态地给一个对象添加职责;
问题: 要使用的对象将执行所需的基本功能。但是,可能需要为这个
对象添加某些功能,这些附加功能可能发生在基础功能之前或
之后;
解决方案: 可以无需创建子类,而扩展了一个对象的功能;
参与者与协作者: ConcreteComponent让Decorator对象为自己添加功能。有时候
用ConcreteComponent的派生类提供核心功能, 在这种情况下
ConcreteComponent类就不再是具体,而是抽象的。Component
类定义了所有这些类所使用的接口;
效果: 所添加的功能放在小对象中;
实现: 创建一个抽象类来表示原类和要添加到这个类的新功能。在装饰类中,
将对新功能的调用放在对紧随其后对象的调用之前或之后,以获得正确
的顺序
4, 结构本身并非模式
第六部分:其他重要模式
第十八章:Observer模式
1,模式的分类:
类型 意图 例子 用途
创建型 创建或实例化对象 Abstract Factory 实例化对象
结构型 将已有的对象组合起来 Facade、Adapter 处理接口、将已有的对象加入新的设计中
将实现与抽象分离起来
行为型 给出一种灵活行为的方式 Strategy 封装变化
2,定义对象间的一种一对多的依赖关系,当一个对象的状态发生变化时,
所有依赖于它的对象都将得到通知并自动更新;
3,与COM中的通知过程很类似;
4,在C++中可以通过使用单一继承或多重继承,使所有观察者的类型相同;
5,在目标中有两个方法:attach(observer)和detach(observer)
6, 步骤:
1)让观察者以相同的方式工作:继承
2)让观察者注册自己:Subject.attach()
3)事件发生时通知观察者:observer.update(), subject.notify()--遍历观察者
4)从目标获取信息:
7,Observer模式有助于灵活性和解耦;
8,依赖关系事先固定时,使用Observer只会增加复杂性;
9,关键特征:
意图: 在对象之间定义一种一对多依赖关系,这样当一个对象的状改变时,所有依赖者
都将得到通知并自动更新
问题: 当某个事件发生时,需要向一系列变化着的对象发出通知
解决方案: Observer将监视某个事件的责任委托给中心:Subject
参与者与协作者: Subject知道自己的Observer。Subject必须在所监视的事件发生时通知Observer。
Observer负责向Subject注册,以及在得到通知时从Subject外获得信息
效果: 如果某些Observer只对事件的一个子集感兴趣,那么Subject可能告诉它们不需要知
到的事件。如果Subject通知Observer,Observer还返回请求更多信息,则可能需要
额外的通信
实现: 通过注册
第十九章:Template Method模式
1,定义一个操作中算法的骨架,而将一些步骤延迟到子类中。不改变算法的结构而重定义它的步骤;
2,控制不同过程的共同序列;
3,可以减少冗余;
4,Template Method模式不是一组共同协作的Strategy模式的集合;
5,懂得模式有助于提高代码质量而且不会增加工作量;
6,关键特征:
意图: 定义一个操作中算法的骨架,而将一些步骤延迟到子类中。
不改变算法的结构而重定义它的步骤
问题: 要完成在某一细节层次一致的一个过程或一个系列步骤,
但其个别步骤在更详细的层次上的可能实现不同
解决方案: 允许定义可变的子步骤,同时保持基本过程一致
参与者与协作者: 由一个抽象类组成,这个抽象类定义了需要覆盖的基本TemaplateMetod方法。
每个从这个抽象类派生的具体类将为此模板实现新方法
效果: 提供了一个很好的代码利用平台。它还有助于确保所需步骤的实现。它将每个
Concrete类的覆盖步骤绑定起来,因此只有在这些变化问题而且只能一起变化时,
才应该使用Template Method模式
实现: 创建一个抽象类,用抽象方法实现一个过程。这些抽象方法必须在子类实现,
以执行过程的每个步骤。如果这些步骤是独立变化的,那这些步骤还可以使用
Strategy模式
第七部分:各种工厂模式
1, 有助于对象的创建,便并非最重要作用。换个角度会发现更多;
第二十章:来自设计模式的教益:各种工厂模式
1,工厂--创建其它对象的对象;
2,会极大地简化设计和代码;
3,先了解对象,然后再决定如何创建和(或)管理它们;
4,工厂封装了创建对象的业务规则;
5,对象在各种不同时候出于不同原因产生;
6,最好不要在对对象有充分了解之前实例化对象;
7,工厂是用来实例化其他对象的方法、对象或者其他任何实体;
8,使用工厂是隐藏变化的一种自然结果;
9,考虑系统中需要什么,再去关注如何创建系统……我们应该在确定
对象是什么之后再定义工厂;
10,开发的两个方式:
1)定义对象和它们的协作方式
2)编写为相应情况实例化对象,并在对象共享时管理已有对象的工厂
11,对象要么构造其他对象,要么使用其他对象,决不要两者兼顾;
12,使集成更加容易,能封装设计;
13,复杂的创建问题可以通过将行为型和结构型模式结合到工厂逻辑中得到解决;
第八部分:终点与起点
第二十五章:设计模式回顾:总结与新起点
1,设计模式回答了一个基本问题:"为什么以这种方式?";
2,面向对象原则:
1)对象是具有明确定义的责任的事物
2)对象是对自己负责
3)封装指的是任何形式隐藏:数据、实现、类、设计、实例化
4)使用共性和可变性分析抽象出行为和数据中的变化
5)按接口设计
6)将继承看成一种将变化概念化的方法,而不是创建已有对象的特殊情形
7)将一个变化放入一个变化中,并与该类中的其他变化解耦
8)力求松耦合、强内聚
9)将使用一个对象的代码与创建该对象的代码分离
10)在应用“一次且仅一次”规则丼要绝对小心
11)通过“按意图编程”,使用反映意图的名字,确保代码的可读性
12)在编程之前就考虑代码的可测试性
3,对客户对象屏蔽了实现细节;
4,按责任分解问题域:
1)需要哪些对象
2)如何实例化和管理这些对象
5,学习模式,寻找以下约束因素:
1)这个模式隐藏了什么实现?
2)这个模式有什么共性?
3)这个模式中对象的责任是什么?
4)这些对象之间有什么关系?
5)这个模式本身怎样成为背景设计的微观示例?
6,在这最后阶段,模式不再重要……,模式已经教会了你对真实的感悟力。