本文旨在快速梳理经常使用的设计模式,了解每一个模式主要针对的是哪些状况以及其基础特征,每一个模式前都有列举出一个或多个能够深刻阅读的参考网页,以供读者详细了解其实现。html
分为三篇文章:java
全复习手册文章导航git
点击公众号下方:技术推文——面试冲刺github
行为型算法
首先搞清楚一点,设计模式不是高深技术,不是奇淫技巧。设计模式只是一种设计思想,针对不一样的业务场景,用不一样的方式去设计代码结构,其最最本质的目的是为了解耦,延伸一点的话,还有为了可扩展性和健壮性,可是这都是创建在解耦的基础之上。spring
高内聚:系统中A、B两个模块进行交互,若是修改了A模块,不影响模块B的工做,那么认为A有足够的内聚。数据库
低耦合:就是A模块与B模块存在依赖关系,那么当B发生改变时,A模块仍然能够正常工做,那么就认为A与B是低耦合的。apache
blog.csdn.net/maoyuanming…设计模式
使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链发送该请求,直到有一个对象处理它为止。
在这种模式中,一般每一个接收者都包含对另外一个接收者的引用。若是一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。
红楼梦中的"击鼓传花"。 JS 中的事件冒泡。 JAVA WEB 中 Apache Tomcat 对 Encoding 的处理,Struts2 的拦截器,jsp servlet 的 Filter,springMVC的拦截器
什么时候使用:在处理消息的时候以过滤不少道。
如何解决:拦截的类都实现统一接口。
关键代码:Handler 里面聚合它本身,在 HandlerRequest 里判断是否合适,若是没达到条件则向下传递,向谁传递以前 set 进去。
处理器的抽象类
package com.mym.designmodel.CoRModel;
/**
* 职责:Handler 职责类的抽象父类
*/
public abstract class AbstractCarHandler {
AbstractCarHandler carHandler = null;
public abstract void carHandler();
public AbstractCarHandler setNextCarHandler(AbstractCarHandler nextCarHandler){
this.carHandler = nextCarHandler;
return this.carHandler;
}
/**职责下传*/
protected void doChain(){
if(this.carHandler != null){
this.carHandler.carHandler();
}
}
}
复制代码
责任链一个执行者1
package com.mym.designmodel.CoRModel;
/**
* 职责:concreteHandler 具体的处理类
*/
public class CarHeadHandler extends AbstractCarHandler {
@Override
public void carHandler() {
System.out.println("处理车的head!");
//下传
this.doChain();
}
}
复制代码
责任链一个执行者2
package com.mym.designmodel.CoRModel;
/**
* 职责:concreteHandler 具体的处理类
*/
public class CarBodyHandler extends AbstractCarHandler {
@Override
public void carHandler() {
System.out.println("处理车的body!");
//下传
this.doChain();
}
}
复制代码
责任链一个执行者3
package com.mym.designmodel.CoRModel;
/**
* 职责:concreteHandler 具体的处理类
*/
public class CarTailHandler extends AbstractCarHandler {
@Override
public void carHandler() {
System.out.println("处理车的tail!");
//下传
this.doChain();
}
}
复制代码
客户端client
package com.mym.designmodel.CoRModel;
/**
* 测试
*/
public class MainClass {
public static void main(String[] args) {
AbstractCarHandler carheadHandle = new CarHeadHandler();
AbstractCarHandler carbodyHandle = new CarBodyHandler();
AbstractCarHandler carTailHandler = new CarTailHandler();
//组装责任链
carheadHandle.setNextCarHandler(carbodyHandle).setNextCarHandler(carTailHandler);
//链头部开始执行
carheadHandle.carHandler();
}
}
复制代码
命令模式是为了解决命令的请求者和命令的实现者之间的耦合关系。
解决了这种耦合的好处我认为主要有两点:
1.更方便的对命令进行扩展(注意:这不是主要的优点,后面会提到)
2.对多个命令的统一控制(这种控制包括但不限于:队列、撤销/恢复、记录日志等等)
struts 1 中的 action 核心控制器 ActionServlet 只有一个,至关于 Invoker,而模型层的类会随着不一样的应用有不一样的模型类,至关于具体的 Command。
所谓解释器模式就是定义语言的文法,而且创建一个解释器来解释该语言中的句子。
这种模式实现了一个表达式接口,该接口解释一个特定的上下文。这种模式被用在 SQL 解析、符号处理引擎等。
编译器、运算表达式计算。
提供一种顺序访问聚合对象元素的方法,而且不暴露聚合对象的内部表示。
优势
①简化了遍历方式,对于对象集合的遍历,仍是比较麻烦的,对于数组或者有序列表,咱们尚能够经过游标来取得,但用户须要在对集合了解很清楚的前提下,自行遍历对象,可是对于hash表来讲,用户遍历起来就比较麻烦了。而引入了迭代器方法后,用户用起来就简单的多了。
②能够提供多种遍历方式,好比说对有序列表,咱们能够根据须要提供正序遍历,倒序遍历两种迭代器,用户用起来只须要获得咱们实现好的迭代器,就能够方便的对集合进行遍历了。
③封装性良好,用户只须要获得迭代器就能够遍历,而对于遍历算法则不用去关心。
缺点
对于比较简单的遍历(像数组或者有序列表),使用迭代器方式遍历较为繁琐,你们可能都有感受,像ArrayList,咱们宁肯愿意使用for循环和get方法来遍历集合。
集中相关对象之间复杂的沟通和控制方式。
Alarm(闹钟)、CoffeePot(咖啡壶)、Calendar(日历)、Sprinkler(喷头)是一组相关的对象,在某个对象的事件产生时须要去操做其它对象,造成了下面这种依赖结构:
使用中介者模式能够将复杂的依赖结构变成星形结构:
在不违反封装的状况下得到对象的内部状态,从而在须要时能够将对象恢复到最初状态。
所谓备忘录模式就是在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象以外保存这个状态,这样能够在之后将对象恢复到原先保存的状态。
一、后悔药。 二、打游戏时的存档。 三、Windows 里的 ctri + z。 四、IE 中的后退。 五、数据库的事务管理。
观察者模式是对象的行为模式,又叫发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知全部观察者对象,使它们可以自动更新本身。
一个对象(目标对象)的状态发生改变,全部的依赖对象(观察者对象)都将获得通知,进行广播通知。
天气数据布告板会在天气信息发生改变时更新其内容,布告板有多个,而且在未来会继续增长。
容许对象在内部状态改变时改变它的行为,对象看起来好像修改了它所属的类。
考虑一个在线投票系统的应用,要实现控制同一个用户只能投一票,若是一个用户反复投票,并且投票次数超过5次,则断定为恶意刷票,要取消该用户投票的资格,固然同时也要取消他所投的票;若是一个用户的投票次数超过8次,将进入黑名单,禁止再登陆和使用系统。
定义一系列算法,封装每一个算法,并使它们能够互换。
策略模式和状态模式的区别:
之因此说状态模式是策略模式的孪生兄弟,是由于它们的UML图是同样的,但意图却彻底不同,**策略模式是让用户指定更换的策略算法,而状态模式是状态在知足必定条件下的自动更换,用户没法指定状态,最多只能设置初始状态。 **
策略模式可让算法独立于使用它的客户端。
假设如今要设计一个贩卖各种书籍的电子商务网站的购物车系统。一个最简单的状况就是把全部货品的单价乘上数量,可是实际状况确定比这要复杂。好比,本网站可能对全部的高级会员提供每本20%的促销折扣;对中级会员提供每本10%的促销折扣;对初级会员没有折扣。
根据描述,折扣是根据如下的几个算法中的一个进行的:
算法一:对初级会员没有折扣。
算法二:对中级会员提供10%的促销折扣。
算法三:对高级会员提供20%的促销折扣。
public static void main(String[] args) {
//选择并建立须要使用的策略对象
MemberStrategy strategy = new AdvancedMemberStrategy();
//建立环境
Price price = new Price(strategy);
//计算价格
double quote = price.quote(300);
System.out.println("图书的最终价格为:" + quote);
}
复制代码
经过让环境类持有一个抽象策略类(超类)的引用,在生成环境类实例对象时,让该引用指向具体的策略子类。再对应的方法调用中,就会经过Java的多态,调用对应策略子类的方法。从而能够相互替换,不须要修改环境类内部的实现。同时,在有新的需求的状况下,也只须要修改策略类便可,下降与环境类之间的耦合度。
工厂模式和策略模式的区别在于实例化一个对象的位置不一样,对工厂模式而言,实例化对象是放在服务端的,即放在了工厂类里面; 而策略模式实例化对象的操做在客户端
工厂模式要求服务端的销售部门足够灵敏,而策略模式因为对策略进行了封装,因此他的销售部门比较傻,须要客户提供足够能区分使用哪一种策略的参数,而这最好的就是该策略的实例了。
典型用例:Spring
模板方法模式是类的行为模式。
准备一个抽象类,将部分逻辑以具体方法以及具体构造函数的形式实现,而后声明一些抽象方法来迫使子类实现剩余的逻辑。不一样的子类能够以不一样的方式实现这些抽象方法,从而对剩余的逻辑有不一样的实现。这就是模板方法模式的用意。
模板方法中的方法能够分为两大类:模板方法和基本方法。
一个模板方法是定义在抽象类中的,把基本操做方法组合在一块儿造成一个总算法或一个总行为的方法。
一个抽象类能够有任意多个模板方法,而不限于一个。每个模板方法均可以调用任意多个具体方法。
基本方法又能够分为三种
抽象方法:一个抽象方法由抽象类声明,由具体子类实现。在Java语言里抽象方法以abstract关键字标示。
具体方法:一个具体方法由抽象类声明并实现,而子类并不实现或置换。
钩子方法:一个钩子方法由抽象类声明并实现,而子类会加以扩展。一般抽象类给出的实现是一个空实现,做为方法的默认实现。
默认钩子方法
一个钩子方法经常由抽象类给出一个空实现做为此方法的默认实现。这种空的钩子方法叫作“Do Nothing Hook”。具体模版类中能够选择是否重写钩子方法,一般重写钩子方法是为了对模版方法中的步骤进行控制,判断钩子方法中的状态,是否进行下一步操做。
模板方法模式是基于继承的代码复用技术,它体现了面向对象的诸多重要思想,是一种使用较为频繁的模式。模板方法模式普遍应用于框架设计中,以确保经过父类来控制处理流程的逻辑顺序(如框架的初始化,测试流程的设置等)。
好比我有一个帐单,帐单有收入,支出两个固定方法。可是访问帐单的人不肯定,有多是一个或者多个。
通常被访问的东西所持有的方法是固定的,就像帐单只有收入和支出两个功能。而访问者是不固定的。
数据操做与数据结构相分离:频繁的更改数据,但不结构不变。好比:虽然每一天帐单的数据都会变化(数据变化),可是只有两类数据,就是支出和收入(结构不变)。
见参考网页
使用什么都不作的空对象来代替 NULL。
一个方法返回 NULL,意味着方法的调用端须要去检查返回值是不是 NULL,这么作会致使很是多的冗余的检查代码。而且若是某一个调用端忘记了作这个检查返回值,而直接使用返回的对象,那么就有可能抛出空指针异常。
更多精彩文章,请查阅个人博客或关注个人公众号:Rude3Knife
全复习手册文章导航
点击公众号下方:技术推文——面试冲刺
知识点复习手册文章推荐
我是蛮三刀把刀,目前为后台开发工程师。主要关注后台开发,网络安全,Python爬虫等技术。
来微信和我聊聊:yangzd1102
Github:github.com/qqxx6661
同步更新如下博客
1. Csdn
拥有专栏:Leetcode题解(Java/Python)、Python爬虫开发、面试助攻手册
2. 知乎
拥有专栏:码农面试助攻手册
3. 掘金
4. 简书
若是文章对你有帮助,不妨收藏起来并转发给您的朋友们~