本文由 简悦 SimpRead 转码, 原文地址 www.cnblogs.com
设计模式总结
- 设计模式学习
23 种设计模式学习下来感觉非常非常的难。更何况是在很短的时间内集中学习设计模式。想要通过这么短的时间学习完成设计模式是不可能的。在学习过程中很多例子都是依葫芦画瓢,其理解的深度并不是很深入。我任务学习设计模式并不是一朝一夕的事情。单学习过程中给我的启发确实很大 。用组合、继承等演绎出各种各样的设计方式。
- 设计模式总结
1. 单例模式
模式定义 : 保证一个类仅有一个实例,并提供一个访问它的全局访问点。
模式痛点 : 当您想控制实例数目,节省系统资源的时候。
设计原则 : 违反单一职责原则,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。
解决方案 : 判断系统是否已经有这个单例,如果有则返回,如果没有则创建。
关键代码 : 构造函数是私有的。
封装变化 : 无
模式优点 : 1. 节省资源, 节省内存资源,减少无谓 GC 的消耗.
2. 防止冲突, 如果一个项目中有多个实例,会造成系统冲突。保证同一时间状态唯一。
模式缺点 : 没有接口,不能继承,
使用场景 : 应用中有对象需要是全局的且唯一。
注意事项 :在以为是单例的情况下,却产生了多个实例。
相关模式 : 原型模式:单例模式是只有一个实例,原型模式每拷贝一次都会创造一个新的实例。
2. 代理模式
模式定义 : 为其他对象提供一种代理以控制对这个对象的访问。
模式痛点 : 在直接访问对象时带来的问题,想在访问一个类时做一些控制。
设计原则 : 遵循组合 / 聚合复用原则(Composite/Aggregate Reuse Principle CARP)
解决方案 : 增加中间层。
关键代码 : 实现与被代理类组合。
封装变化 : 静态代理没有变化点,动态代理的变化点为具有相同切入点的类
模式优点 : 1、访问控制 2、高扩展性。
模式缺点 : 1.jdk 默认代理方式必须实现接口。2. 由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。 2、实现代理模式需要额外的工作,有些代理模式的实现非常复杂。
使用场景 : 需要修改或屏蔽某一个或若干个类的部分功能,复用另外一部分功能,可使用静态代理,若是需要拦截一批类中的某些方法,在方法的前后插入一些一致的操作,假设这些类有一致的接口,可使用 JDK 的动态代理,否则可使用 cglib
注意事项 :切入点的不稳定。
相关模式 :
1、适配器模式:适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口。
2、装饰器模式:装饰器模式为了增强功能,而代理模式是为了加以控制。
3. 工厂方法模式
模式定义 : 定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。
模式痛点 : 接口选择的问题,我们明确地计划不同条件下创建不同实例时。
设计原则 : 遵循单一职责、依赖倒置、开闭原则
解决方案 : 让其子类实现工厂接口,返回的也是一个抽象的产品。
关键代码 : 创建过程在其子类执行。
封装变化 : 工厂与产品的种类
模式优点 : 1、一个调用者想创建一个对象,只要知道其名称就可以了。 2、扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。 3、依赖接口,屏蔽产品的具体实现,调用者只关心产品的接口。
模式缺点 : 每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。
使用场景 : 作为一种创建类模式,在任何需要生成复杂对象的地方,都可以使用工厂方法模式。有一点需要注意的地方就是复杂对象适合使用工厂模式
注意事项 :简单对象创建使用工厂方法模式会增加系统的复杂度。
相关模式 :
抽象工厂模式:工厂方法模式与抽象工厂模式最大的区别在于,在工厂方法模式中,工厂创造的是一个产品,而在抽象工厂模式中,工厂创造的是一个产品族。
3. 抽象工厂模式
模式定义 : 提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
模式痛点 : 主要解决接口选择的问题,系统的产品有多于一个的产品族,而系统只消费其中某一族的产品。
设计原则 : 遵循单一职责、依赖倒置、开闭原则
解决方案 : 在一个产品族里面,定义多个产品。
关键代码 : 在一个工厂里聚合多个同类产品。
封装变化 : 工厂与产品的种类。
模式优点 : 当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。
模式缺点 : 产品族扩展非常困难,要增加一个系列的某一产品,既要在抽象的 Creator 里加代码,又要在具体的里面加代码。
使用场景 : 需要一个接口可以提供一个产品族,且不必知道产品的具体种类
注意事项 :产品族难扩展,产品易扩展。
相关模式 : 建造者模式:两者都是建造一批对象或者说产品,不同的是两者的目的和实现手段,在建造者模式中,是为了复用对象的构建过程而定义了一个指挥者,而在抽象工厂模式中,是为了提供一个这批对象的创建接口而定义了抽象工厂接口。
5. 观察者模式
模式定义 : 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
模式痛点 : 一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。
设计原则 : 遵循迪米特、开闭原则
解决方案 : 使用面向对象技术,可以将这种依赖关系弱化。
关键代码 : 在抽象类里有一个 ArrayList 存放观察者们。
封装变化 : 观察者的种类与个数。
模式优点 : 1、观察者和被观察者是抽象耦合的。 2、建立一套触发机制。
模式缺点 : 1、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。 2、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。 3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
使用场景 : 需要将观察者与被观察者解耦或者是观察者的种类不确定。
注意事项 :观察者之间有过多的细节依赖。
相关模式 :
适配器模式:可能需要适配器模式来帮助完成功能。
6. 策略模式
模式定义 : 定义一系列的算法, 把它们一个个封装起来, 并且使它们可相互替换。
模式痛点 : 在有多种算法相似的情况下,使用 if…else 所带来的复杂和难以维护。客户端是否依赖于某一个或若干个具体的策略。
设计原则 : 遵循单一职责、依赖倒置、迪米特、开闭原则
解决方案 : 一个系统有许多许多类,而区分它们的只是他们直接的行为。将这些算法封装成一个一个的类,任意地替换。
关键代码 : 实现同一个接口。
封装变化 : 策略的种类
模式优点 : 1、算法可以自由切换。 2、避免使用多重条件判断。 3、扩展性良好。
模式缺点 : 1、策略类会增多。 2、所有策略类都需要对外暴露。
使用场景 : 算法或者策略需要经常替换
注意事项 :如果一个系统的策略多于四个,就需要考虑使用混合模式,解决策略类膨胀的问题。
相关模式 :
状态模式:策略模式就像是每次都可以切换新的算法,状态模式就是在固定的几种算法之间切换。
模板方法模式:策略模式替换的是整个算法,而模板方法模式替换的是部分算法。
7. 适配器模式
模式定义 : 将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
模式痛点 : 主要解决在软件系统中,常常要将一些 “现存的对象” 放到新的环境中,而新环境要求的接口是现对象不能满足的。
设计原则 : 遵循开闭原则、组合 / 聚合复用原则
解决方案 : 继承或依赖。
关键代码 : 适配器继承或依赖已有的对象,实现想要的目标接口。
封装变化 : 无
模式优点 : 1、可以让任何两个没有关联的类一起运行。 2、提高了类的复用。 3、增加了类的透明度。 4、灵活性好。
模式缺点 : 1、过多地使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是 A 接口,其实内部被适配成了 B 接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。 2. 由于 JAVA 至多继承一个类,所以至多只能适配一个适配者类,而且目标类必须是抽象类。
使用场景 : 需要使用一个类的功能,但是该类的接口不符合使用场合要求的接口,可使用定制适配器,又或者是有一个接口定义的行为过多,则可以定义一个缺省适配器,让子类选择性的覆盖适配器的方法
注意事项 :适配器不是在详细设计时添加的,而是解决正在服役的项目的问题。
相关模式 : 装饰器模式:对于适配器模式中的定制适配器与装饰器模式,二者都是使用组合加继承的手段,不同的是,适配器模式的目的在于适配接口,装饰器模式的目的在于动态的添加功能,且可以叠加。
8. 模板方法模式
模式定义 : 定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
模式痛点 : 一些方法通用,却在每一个子类都重新写了这一方法。
设计原则 : 破坏里氏替换,组合 / 聚合复用原则
解决方案 : 将这些通用算法抽象出来。
关键代码 : 在抽象类实现,其他步骤在子类实现。
封装变化 : 算法骨架内各个步骤的具体实现
模式优点 : 1、封装不变部分,扩展可变部分。 2、提取公共代码,便于维护。 3、行为由父类控制,子类实现。
模式缺点 : 每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大。
使用场景 : 1、有多个子类共有的方法,且逻辑相同。 2、重要的、复杂的方法,可以考虑作为模板方法。
注意事项 :为防止恶意操作,一般模板方法都加上 final 关键词。
相关模式 : 无
9. 装饰器模式
模式定义 : 动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。
模式痛点 : 在不想增加很多子类的情况下扩展类。一般的,我们为了扩展一个类经常使用继承方式实现,由于继承为类引入静态特征,并且随着扩展功能的增多,子类会很膨胀。
设计原则 : 遵循迪米特、单一职责、开闭原则,破坏里氏替换,组合 / 聚合复用原则
解决方案 : 将具体功能职责划分,同时继承装饰者模式。
关键代码 : 1、Component 类充当抽象角色,不应该具体实现。 2、修饰类引用和继承 Component 类,具体扩展类重写父类方法。
封装变化 : 动态添加的功能或者说装饰器
模式优点 : 装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。
模式缺点 : 多层装饰比较复杂。
使用场景 : 一个类需要动态的添加功能,且这些功能可以相互叠加
注意事项 :可代替继承。
相关模式 : 无
10. 外观模式
模式定义 : 为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
模式痛点 : 降低访问复杂系统的内部子系统时的复杂度,简化客户端与之的接口。1、客户端不需要知道系统内部的复杂联系,整个系统只需提供一个 “接待员” 即可。 2、定义系统的入口。
设计原则 : 遵循迪米特
解决方案 : 客户端不与系统耦合,外观类与系统耦合。
关键代码 : 在客户端和复杂系统之间再加一层,这一层将调用顺序、依赖关系等处理好。
封装变化 : 无
模式优点 : 1、减少系统相互依赖。 2、提高灵活性。 3、提高了安全性。
模式缺点 : 不符合开闭原则,如果要改东西很麻烦,继承重写都不合适。
使用场景 : 1、为复杂的模块或子系统提供外界访问的模块。 2、子系统相对独立。 3、预防低水平人员带来的风险。
注意事项 :子系统对外提供的服务的变化或子系统本身的不稳定。
相关模式 : 中介者模式:二者都是为了处理复杂的耦合关系,不同的是外观模式处理的是类之间复杂的依赖关系,中介者模式处理的是对象之间复杂的交互关系
11. 命令模式
模式定义 : 将一个请求封装成一个对象,从而使您可以用不同的请求对客户进行参数化。
模式痛点 : 在软件系统中,行为请求者与行为实现者通常是一种紧耦合的关系,但某些场合,比如需要对行为进行记录、撤销或重做、事务等处理时,这种无法抵御变化的紧耦合的设计就不太合适。
设计原则 : 遵循迪米特、开闭原则
解决方案 : 通过调用者调用接受者执行命令,顺序:调用者→接受者→命令。
关键代码 : 定义三个角色:1、received 真正的命令执行对象 2、Command 3、invoker 使用命令对象的入口
封装变化 : 命令的种类
模式优点 : 1、降低了系统耦合度。 2、新的命令可以很容易添加到系统中去。
模式缺点 : 使用命令模式可能会导致某些系统有过多的具体命令类。
使用场景 : 行为的请求者与行为的处理者耦合度过高。
注意事项 : 系统需要支持命令的撤销 (Undo) 操作和恢复 (Redo) 操作,也可以考虑使用命令模式,见命令模式的扩展。
相关模式 :
职责链模式:容易将二者关联在一起的原因是,二者都是为了处理请求或者命令而存在的,而且二者都是为了将请求者与响应者解耦,不同的是命令模式中,客户端需要知道一个命令的接受者,在创建命令的时候就把接受者与命令绑定在一起发送给调用者,而职责链模式中,客户端并不关心最终处理请求的对象是谁,客户端只是封装一个请求对象,随后交给职责链的头部而已,也正因为这样,二者的实现方式,有着很大的区别
12. 中介者模式
模式定义 : 用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
模式痛点 : 多个类相互耦合,形成了网状结构。对象与对象之间存在大量的关联关系,这样势必会导致系统的结构变得很复杂,同时若一个对象发生改变,我们也需要跟踪与之相关联的对象,同时做出相应的处理。
设计原则 : 遵循迪米特,破坏单一职责。
解决方案 : 将上述网状结构分离为星型结构。
关键代码 : 对象 Colleague 之间的通信封装到一个类中单独处理。
封装变化 : 对象之间的交互
模式优点 : 1、降低了类的复杂度,将一对多转化成了一对一。 2、各个类之间的解耦。 3、符合迪米特原则。
模式缺点 : 中介者会庞大,变得复杂难以维护。
使用场景 : 一个系列的对象交互关系十分复杂。
1、系统中对象之间存在比较复杂的引用关系,导致它们之间的依赖关系结构混乱而且难以复用该对象。 2、想通过一个中间类来封装多个类中的行为,而又不想生成太多的子类。
注意事项 :复杂的交互关系是否有共性可被中介者承担。中介者承担责任重大。
相关模式 : 无
13. 桥接模式
模式定义 : 将抽象部分与实现部分分离,使它们都可以独立的变化。
模式痛点 : 实现系统可能有多个角度分类,每一种角度都可能变化
设计原则 : 遵循单一职责、迪米特、开闭原则,体现功能复用
解决方案 : 把这种多角度分类分离出来,让它们独立变化,减少它们之间耦合。
关键代码 : 抽象类依赖实现类。
封装变化 : 维度的扩展与增加。
模式优点 : 1、抽象和实现的分离。 2、优秀的扩展能力。 3、实现细节对客户透明。
模式缺点 : 桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程。
使用场景 : 一个对象有多个维度的变化,需要将这些维度抽离出来,让其独立变化
注意事项 :对于两个独立变化的维度,使用桥接模式再适合不过了。
相关模式 : 无
- 建造者模式
模式定义 : 将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。
模式痛点 : 一些基本部件不会变,而其组合经常变化的时候。
设计原则 : 遵循单一职责、开闭原则
解决方案 : 将变与不变分离开。
关键代码 : 建造者:创建和提供实例,导演:管理建造出来的实例的依赖关系。
封装变化 : 产品的表示
模式优点 : 1、建造者独立,易扩展。 2、便于控制细节风险。
模式缺点 : 1、产品必须有共同点,范围有限制。 2、如内部变化复杂,会有很多的建造类。
使用场景 : 1、需要构建一批构建过程相同但表示不同的产品,而构建过程非常复杂。2、需要生成的对象具有复杂的内部结构。 3、需要生成的对象内部属性本身相互依赖。
注意事项 :指挥者不能正常工作。
相关模式 :
工厂模式:建造者模式更加关注与零件装配的顺序。
- 备忘录模式
模式定义 : 在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。
模式痛点 : 很多时候我们总是需要记录一个对象的内部状态,这样做的目的就是为了允许用户取消不确定或者错误的操作,能够恢复到他原先的状态,使得他有 “后悔药” 可吃。
设计原则 : 遵循迪米特、开闭原则
解决方案 : 通过一个备忘录类专门存储对象状态。
关键代码 : 客户不与备忘录类耦合,与备忘录管理类耦合。
封装变化 : 无
模式优点 : 1、给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态。 2、实现了信息的封装,使得用户不需要关心状态的保存细节。
模式缺点 : 消耗资源。如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存。
使用场景 : 1、需要保存 / 恢复数据的相关状态场景。 2、提供一个可回滚的操作。
注意事项 :1、为了符合迪米特原则,还要增加一个管理备忘录的类。 2、为了节约内存,可使用原型模式 + 备忘录模式。
相关模式 : 1、为了符合迪米特原则,还要增加一个管理备忘录的类。 2、为了节约内存,可使用原型模式 + 备忘录模式。
16. 迭代器模式
模式定义 : 提供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示。
模式痛点 : 不同的方式来遍历整个整合对象。遍历一个聚合对象。
设计原则 : 遵循迪米特
解决方案 : 把在元素之间游走的责任交给迭代器,而不是聚合对象。
关键代码 : 定义接口:hasNext, next。
封装变化 : 容器对象的种类
模式优点 : 1、它支持以不同的方式遍历一个聚合对象。 2、迭代器简化了聚合类。 3、在同一个聚合上可以有多个遍历。 4、在迭代器模式中,增加新的聚合类和迭代器类都很方便,无须修改原有代码。
模式缺点 : 由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。
使用场景 : 1、访问一个聚合对象的内容而无须暴露它的内部表示。 2、需要为聚合对象提供多种遍历方式。 3、为遍历不同的聚合结构提供一个统一的接口。
注意事项 :迭代器模式就是分离了集合对象的遍历行为,抽象出一个迭代器类来负责,这样既可以做到不暴露集合的内部结构,又可让外部代码透明地访问集合内部的数据。
相关模式 :
访问者模式:二者都是迭代的访问一个聚合对象中的各个元素,不同的是,访问者模式中,扩展开放的部分在作用于对象的操作上,而迭代器模式中,扩展开放的部分在聚合对象的种类上,而且二者的实现方式也有着很大的区别。
17. 享元模式
模式定义 : 运用共享技术有效地支持大量细粒度的对象。
模式痛点 : 1、系统中有大量对象。 2、这些对象消耗大量内存。 3、这些对象的状态大部分可以外部化。 4、这些对象可以按照内蕴状态分为很多组,当把外蕴对象从对象中剔除出来时,每一组对象都可以用一个对象来代替。 5、系统不依赖于这些对象身份,这些对象是不可分辨的。
设计原则 : 无
解决方案 : 用唯一标识码判断,如果在内存中有,则返回这个唯一标识码所标识的对象。
关键代码 : 用 HashMap 存储这些对象
封装变化 : 无
模式优点 : 大大减少对象的创建,降低系统的内存,使效率提高。
模式缺点 : 提高了系统的复杂度,需要分离出外部状态和内部状态,而且外部状态具有固有化的性质,不应该随着内部状态的变化而变化,否则会造成系统的混乱。
使用场景 : 1、系统有大量相似对象。 2、需要缓冲池的场景。
注意事项 :1、注意划分外部状态和内部状态,否则可能会引起线程安全问题。 2、这些类必须有一个工厂对象加以控制。3. 没有将外部状态提取完全
相关模式 : 无
18. 组合模式
模式定义 : 将对象组合成树形结构以表示 “部分 - 整体” 的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
模式痛点 : 1. 树枝和叶子实现统一接口,树枝内部组合该接口。2,您想表示对象的部分 - 整体层次结构(树形结构)。 3. 您希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。
设计原则 : 遵循依赖倒置、开闭原则,破坏接口隔离
解决方案 : 树枝和叶子实现统一接口,树枝内部组合该接口。
关键代码 : 树枝内部组合该接口,并且含有内部属性 List,里面放 Component。
封装变化 : 节点的数量
模式优点 : 1、高层模块调用简单。 2、节点自由增加。
模式缺点 : 在使用组合模式时,其叶子和树枝的声明都是实现类,而不是接口,违反了依赖倒置原则。
使用场景 : 当有一个结构可以组合成树形结构,且需要向客户端提供一致的操作接口,使得客户端操作忽略简单元素与复杂元素
注意事项 :结构不稳定或结构中的节点有递归关系, 对外提供一致操作接口的结构是否可转化为树形结构.
相关模式 : 无
19. 职责链模式
模式定义 : 避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。
模式痛点 : 在处理消息的时候以过滤很多道。职责链上的处理者负责处理请求,客户只需要将请求发送到职责链上即可,无须关心请求的处理细节和请求的传递,所以职责链将请求的发送者和请求的处理者解耦了。
设计原则 : 遵循迪米特
解决方案 : 在处理消息的时候以过滤很多道。
关键代码 : 拦截的类都实现统一接口。
封装变化 : 处理链的长度与次序
模式优点 : 1、降低耦合度。它将请求的发送者和接收者解耦。 2、简化了对象。使得对象不需要知道链的结构。 3、增强给对象指派职责的灵活性。通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任。 4、增加新的请求处理类很方便。
模式缺点 : 1、不能保证请求一定被接收。 2、系统性能将受到一定影响,而且在进行代码调试时不太方便,可能会造成循环调用。 3、可能不容易观察运行时的特征,有碍于除错。
使用场景 : 一个请求的处理需要多个对象当中的一个或几个协作处理
注意事项 :1、有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定。 2、在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。 3、可动态指定一组对象处理请求。
相关模式 : 无
- 访问者模式
模式定义 : 主要将数据结构与数据操作分离。
模式痛点 : 稳定的数据结构和易变的操作耦合问题。需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作 “污染” 这些对象的类,使用访问者模式将这些封装到类中。
设计原则 : 遵循开闭原则
解决方案 : 在被访问的类里面加一个对外提供接待访问者的接口。
关键代码 : 在数据基础类里面有一个方法接受访问者,将自身引用传入访问者。
封装变化 : 数据结构之上的操作
模式优点 : 1、符合单一职责原则。 2、优秀的扩展性。 3、灵活性。
模式缺点 : 1、具体元素对访问者公布细节,违反了迪米特原则。 2、具体元素变更比较困难。 3、违反了依赖倒置原则,依赖了具体类,没有依赖抽象。
使用场景 : 作用于一个数据结构之上的操作经常变化
注意事项 :数据结构的不稳定
相关模式 : 无
21. 原型模式
模式定义 : 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
模式痛点 : 在运行期建立和删除原型。
设计原则 : 无
解决方案 : 利用已有的一个原型对象,快速地生成和原型对象一样的实例。
关键代码 : 1、实现克隆操作,在 JAVA 继承 Cloneable,重写 clone() 方法来实现对象的浅拷贝或通过序列化的方式来实现深拷贝。 2、原型模式同样用于隔离类对象的使用者和具体类型(易变类)之间的耦合关系,它同样要求这些 “易变类” 拥有稳定的接口。
封装变化 : 无
模式优点 : 1、性能提高。 2、逃避构造函数的约束。
模式缺点 : 1、配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类不是很难,但对于已有的类不一定很容易,特别当一个类引用不支持串行化的间接对象,或者引用含有循环结构的时候。 2、必须实现 Cloneable 接口。 3、逃避构造函数的约束。
使用场景 : 1、资源优化场景。 2、类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。 3、性能和安全要求的场景。 4、通过 new 产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。 5、一个对象多个修改者的场景。 6、一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用。 7、在实际项目中,原型模式很少单独出现,一般是和工厂方法模式一起出现,通过 clone 的方法创建一个对象,然后由工厂方法提供给调用者。原型模式已经与 Java 融为浑然一体,大家可以随手拿来使用。
注意事项 :在以为是深度拷贝的情况下,却未实现深度拷贝。
相关模式 : 无
- 状态模式
模式定义 : 允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类。
模式痛点 : 对象的行为依赖于它的状态(属性),并且可以根据它的状态改变而改变它的相关行为。
设计原则 : 遵循单一职责、依赖倒置、开闭原则
解决方案 : 将各种具体的状态类抽象出来。
关键代码 : 通常命令模式的接口中只有一个方法。而状态模式的接口中有一个或者多个方法。而且,状态模式的实现类的方法,一般返回值,或者是改变实例变量的值。也就是说,状态模式一般和对象的状态有关。实现类的方法有不同的功能,覆盖接口中的方法。状态模式和命令模式一样,也可以用于消除 if…else 等条件选择语句。
封装变化 : 状态的种类
模式优点 : 1、封装了转换规则。 2、枚举可能的状态,在枚举状态之前需要确定状态种类。 3、将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。 4、允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。 5、可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。
模式缺点 : 1、状态模式的使用必然会增加系统类和对象的个数。 2、状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。 3、状态模式对 “开闭原则” 的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态,而且修改某个状态类的行为也需修改对应类的源代码。
使用场景 : 一个对象在多个状态下行为不同,且这些状态可互相转换
注意事项 : 在行为受状态约束的时候使用状态模式,而且状态不超过 5 个。
相关模式 :
策略模式:二者的实现方式非常相似,策略接口与状态接口,具体的策略与具体的状态以及二者都拥有的上下文,如果看它们的类图,会发现几乎一模一样,而二者不同的地方就在于,状态模式经常会在处理请求的过程中更改上下文的状态,而策略模式只是按照不同的算法处理算法逻辑,而且从实际场景来讲,顾名思义,状态模式改变的是状态,策略模式改变的是策略
- 解释器模式
模式定义 : 给定一个语言,定义它的文法表示,并定义一个解释器,这个解释器使用该标识来解释语言中的句子。
模式痛点 : 对于一些固定文法构建一个解释句子的解释器
设计原则 : 遵循单一职责
解决方案 : 构件语法树,定义终结符与非终结符。
关键代码 : 构件环境类,包含解释器之外的一些全局信息,一般是 HashMap。
封装变化 : 语言的规则
模式优点 : 1、可扩展性比较好,灵活。 2、增加了新的解释表达式的方式。 3、易于实现简单文法。
模式缺点 : 1、可利用场景比较少。 2、对于复杂的文法比较难维护。 3、解释器模式会引起类膨胀。 4、解释器模式采用递归调用方法。
使用场景 : 1、可以将一个需要解释执行的语言中的句子表示为一个抽象语法树。 2、一些重复出现的问题可以用一种简单的语言来进行表达。 3、一个简单语法需要解释的场景。
注意事项 :可利用场景比较少,JAVA 中如果碰到可以用 expression4J 代替。语言的规则无限制增长或规则十分不稳定。
相关模式 : 无
- 各个设计模式的区别
设计模式学习完成后,为了巩固学习成果。我将不断编写各个设计模式之间的用法区别。
- 设计模式的几点心得
- 如果项目结构能随意变化,那么设计模式就不存在意义.
- 设计模式是在需求更改的情况下维持最大程度的代码复用。在不改变结构的情况下增加新的业务。
- 封装变化的代码、复用不变化的代码。变化在那里封装就在那里。
- 其实任何的解除耦合都是要付出代价的。解除了耦合必然增加复杂性。从来就没有一种完美的解决方案,只有合适不合适。
- 其实你看 jdk 很多代码都是设计模式,只不过天天用感受不到。有的时候天天这么写,回头一看才发现,实际上符合了某种设计模式的样板。
设计模式的意义就在于给某种写法,定义了一个明确的名称。增加同行之间的沟通效率。 - 学习心得
- 动手
学习设计模式一定要动手,光看一点用都没有。你要深入到代码中去感觉代码数据传递方式。去感受继承和组合的配合发挥作用。这些是不能靠眼睛去看能决绝的。想要深入其中只有亲自去写去实践。楼主很多设计模式死活弄不明白。比如访问者模式,我就把 demo 弄出来一遍一遍的 “抄作业”。抄的过程中突然灵光一闪就开窍了。这个过程真的很重要。别人代替不了你。一遍一遍写的过程中是不断在强化你对该设计模式结构理解的程度。
2.UML 类图
uml 类图的解释能力很强,学习设计模式要学会看 uml 图,这样能让你的理解设计模式理解的更加深入。
- 区别
再次强调设计模式是用来封装变化的。一定要将设计模式的变化点搞清楚,这点非常重要,甚至重要程度高于设计模式的场景、实现方式以及类和对象之间的耦合关系,很多时候,设计模式的滥用就是因为变化点没搞清楚,以至于该变化的没变化,不该变化的经常变化,增加系统的负担。 - 应用
了解开源项目中代码的设计模式,去看看里面是怎么设计软件结构的。
如果在工作中,看看项目中哪些设计能用到设计模式。这么用能解决什么问题。但是一定要记住不要轻易尝试使用设计模式。在你对业务不清晰了解之前对项目中变化的结构并不能很好的观察到。滥用设计模式会造成灾难性的后果。
引用
http://www.cnblogs.com/zuoxiaolong/p/pattern26.html
http://www.runoob.com/design-pattern/singleton-pattern.html