大话设计模式-读书笔记

面向对象:

需求变换是必然的,那么尽管无法预测会发生什么,但通常可以预测哪里会发生变化,面向对象的优点之一是可以封装这些变化的区域,从而更容易的将代码与变化产生的影响隔离开。代码可以逐步演进,新代码可以影响较少的加入。经典的设计模式是可以复用的面向对象设计方法。

------------------书的配套code分析,回头补上

 

1. 策略模式

简单工厂模式的工厂类包含了必要的逻辑判断,根据客户端的选择动态的实例化相关的类。需要让客户端认识两个类,一个是super基类(例如加法操作的基类-运算符类),一个是Factory工厂类(返回基类,以便利用多态)

策略模式与简单工厂模式结合,客户端只需要一个Context对象(类似Factory工厂类),调用其函数(复合的super类对象调用多态函数),这样大大降低耦合性,在客户端连父类super类都可以隐藏

 

2. 单一职责原则

如果能够想到多于一个动机去改变一个类,那么这个就多于一个职责,最好考虑类的职责分离,从而易维护,易扩展,易复用,更加灵活

 

3. 开放-封闭原则

(类,模块,函数等等)应该可以扩展,但不可修改。对扩展开放,对更改封闭。考虑到为了需求得修改,尽量使当前类封闭,如果需要修改当前类,需要重新对类抽象。

 

4. 依赖倒转原则

抽象不应该依赖于细节,细节应该依赖于抽象。针对接口编程,不要对实现编程。有点像Restful API使得前后端分离,前后端只关注接口即可。

5. 装饰模式

Component定义了一个对象接口,可以给具体ConcreteComponent对象动态添加职责。

如果系统的新需求仅仅是为了满足一些只在某种特定情况下才会执行的特殊行为,那么可以选用装饰模式,它把每个要装饰的功能放在单独的类中,并让这个类包装它所要修饰的对象。客户代码可以有选择的,有顺序的使用装饰功能包装的对象。

 

5. 代理模式

为其他对象提供一种代理以控制对这个对象的访问,即代理模式在访问对象时引入一定程度的间接性,从而可以附加多种用途。

代理类中会实例化原始类,并在相应接口中调用实例化原始对象的相应函数

 

6. 工厂方法模式

例: 左边是操作符类(Product), 加法类(ConcreteProduct),右边是Factory类(Creator), 返回ConcreteProduct实例的ConcreteCreator类

相比简单工厂模式,在添加新需求时更加符合开放-封闭原则,不用在Factory类中修改case分支。

概念:定义一个用于创建对象的接口,让子类决定实例化哪一个类,工厂方法使一个类的实例化延迟到其子类,简单工厂模式则在基类中用case分支实例化

 

7. 原型模式

this.MenberwiseClone()函数将创建一个新对象,然后将当前对象的非静态字段复制到该新对象,如果是值类型则逐位复制,引用则复制引用但不复制引用的对象,即指向同一个对象。

Clone使性能大幅提高,一般在初始化信息不变,clone是最好的方法,即隐藏了对象创建的过程,又省略初始化构造函数的执行过程。

注意深拷贝和浅拷贝问题,如果需要的类中实例化了其他复合类,需要在复合类中调用MenberwiseClone,然后将其他值字段复制过来

 

8. 模板方法模式

所有重复的代码都应该上升到父类去。

当要完成在某一细节层次一致的一个过程,但其个别步骤在更详细层次上的实现可能不同,常考虑模板方法。

定义一个操作骨架,将一些步骤延迟到子类中,模板方法使子类可以不改变基类的结构即可重新定义该基类的某些特定函数。

在基类中把可变的部分声明虚函数,让子类去继承重写,最后利用多态。

 

9. 迪米特法则

 如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用,如果其中一个类需要调用另一个类的某一个方法,可以通过第三者(接口)转发这个调用。

类之间的耦合越弱,越利于复用,一个处在弱耦合的类被修改,不会对有关系的类造成波及。

 

10. 外观模式

为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。

外观Facade类,可以为设计粗糙或高度复杂的遗留代码提供相对清晰和简单的接口,让新系统与Facade对象交互,Facede与遗留代码交互所有复杂的工作,相当于包了一层,提供简单接口。

 

11. 建造者模式

当需要将一个复杂的对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示时,可以考虑建造者(生成器)模式。(这些对象内部构建顺序是稳定的,但对象内部的构建通常面临复杂的变话)

抽象类描述需要建造的所有函数,派生类继承后实现这些函数,指挥者类控制建造过程,从而隔离用户与建造过程的关联,用户不会出现由于失误少建造对象的一部分。

 

12. 观察者模式

定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主体对象,主体对象在状态发生变化时,会通知所有观察对象,使其自动更新自己。

当一个对象的改变需要同时改变其他对象的时候,需要考虑观察者模式。观察者模式所做的工作就是解耦,让耦合的双方都依赖于抽象,而不是依赖于具体,从而使各自的变化都不会影响另一边的变化。

 委托:

去掉抽象观察者类,将update声明为委托,在客户端将需要通知的不同函数挂在委托update函数上。委托可以看作是对函数的抽象,是函数的类,委托搭载的方法并不需要属于同一个类。

 

13. 抽象工厂模式 

提供一个创建一系列相关或相互依赖对象的接口(抽象),而无需指定它们具体的类。

eg: AbstructProductA和B分别表示两个数据库中的表类(读写),派生类1和2分别代表不同数据库的具体实现类。

简单工厂模式中的switch判断实例化不同的类,可以用反射技术(设置字符串变量)替换,解除分支判断带来的耦合

 

14. 状态模式

当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。

当控制一个对象状态转换的条件表达式过于复杂时,把状态的判断逻辑转移到表示不同状态的一系列类中,可以把复杂的判断逻辑简化。

 将条件分支改写成一个个不同的状态类,并在类内处理状态转换,使得实例化后的Context对象改变属性后利用多态可以实现不同状态之间的转换。

 

15. 适配器模式

 将一个类的接口转换成客户希望的另外一个接口,Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

系统的数据和行为都正确,但接口不符时,应该考虑用适配器,目的是使控制范围之外的一个原有对象与某个接口匹配,适配器模式主要应用于希望复用一些现存的类,但是接口与复用环境要求不一致的情况。

 

16. 备忘录模式

 在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样以后就可将该对象恢复到原先保存的状态。

Memento类用来存储需要备份的信息,Caretaker是状态管理者类,内涵Memento属性用于备份。

 

17. 组合模式

将对象组合成树形结构以表示“部分-整体”的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性。

透明:Leaf和Composite有一致的接口,只不过Leaf不实现Add和Remove。构成一棵树形结构

组合模式让客户可以一致的使用组合结构和单个对象。

 

18. 迭代器模式

提供一种方法顺序访问一个聚合对象中各个元素,而又不暴露该对象的内部表示。

 ConcreteIterator类可以实现不同的迭代方式

迭代器模式就是分离了集合对象的遍历行为,抽象出一个迭代器类来负责。这样既可以做到不暴露集合的内部结构,又可让外部代码透明的访问集合内部的数据。

 

19. 单例模式
保证一个类仅有一个实例,并提供一个访问它的全局访问点。让类自身负责保存它的唯一实例,这个类可以保证没有其他实例可以被创建,并且它可以提供一个访问该实例的方法。

构造函数设为private使其在外部无法被实例化,类内声明public静态类变量,和getInstance静态类方法,如果类变量为空则在类内调用private构造函数实例化,否则返回静态类变量。

多线程中的单例模式

(1) 需要采用双重锁定,先check getInstance函数中唯一实例是否已经创建,没有创建再加互斥锁

 

这两个判断都是必须的,第一个是避免全部加锁节约时间,第二个判断是多线程保持单例的必要步骤

 (2) 饿汉式,静态初始化式,它是类一加载就实例化的对象,不会面临多线程访问的安全性问题。

 

20. 桥接模式

合成/聚合复用原则:尽量使用合成/聚合,尽量不要使用类继承。

继承是一种强耦合,父类变,子类必须变。继承适用于is-a的关系,而不是任何时候都去使用。聚合适用于has-a的关系。

定义:将抽象部分与它的实现部分分离,使它们都可以独立的变化。(手机品牌和手机软件分离)

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

Abstraction类中复合有Implementor类变量

实现系统可能有多角度分类,每一种分类都有可能变化,那么就把这种多角度分离出来让它们独立变化,减少它们之间的耦合。

 

21. 命令模式

 将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作

eg:Invoker 服务员类(有一个list存一系列命令类的实例化对象),Receiver 厨师类

支持撤销/恢复操作

 

22. 职责链模式

 使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。

successor用来存后继对象。解决大量分支判断造成难维护、灵活性差的问题。

 

23. 中介者模式

用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式的相互引用,从而使其耦合松散,而且可以独立的改变它们之间的交互。

 ConcreteMediator类内需要有所有需要交互的Colleague实例化对象,在中介者类内完成交互通知。

 

24. 享元模式

运用共享技术有效的支持大量细粒度的对象

可以向ConcreteFlyweight对象调用过程中引入外部对象,将该状态传递给它。

享元模式可以避免大量非常相似类的开销。有时需要大量细粒度的类实例表示数据,如果发现这些实例除了几个参数外基本上都是相同的,有时就能大幅减少需要实例化的类的数量。如果能把那些参数移到类实例外面,在方法调用时将它们传递进来,就可以通过共享大幅度的减少单个实例的数目。比如围棋,五子棋,都有大量的求棋子对象,它们的内部状态是颜色,外部状态是棋子位置,需要把坐标传递给棋子实例。

 

25. 解释器模式

给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。

正则表达式和浏览器都有应用该模式,将每一条规则用一个类表示,在客户端遍历语法字符串,对每一个字符实例化对应的规则对象,利用多态调用该对象的解释函数。

 

26. 访问者模式

表示一个作用于某对象结构中的各元素的操作,它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。

 

 这里用了双分派技术(实现处理与数据结构的分离),如果有新的需求,那么只需要增加一个“状态”子类,就可以在客户端调用来查看,不需要改动其他任何类的代码。

双分派:客户程序中将具体状态(ConcreteVisitor1)作为参数传递给ConcreteElementA类完成一次分派,然后ConcreteElementA类调用作为参数的(ConcreteVisitor1)中的函数VisitConcreteElementA,同时将自己(this)作为参数传递进去,这变完成第二次分派。它得到执行的操作不仅决定于状态了类的具体状态,还决定于它访问的具体对象的类别(ConcreteElementA或B)。

访问者模式把数据结构和作用于结构上的操作之间的耦合解脱开,使操作集合可以相对自由的演化,使增加操作变得容易。适用于有比较稳定的数据结构,又有易于变化的算法。但使得增加新的数据结构变难了,需要改动很多类。

 

posted @ 2019-07-30 23:36  demianzhang  阅读(294)  评论(0编辑  收藏  举报