《设计模式笔记》
23种设计模式: 1.单例模式 2.工厂方法模式 3.抽象工厂模式 4.模版方法模式 5.建造者模式 6.代理模式 7.原型模式 8.中介者模式 9.命令模式 10.责任链模式 11.装饰模式 12.策略模式 13.适配器模式 14.迭代器模式 15.组合模式 16.观察者模式 17.门面模式 18.备忘录模式 19.访问者模式 20.状态模式 21.解释器模式 22.享元模式 23.桥梁模式
设计模式的应用: 对于一个场合到底用不用模式,这对所有的开发人员来说都是一个很纠结的问题。 有时候,因为预见到需求上会发生的某些变化,为了系统的灵活性和可扩展性而使用了某种设计模式, 但这个预见的需求偏偏没有,相反,没预见到的需求倒是来了不少,导致在修改代码的时候, 使用的设计模式反而起了相反的作用,以至于整个项目组怨声载道。这样的例子, 我相信每个程序设计者都遇到过。所以,基于敏捷开发的原则,我们在设计程序的时候, 如果按照目前的需求,不使用某种模式也能很好地解决,那么我们就不要引入它,因为要引入一种设计 模式并不困难,我们大可以在真正需要用到的时候再对系统进行一下,引入这个设计模式。
参考链接: 猴菇同学的博客:http://blog.csdn.net/qq_31715429/article/category/7258862 卡奴达摩的专栏:http://blog.csdn.net/zhengzhb
====================================================================================================
单例模式: 使用场景: 确保某个类有且只有一个对象的场景,避免产生多个对象消耗过多的资源,或者某种类型的对象只应该有且只有一个。
关键点:
.1)构造函数不对外开放,一般为private,不能通过new的形式手动构造单例类的对象;
.2)通过一个静态方法或者枚举返回单例类对象;
.3)确保单例类的对象有且只有一个,尤其在多线程环境下(synchronized);
.4)确保单例类对象在反序列化时不会重新构建对象。
优点:只有在使用时才会被实例化,在一定程度上节约了资源、 避免对资源的多重占用 可以在系统设置全局的访问点,优化和共享资源访问
缺点:第一次加载时需要及时实例化,反应稍慢;每次调用getInstance都会进行同步,造成不必要的同步开销。 单例对象如果持有Context,那么很容易引发内存泄漏 单例模式一般没有接口,扩展很困难
总结:不管以哪种形式实现单例模式,它们的核心原理都是将构造函数私有化,并且通过静态方法获取一个唯一的实例, 在这个获取的过程中必须保证线程安全、防止反序列化导致重新生成实例对象等问题。选择哪种实现方式 取决于项目本身,如是否是复杂的并发环境、JDK版本是否过低、单例对象的资源消耗等。 单例模式是运用频率很高的模式,但是,由于在客户端通常没有高并发的情况,因此, 选择哪种实现方式并不会有太大的影响。即便如此,出于效率考虑,我们推荐用DCL、静态内部类的形式。
====================================================================================================
构建者模式: 1.介绍: Builder模式是一步一步创建一个复杂对象的创建型模式,它允许用户在不知道内部构建细节的情况下, 可以更精细地控制对象的构造流程。该模式是为了将构建复杂对象的过程和它的部件解耦, 使得构建过程和部件的表示隔离开来。
2.使用场景:
(1) 相同的方法,不同的执行顺序,产生不同的事件结果时。
(2) 多个部件或零件,都可以装配到一个对象中,但是产生的运行结果又不相同时。
(3) 产品类非常复杂,或者产品类中的调用顺序不同产生了不同的作用,这个时候使用建造模式非常合适。
(4) 当初始化一个对象特别复杂,如参数多,且很多参数都具有默认值时。
总结:
Builder模式在Android开发中也较为常用,通常作为配置类的构建器将配置的构建和表示分离开来,
同时也是将配置从目标类中隔离出来,避免过多的setter方法。Builder模式比较常见的实现形
式是通过链式调用实现,每一个setter方法都返回自身,也就是return this,这样使得代码更简洁、易懂
优点
(1) 良好的封装性,使用建造者模式可以使客户端不必知道产品内部组成的细节。
(2) 建造者独立,容易扩展。
缺点 :会产生多余的Builder对象,消耗内存。
====================================================================================================
原型模式: 1.定义: 用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。
2.适用场景
使用原型模式创建对象比直接new一个对象在性能上要好的多,因为Object类的clone方法是一个本地方法,
它直接操作内存中的二进制流,特别是复制大对象时,性能的差别非常明显。
使用原型模式的另一个好处是简化对象的创建,使得创建对象就像我们在编辑文档时的复制粘贴一样简单。
注意事项:
使用原型模式复制对象不会调用类的构造方法。因为对象的复制是通过调用Object类的clone方法来完成的,
它直接在内存中复制数据,因此不会调用到类的构造方法。不但构造方法中的代码不会执行,
甚至连访问权限都对原型模式无效。还记得单例模式吗?单例模式中,只要将构造方法的访问权限
设置为private型,就可以实现单例。但是clone方法直接无视构造方法的权限,所以,单例模式
与原型模式是冲突的,在使用时要特别注意。
浅拷贝:Object类的clone方法只会拷贝对象中的基本的数据类型,对于数组、容器对象、引用对象等都不会拷贝
深拷贝:必须将原型模式中的数组、容器对象、引用对象等另行拷贝
====================================================================================================
工厂模式: 定义: 定义一个用于创建对象的接口,让子类决定实例化哪一个类,工厂方法使一个类的实例化延迟到其子类。
类型:
抽象程度的不同分为三种:简单工厂模式(也叫静态工厂模式)、工厂方法模式、抽象工厂模式
优点:
(1)可以使代码结构清晰,有效地封装变化。在编程中,产品类的实例化有时候是比较复杂和多变的,
通过工厂模式,将产品的实例化封装起来,使得调用者根本无需关心产品的实例化过程,
只需依赖工厂即可得到自己想要的产品。
(2)对调用者屏蔽具体的产品类。如果使用工厂模式,调用者只关心产品的接口就可以了,至于具体的实现,
调用者根本无需关心。即使变更了具体的实现,对调用者来说没有任何影响。
(3)降低了对象之间的耦合度,而且依赖于抽象的架构,其将实例化的任务交由子类去完成,有很好的拓展性。
缺点:每次添加新产品时就要编写新的产品类,同时还要引入抽象层,导致类结构的复杂化。
====================================================================================================
抽象工厂模式: 定义:为创建一组相关或相互依赖的对象提供一个接口,而且无需指定他们的具体类。
抽象工厂模式与工厂方法模式的区别:
抽象工厂模式是工厂方法模式的升级版本,他用来创建一组相关或者相互依赖的对象。他与工厂方法
模式的区别就在于,工厂方法模式针对的是一个产品等级结构;而抽象工厂模式则是针对的多个产
品等级结构。在编程中,通常一个产品结构,表现为一个接口或者抽象类,也就是说,工厂方法模
式提供的所有产品都是衍生自同一个接口或抽象类,而抽象工厂模式所提供的产品则是衍生自
不同的接口或抽象类。
适用场景
当需要创建的对象是一系列相互关联或相互依赖的产品族时,便可以使用抽象工厂模式。
说的更明白一点,就是一个继承体系中,如果存在着多个等级结构(即存在着多个抽象类),并且
分属各个等级结构中的实现类之间存在着一定的关联或者约束,就可以使用抽象工厂模式。假如各个
等级结构中的实现类之间不存在关联或约束,则使用多个独立的工厂来对产品进行创建,
则更合适一点。
总结:
无论是简单工厂模式,工厂方法模式,还是抽象工厂模式,他们都属于工厂模式,在形式和特点上也是极
为相似的,他们的最终目的都是为了解耦。在使用时,我们不必去在意这个模式到底工厂方法模式还是抽
象工厂模式,因为他们之间的演变常常是令人琢磨不透的。经常你会发现,明明使用的工厂方法模式,当新
需求来临,稍加修改,加入了一个新方法后,由于类中的产品构成了不同等级结构中的产品族,它就变
成抽象工厂模式了;而对于抽象工厂模式,当减少一个方法使的提供的产品不再构成产品族之后,它就
演变成了工厂方法模式。
所以,在使用工厂模式时,只需要关心降低耦合度的目的是否达到了。
====================================================================================================
策略模式: 1.定义: 策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们可以在不同场景下相互替换。
2.使用场景:
(1)针对同一类型问题的多种处理方式,仅仅是具体行为有差别时。
(2)需要安全的封装多种同一类型的操作时。
(3)出现同一抽象类有多个子类,而又需要使用if-else或者switch-case来选择具体子类时。
3.例子:
在Android中差值器Interpolator就是一个典型的策略模式,给动画设置差值器,只需要new不同类型的差值器对象。
4.总结:
策略模式主要用来分离算法,在相同的行为抽象下有不同的具体实现策略。这个模式很好地演示了开闭原则,
也就是定义抽象,注入不同的实现,从而达到很好的可扩展性。
优点
• 策略类之间可以自由切换,由于策略类实现自同一个抽象,所以他们之间可以自由切换
• 耦合度相对而言较低,扩展方便;
• 避免使用多重条件,如果不使用策略模式,对于所有的算法,必须使用条件语句进行连接,
通过条件判断来决定使用哪一种算法
• 操作封装也更为彻底,数据更为安全。
缺点
•随着策略的增加,子类也会变得繁多。
====================================================================================================
状态模式: 1.介绍: 状态模式中的行为是由状态来决定的,不同的状态下有不同的行为。状态模式和策略模式的结构几乎完全一样, 但它们的目的、本质却完全不一样。状态模式的行为是平行的、不可替换的,策略模式的行为是彼此独立、 可相互替换的。用一句话来表述,状态模式把对象的行为包装在不同的状态对象里,每一个状态对象都 有一个共同的抽象状态基类。状态模式的意图是让一个对象在其内部状态改变的时候,其行为也随之改变。
2.定义:
当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。
3.使用场景:
1) • 一个对象的行为取决于它的状态, 并且它必须在运行时刻根据状态改变它的行为。
2) • 代码中包含大量与对象状态有关的条件语句:一个操作中含有庞大的多分支的条
件(if else(或switch case)语句,且这些分支依赖于该对象的状态。这个状态通常用一个
或多个枚举常量表示。通常 , 有多个操作包含这一相同的条件结构。 State模式将每一个条
件分支放入一个独立的类中。这使得你可以根据对象自身的情况将对象的状态作为一个对象,
这一对象可以不依赖于其他对象而独立变化。
====================================================================================================
责任链模式: 1.定义: 将一个请求从链式的首端发出,沿着链的路径依次传递给每一个节点对象,直至有对象处理这个请求为止。 使多个对象都有机会处理请求,从而避免了请求的发送者和接受者之间的耦合关系。
2.使用场景:
多个对象可以处理同一请求,但具体由哪个对象处理则在运行时动态决定。
在请求处理者不明确的情况下向多个对象中的一个提交一个请求。
需要动态指定一组对象处理请求。
对于责任链中的一个处理者对象,其只有两个行为,一是处理请求,二是将请求转送给下一个节点,
不允许某个处理者对象在处理了请求后又将请求转送给上一个节点的情况。对于一条责任链来说,
一个请求最终只有两种情况,一是被某个处理对象所处理,另一个是所有对象均未对其处理,
对于前一种情况我们称该责任链为纯的责任链,对于后一种情况我们称为不纯的责任链。
责任链模式在Android源码中比较类似的实现如事件分发和有序广播。
4.总结:
优点:可以对请求者和处理者关系解耦,提高代码灵活性。
缺点:对链中请求处理者的遍历,如果处理者太多那么遍历必定会影响性能,特别是在一些递归调用中。
====================================================================================================
命令模式: 定义: 将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志, 可以提供命令的撤销和恢复功能。
命令模式的结构:
顾名思义,命令模式就是对命令的封装,首先来看一下命令模式类图中的基本结构:
Command类:是一个抽象类,类中对需要执行的命令进行声明,一般来说要对外公布一个execute方法用来执行命令。
ConcreteCommand类:Command类的实现类,对抽象类中声明的方法进行实现。
Client类:最终的客户端调用类。
Invoker类:调用者,负责调用命令。
Receiver类:接收者,负责接收命令并且执行命令。
优点:
首先,命令模式的封装性很好:每个命令都被封装起来,对于客户端来说,需要什么功能就去调用相
应的命令,而无需知道命令具体是怎么执行的。比如有一组文件操作的命令:新建文件、复制文件、
删除文件。如果把这三个操作都封装成一个命令类,客户端只需要知道有这三个命令类即可,至于
命令类中封装好的逻辑,客户端则无需知道。
其次,命令模式的扩展性很好,在命令模式中,在接收者类中一般会对操作进行最基本的封装,命
令类则通过对这些基本的操作进行二次封装,当增加新命令的时候,对命令类的编写一般不是从
零开始的,有大量的接收者类可供调用,也有大量的命令类可供调用,代码的复用性很好。比如,
文件的操作中,我们需要增加一个剪切文件的命令,则只需要把复制文件和删除文件这两个命令组
合一下就行了,非常方便。
缺点:
命令如果很多,开发起来就要头疼了。特别是很多简单的命令 ,实现起来就几行代码的事,
而使用命令模式的话,不用管命令多简单,都需要写一个命令类来封装
适用场景
对于大多数请求-响应模式的功能,比较适合使用命令模式,正如命令模式定义说的那样,
命令模式对实现记录日志、撤销操作等功能比较方便。
====================================================================================================
迭代器模式: 定义:提供一种方法访问一个容器对象中各个元素,而又不暴露该对象的内部细节。
迭代器模式的结构:
抽象容器:一般是一个接口,提供一个iterator()方法,例如java中的Collection接口,List接口,Set接口等。
具体容器:就是抽象容器的具体实现类,比如List接口的有序列表实现ArrayList,List接口的链表实现LinkList,
Set接口的哈希列表的实现HashSet等。
抽象迭代器:定义遍历元素所需要的方法,一般来说会有这么三个方法:取得第一个元素的方法first(),
取得下一个元素的方法next(),判断是否遍历结束的方法isDone()(或者叫hasNext()),
移出当前对象的方法remove(),
迭代器实现:实现迭代器接口中定义的方法,完成集合的迭代。
====================================================================================================
观察者模式: 定义:定义对象间一种一对多的依赖关系,使得当每一个对象改变状态, 则所有依赖于它的对象都会得到通知并自动更新。
观察者模式的结构
被观察者:从类图中可以看到,类中有一个用来存放观察者对象的Vector容器,这个Vector容器是被
观察者类的核心,另外还有三个方法:attach方法是向这个容器中添加观察者对象;
detach方法是从容器中移除观察者对象;notify方法是依次调用观察者对象的对应方法。
这个角色可以是接口,也可以是抽象类或者具体的类,因为很多情况下会与其他的模式混用,
所以使用抽象类的情况比较多。
观察者:观察者角色一般是一个接口,它只有一个update方法,在被观察者状态发生变化时,这个方法就会被触发调用。
具体的被观察者:使用这个角色是为了便于扩展,可以在此角色中定义具体的业务逻辑。
具体的观察者:观察者接口的具体实现,在这个角色中,将定义被观察者对象状态发生变化时所要处理的逻辑。
总结:
观察者模式是一种常用的触发机制,它形成一条触发链,依次对各个观察者的方法进行处理。但同时,
这也算是观察者模式一个缺点,由于是链式触发,当观察者比较多的时候,性能问题是比较令人担忧的。
并且,在链式结构中,比较容易出现循环引用的错误,造成系统假死。
如果要设计一个事件触发处理机制的功能,使用观察者模式是一个不错的选择
====================================================================================================
====================================================================================================