图解设计模式读书笔记名词解释:一致性
可以将不同的东西当作同一种东西看待。Iterator(迭代器)模式可以无视集合的实现,完成对集合的遍历。可以透明的变更集合的实现。
为了弱化类之间的耦合,进而使得类更容易作为组件被复用,我们需要引入抽象类和接口。
不要只使用具体类来编程,要优先使用抽象类和接口来编程。
Adapter(适配器)模式适配器位于实际情况和需求之间,使实际情况转换为需求所需的模样。
Adapter模式有以下两种:
类适配器模式(使用继承的适配器)对象适配器模式(使用委托的适配器)什么时候使用Adapter模式将,现有的已经经过充分测试的类,进行适配,生成新的类以满足新的需求。
Template Method(模版方法)方法在父类中确定处理的流程,然后将具体处理交给子类。
在父类中定义处理流程的框架,在子类中实现具体处理的模式称为TemplateMethod模式。
模版方法类中的模版方法一般被声明为final,用于不允许被子类覆盖。
Factory Method(工厂方法)模式将Template Method模式用于实例的生成就是Factory Method模式。
抽象类不能用new生成实例。
建议在程序注释和开发文档中记录所使用的设计模式的名称和意图,以免后续维护人员做出违背设计者初衷的改动。
分析设计模式的时候,主要是分析多个类和多个接口之间的相互关系,不应当只看其中的某个类。
Singleton(单例)模式确保任何情况下都绝对只存在一个实例的模式被称为Singleton模式。
Singleton模式需要确保不因人的失误出现意外情况,不能靠通过口口相传或者注释的方式要求其他所有人自觉做到不实例化出多个实例。
Prototype模式通过复制生成实例:不根据类生成实例,而是通过实例生成新实例。
可以用java.lang.Cloneable接口
面向对象编程的目标之一:作为组件复用。
一旦在代码中出现要使用的类的名字,就无法与该类分离开来,也就无法实现复用。即使没有Java文件也能复用该类才是关键。
Builder(构造器)模式组装复杂的实例。
模式中的角色:
Builder(建造者)
负责定义用于生成实例的接口ConcreteBulder(具体的建造者)
Impl了Builder接口的具体的类。Director(监工)
负责使用Builder角色的接口来生成实例。其只调用Builder角色中被定义的方法。一般有个construct方法用于生成实例。只有不知道子类才能替换。
正因为不知道才能够替换,正因为可以替换,组件才具有高价值。
Abstract Factory(抽象工厂)模式将关联零件组装成产品。
抽象工厂的工作是将“抽象零件”组装成“抽象产品”。
“抽象”这个词的含义是指“不考虑具体怎样实现,而是仅关注接口”
我们不关注零件的具体实现,而是只关心接口。我们仅使用接口将零件组装成为产品。
Abstract Factory中的登场角色:
AbstractProduct(抽象产品)
负责定义AbstractFactory角色所生成的抽象零件和产品的接口。AbstractFactory(抽象工厂)
负责定义用于生成抽象产品的接口java的文件夹名称全小写。
Bridge(桥接)模式设计时,将类的功能层次结构和类的实现层次结构分离,然后使用Bridge模式将二者连接起来。
层次结构:类的继承结构。
类的功能层次结构:继承的目的是为了增加功能而产生的层次结构。
类的实现层次结构父类通过声明抽象方法来定义接口子类通过实现具体方法来实现接口AbstractClass
ConcreteClass1ConcreteClass2实现方式通过在功能层次的类中增加一个实现层次结构的类的成员属性,需要使用相关方法时,委托该成员属性调用方法实现。
注意,两个层次不源自同一个接口或类,即实现层次结构的类的祖先接口和功能层次的类祖先类之前没有继承和实现关系。
编写子类的时候要先确认自己的意图:我是要增加功能呢,还是要增加实现呢?
Strategy(策略)模式整体的替换算法的实现部分,可以轻松的用不同的算法去解决同一个问题。
Strategy模式和State模式很相似,但是二者的目的不同:
策略模式的意图:定义一系列算法,把它们一个个封装起来,并且相互之间可以替换。
状态模式的意图:允许一个对象在其内部状态改变的情况下,改变其行为。
Strategy模式是可以方便的用不同的算法解决同一个问题;State模式是关注状态的变更,具体操作都在state接口的具体实现类里(即表示具体状态的类)
Composite(组合/复合物)模式保证容器(比如文件夹)和内容(比如文件)(的外在表现)的一致性。
比如文件类有size方法,文件夹类也有size方法,两个方法调用后的外在表现是一致的(文件夹类的size方法调用文件夹类里所有内容(包括子文件和子文件夹)的size方法获取总size),这样就可以忽略容器和内容在处理上的差异。
登场角色:
Leaf
表示内容的角色(比如文件)Composite(复合物)
表示容器的角色,可以在其中放入Leaf和Composite角色(比如文件夹)Component
接口/父类;使Leaf和Composite角色具有一致性的角色。树结构的数据结构都适用Composite模式。
Decorator(装饰器)模式装饰边框和被装饰物的一致性。
即【被装饰物】(蛋糕)被【装饰器】(奶油装饰-奶油蛋糕|巧克力再装饰-巧克力奶油蛋糕|草莓再装饰-草莓巧克力奶油蛋糕-都还是蛋糕)装饰之后的产物和【被装饰物】是同一类东西(类/接口)。
【装饰器类】和【被装饰物类】继承同一个父类/接口,以达到【装饰边框和被装饰物的一致性】的效果。
Decorator模式的主要目的是通过增加装饰物来增加对象的功能。在不改变被装饰物的前提下增加功能
java.io.Reader的各个实现类是装饰器模式的一个比较典型的实现。
Visitor模式目的是:把处理从数据结构中分离出来。
把对数据的处理和数据结构本身分隔开,数据结构类只关注数据结构,对数据的处理都封装在Visitor类中。
数据结构类的accpet方法,用于“接受”各种不同的数据处理Visitor类(对数据的不同处理算法),accept之后(即在数据结构的accpet方法中)调用Visitor类的visit方法用于处理该数据。
使用重载定义处理不同数据的visit方法;
延伸:双重分发:element和visitor两个角色共同决定了实际进行的处理的分发机制。
开闭原则:面向扩展开放,面向修改关闭:在不改变现有代码(类)的前提下进行拓展
Chain of Responsibility(责任链)模式把多个对象组成一条责任链,然后按照他们在责任链上的顺序一个一个的找出应该谁来负责处理
链条上的一个节点接收到需求,判断自己能不能做,能做则自己处理,不能做就转交下一个节点;下一个节点也是一样的处理;
public abstract class Node { private Node next; // setNext(next).setNext(nextNext)..... private Node setNext(Node next) { this.next = next; return next; } // 模板方法,被申明为final public final void support(Trouble trouble) { if (resolve(trouble)) { done(trouble); } else if (next != null) { next.support(trouble); } else { fail(trouble); } } // 解决问题的方法,不能解决返回false protected abstract boolean resolve(Trouble trouble);}责任链模式弱化了发出请求的人和处理请求的人之间的关系,如果不用这种模式,则需要有一个“伟大的角色”知道“谁应该处理什么请求”。
责任链模式不可避免的会导致处理延迟,所以如果请求和处理者之间的关系是确定的,而且对处理速度有较高要求,则不使用责任链模式会更好;(策略/请求和处理者之间的关系表)
Facade模式“你调用这个方法前要先调用这个方法,然后必须再调用那个方法,不然就会报错”——此时需要简单窗口。
使用Facade模式可以为互相关联在一起的(错综复杂)的类整理出高层接口(API)。其中的Facade角色可以让系统对外只有一个简单的接口(比如:造一栋楼())
Facade模式可以让复杂的东西“看起来简单”(看起来只有一个接口)。
Mediator(仲裁者)模式所有组员之间不互相交流,所有组员都只向仲裁者报告,仲裁者向组员下达指令。
需要协调多个对象之间的关系时,可以用仲裁者模式。
例子:登陆框中各个按钮和输入框是组员,其有各种禁用、错误提示等状态,各个状态的产生原因错综复杂,此时可以有个仲裁者在所有组件状态发生变更的时候检查所有组员的状态,调整所有组员的状态。
充当Mediator角色的类很难复用。
Observer(观察者)模式/Publish-Subscribe(发布订阅)模式发送状态变化通知;
Observer模式中,当观察对象的状态发生变更的时候,会通知给观察者。Observer模式适用于根据对象状态进行相应处理的场景。
【观察者】角色观察【被观察对象(Subject对象)】角色;【被观察对象】角色在状态变更的时候,给所有已注册的【观察者】角色发送通知;
说是“观察”,其实是【被观察者】主动向【观察者】发送通知。
和Mediator模式的差别:
Mediator模式中,发送通知是为了对Colleague角色进行仲裁Observer模式中,将Subject角色的状态变化通知给Observer角色的目的则主要是为了使Subject角色和Observer角色同步。
Memento(备忘录)模式保存对象状态。
Memento模式通过引入表示实例状态的角色,可以在保存和恢复实例时,有效的防止对象的封装性遭到破坏。
使用Memento模式可以实现应用程序的以下功能:
Undo(撤销)Redo(重做)History(历史记录)Snapshot(快照)得抽出关键状态(类似Angular中的组件参数),保存快照,恢复快照。
模式中的角色:Originator(生成者)角色Originator角色会在保存自己的最新状态的时候生成Memento角色。当把以前保存的Memento角色传递给Originator角色时,它会将自己恢复至生成该Memento角色时的状态。
Memento(备忘录)角色Memento角色提供两种接口:提供两种接口:
Wide interface(宽接口):
指所有用于获取恢复对象状态信息的方法的集合。因为宽接口会暴露Memento角色的内部信息,所以只有Originator角色可以使用宽接口Narrow interface(窄接口)
Memento角色为外部调用者提供了窄接口,可以通过窄接口获取有限的内部信息,以防止信息泄露。
State(状态)模式用类表示状态,通过切换类来方便的改变对象的状态。
State模式中的登场角色State(状态)State角色表示状态,定义了根据不同状态进行不同处理的接口。该接口时那些处理内容依赖于状态的方法的集合。
Context(上下文)Context持有State角色(的实现类)。同时定义了供外部调用者使用State模式的接口。
需要注意:谁来管理状态迁移:
State的实现类中,每个实现类处理从当前状态转换为其他状态的所有情况Context模式中,管理所有状态的转换(Mediator模式)状态迁移表记录所有状态的变换条件
Flyweight(轻量级)模式共享对象,避免浪费(线程池)。
Flyweight模式,一言以蔽之就是,“通过尽量共享实例来避免new出新实例(以节约内存)”,即尽量共用已经存在的实例。
Flyweight模式会导致Flyweight角色被共享,所以为了避免被共享角色发生变更导致所有使用共享角色的地方都受到影响,我们只将那些真正应该在多个地方共享的字段定义在Flyweight角色中即可。
Intrinsic(本质的、固有的)信息
不依赖于位置与状况,可以共享Extrinsic(外在的、非本质的)信息
依赖于位置和状况,不能共享
Proxy模式只在必要时生成实例。
为了加快游戏启动速度,启动游戏时只创建最低限度的实例,其他的初始化比较耗时的实例在需要使用时才创建。为了达到此目的,需要Proxy可以代替真实对象完成一些基础操作,直到真实对象不得不出马处理的时候才初始化真实对象。
比如打印机的Proxy对象可以实现设置/获取打印机名称等基础操作,但是需要【打印】操作的时候,Proxy对象就无能为力,必须实例化真实的打印机对象以完成【打印】操作。
Proxy对象和真实对象之间,涉及Proxy对象代理的属性的变更时,需要注意两个对象持有的属性值的一致性(可以使用synchronized关键字)。
Command模式命令也是类。
把每个独立操作(命令)的处理抽象成一个类,这些类可以在对应的操作(命令)发生时重复执行,同时可以组合起来成为新的操作(命令)。
命令(Command)有时候也被称为事件(event),和“事件驱动编程”中的“事件”是一个意思。
Interpreter(翻译/解释器)模式语法规则也是类。
设计模式的目的之一就是提高类的可复用性。可复用性是指不用做太大的修改(甚至不做任何修改)就可以在多种应用场景使用之前编写的类。
Interpreter模式中,程序要解决的的问题会被用非常简单的“迷你语言”表述出来,即用“迷你语言”编写的“迷你程序”把具体的问题表述出来。(公式)
其他protected关键字主要是同一包下的类可以访问,可以用于由各种抽象类/接口组成的设计模式类的封装。
使用设计模式的目的之一就是使类成为可复用的组件。
利用抽象类和接口从具体类中抽象出抽象方法;在将实例作为参数传递至类中,或者再类的字段中保存实例时,(类型定义)不使用具体类型,而是使用抽象类型和接口——这两点组合起来可以帮助我们轻松替换具体类;
MVC:
Model指操作“不依赖与显示形式的内部模型”的部分View是管理Model“怎么显示”的部分通常情况下,一个Model对应多个View
从“设计模式”的角度去看程序:抽象类和接口的作用、继承和委托的使用方法、类和方法的可见性、类的可替换性、不用修改代码即可将类作为组件复用的方法。
如何选择合适的设计模式?
首先要明确的知道自己的程序中存在什么样的问题。如果问题不够明确,是无法选择出合适的设计模式的。
学习设计模式时,我们要注意该模式“可以解决什么问题”。
设计模式就是开发人员对反复遇到的问题总结出来的解决方法。
学习设计模式的重点是理解设计模式是怎么样解决问题的。