Java设计模式
创建型模式:都是用来创建对象的。(关注的是对象的创建过程)
单例模式
- 五种单例模式
- 饿汉式单例 (立即加载实例)
- 饿汉式单例在类加载时就创建一个静态的对象供外使用,除非系统重新启动,这个对象不会改变;,所以本身就是线程安全。
- Singleton通过将构造器设置为private避免了类在外部被实例化,在同一个虚拟机里,Singleton唯一实例只能通过getInstance()方法访问。
- 懒汉式单例(延时加载实例)
- 虽然用延迟加载方式实现了懒汉式单例,但在多线程环境下会产生多个single对象。
- 在方法上加synchronized同步锁或是用同步代码块对类加同步锁,此种方式虽然解决了多个实例对象问题,但是该方式运行效率却很低下,下一个线程想要获取对象,就必须等待上一个线程释放锁之后,才可以继续运行。
- 使用双重检查进一步做了优化,可以避免整个方法被锁,只对需要锁的代码部分加锁,可以提高执行效率。
- 静态内部类实现
- 静态内部类虽然保证了单例在多线程并发下的线程安全性,但是在遇到序列化对象时,默认的方式运行得到的结果就是多例的。
- 静态内部类在创建实例是无法传递参数。
- static静态代码块实现
- 内部枚举类实现
工厂模式
简介:实现了创建者和调用者的分离,工厂模式一般用于当我们创建复杂对象时,通过简单的new会比较麻烦,
这个时候我们就可以使用工厂模式来创造一个工厂类,我们只需要向工厂类中传入需要创建的类的信息即可,工厂类中
实现创建对象的复杂细节。例如:我们创建一个制造汽车的工厂类,我们传入不同的车型号,制造出不同的车。
- 饿汉式单例 (立即加载实例)
- 核心本质:
- 实例化对象,用工厂方法代替new操作
- 将选择实现类、创建对象统一管理和控制。从而实现调用者和我们的实现类解耦。
- 面向对象设计的基本原则
- OCP(开闭原则,Open-Close Principle):一个软件的实体应当对扩展开放,对修改关闭。
- DIP(依赖倒转原则): 要针对接口编程,不要针对实现编程。
- LoD(迪米特原则):只与直接的朋友通信,而避免和陌生人通信。
- 简单工厂模式
- 用来生产同一等级结构中的任意产品。(对于增加新的商品,需要修改已有代码)
- 简单工厂模式也叫静态工厂模式,就是工厂类一般使用静态方法,通过接收的参数不同来返回不同的对象实例。
- 对于增加产品无能为力!不修改代码的话,是无法扩展的。
- 工厂方法模式
- 用来生产同一等级结构中的固定产品。(支持增加任意产品)
- 为了避免简单工厂模式的缺点,不完全满足OCP
- 工厂方法模式和简单工厂模式的最大的不同是:简单工厂模式只有一个(对于一个项目或一个独立模块而言)工厂
类,而工厂方法模式有一组实现了相同接口的工厂类。 - 结论:根据设计理论建议:工厂方法模式。但是实际上,我们一般都是用简单工厂模式。
- 抽象工厂模式
- 用来生产不同产品族的全部产品。(对于增加新的产品,无能为力,支持增加产品族)
- 抽象工厂模式是工厂方法模式的升级版本,在有多个业务品种、业务分类时,通过抽象工厂模式产生需要的对象
是一种非常好的解决方式。
- 工厂方法模式与简单工厂模式PK
- 结果复杂度,工厂方法模式比简单工厂模式复杂。
- 代码复杂度,工厂方法模式比简单工厂模式复杂。
- 客户端编程难度,工厂方法模式比简单工厂模式难。
- 管理上的难度,工厂方法模式比简单工厂模式难。
- 应用场景
- JDK中的calendar的getInstance方法;
- JDBC中Connection对象的获取
- Hibernate中SessionFactory创建Session
- spring中IOC容器创建管理bean对象
- XML解析式的DocumentBuilderFactory创建解析器对象
- 反射中Class对象的newInstance()
建造者模式
- 场景
- 我们要创建一个复杂的产品,比如:神舟飞船、Iphone。这个复杂的产品的创建。有这样一个问题要解决 :
- 装备这些组建是不是有个步骤?
- 实际开发中,我们需要的对象构建时,也非常复杂,有很多步骤需要处理时
- 我们要创建一个复杂的产品,比如:神舟飞船、Iphone。这个复杂的产品的创建。有这样一个问题要解决 :
- 建造模式的本质:
- 分离了对象子组件的单独构造(由Builder来负责)和装配(由Director负责)。从而可以构造出复杂的对象,这个
模式适用于构建过程复杂的情况下使用。 - 由于实现了构建和装配的解耦。不同的构造器,相同的装配也可以做出不同的对象,相同的构造器,不同的装配顺
序也可以做出不同的对象。也就是实现了构建算法、装配算法的解耦,实现了更好的复用。
- 分离了对象子组件的单独构造(由Builder来负责)和装配(由Director负责)。从而可以构造出复杂的对象,这个
- 应用场景
- 场景
- 思考一下:克隆技术是怎么样的过程?克隆羊多利还记得吗?
- javascript语言中,继承怎么实现?那里面也有prototype,还记得吗?
- 原型模式:
- 通过new产生一个对象需要非常繁琐的数据准备或访问权限,即可以使用原型模式
- 就是java中的克隆技术,以某个对象为原型,复制出新的对象,显然,新的对象具有原型对象的特点。
- 优势有:效率高(直接克隆,避免重新执行构建过程步骤)。
- 克隆类似于new,但是不同于new。new创建新的对象属性采用的是默认值。克隆出来的对象的属性
的值与原型对象相同,并且克隆出来的新对 象改变不会影响原型对象。然后,再修改克隆对象的值。
- 原型模式的实现
- Cloneable接口和clone()方法。
- prototype中实现起来最困难的地方就是内存复制操作,所幸在java中提供了clone()方法替我们做了绝大部分的事情!
- 深克隆:就是复制时连属性也一起复制。
- 浅克隆:反之就是浅复制。
- 利用序列化和反序列化技术实现深克隆!
- 注意用词:克隆和拷贝一回事!!!
- 开发中的应用场景
- 核心作用:是从程序的结构上实现松耦合,从而可以扩大整体的类,用来解决更大的问题。
- 分类:适配器模式,代理模式,桥接模式,装饰模式,组合模式,外观模式,享元模式
适配器(adapter)模式
- 什么是适配器模式?
- 将一个类的接口转换成客户希望的另一个接口,Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以在一起
工作。
- 将一个类的接口转换成客户希望的另一个接口,Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以在一起
- 模式中的角色
- 目标接口(Target):客户所期待的接口。目标可以是具体的或者是抽象的类,也可以是接口。
- 需要适配的类(Adaptee):需要适配的类或适配者类。
- 适配器(Adapter):通过包装一个需要适配的对象,把原接口转换成目标接口。
- 适配器分类
- 类适配器:通过继承实现。
- 属性适配器:通过构造器传入需要适配的对象。
- 工作中的场景
- 经常用来做旧系统的改造很升级
- 如果我们的系统开发之后再也不需要维护,那么很多模式都是没必要的,但是不幸的是,事实却是维护一个 系统的代价
往往是开发一个系统的数倍。
- 我们学习中见过的场景
- 核心作用:
- 通过代理,控制对对象的访问!
- 可以详细控制访问某个(某类)对象,在调用这个方法前做前置处理,调用这个方法后做后置处理。(即:AOP的微观实现)
- AOP(Aspect Oriented Programming面向切面编程)的核心实现机制。
- 通过代理,控制对对象的访问!
- 核心角色
- 抽象角色
- 定义真实角色和代理角色的公共对外方法。
- 真实角色
- 实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。
- 关注真正的业务逻辑。
- 代理角色
- 实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。
- 将统一的流程控制放到代理角色中处理。
- 抽象角色
- 应用场景:
- 安全代理:屏蔽对真实角色的直接访问。
- 远程代理:通过代理类处理远程方法的调用(RMI)。
- 延迟代理:先加载轻量级的代理对象,真正需要再加载真实对象。
- 分类:
- 静态代理(静态定义代理类)
- 动态代理(动态生成代理类)
- JDK自带的动态代理。
- java.lang.reflect.InvocationHandler:需要指定处理器。
- javaassist自己码操作库的实现。
- CGLIB。
- ASM(底层使用指令,可维护性较差)。
- JDK自带的动态代理。
- 实际应用场景
- 多层继承结构
- 问题:
- 扩展性问题(类个数膨胀问题) - 违反单一职责原则
- 问题:
- 桥接模式核心要点
- 处理多层继承结构,处理多维度变化的场景,将各个维度设计成独立的继承结构,使各个维度可以独立的扩展在抽象层建立关联。
- 桥接模式在实际开发中应用场景
- 使用组合模式的场景
- 把部分和整体的关系用树形结构来表示,从而使客户端可以使用统一的方式处理部分对象和整体对象。
- 组合模式核心
- 抽象构件(Component)角色:定义了叶子和容器构建的共同点。
- 叶子(Leaf)构件角色:无子节点。
- 容器(Composite)构件角色:有容器特征,可以包含子节点。
- 组合模式在实际开发中应用场景
- 职责
- 动态的为一个对象增加新的功能
- 装饰模式是一种用于代替继承的技术,无须通过继承增加子类就能扩展对象的新功能。使用对象的关联关系代替继承关系,更加灵活
同时避免类型体系的快速膨胀。
- 实现细节
- Compoent抽象构件角色
- 真实对象和装饰对象具有接口。这样,客户端对象就能够以与真实对象相同的方式同装饰对象交互。
- ConcreteComponent具有构件角色(真实对象)
- io流中的FileInputStream、FileOutputStream
- Decorator装饰角色:
- 持有一个抽象构件的引用。装饰对象接收所有客户端的请求,并把这些请求转发给真实的对象,这样,就能在真实对象调用前后增加新的
功能。
- 持有一个抽象构件的引用。装饰对象接收所有客户端的请求,并把这些请求转发给真实的对象,这样,就能在真实对象调用前后增加新的
- ConcreteDecorator具体装饰角色:
- 负责给构件对象增加新的责任。
- Compoent抽象构件角色
- 开发中的应用场景:
- IO中输入流和输出流的设计
- Swing包中图形界面构件功能
- Struts2中,request、response、session对象处理
- Servlet API中提供了一个request对象的Decorator设计模式的默认实现类HttpServletRequestWrapper,HttpServletRequestWrapper类,增强了request对象的功能。
- 装饰模式与桥接模式的区别
- 装饰模式是对原来的对象动态增加新的功能,桥接模式关注的是多维度,即具有部分的不稳定。
- 优点:装饰类与装饰类之间相互独立,不会相互耦合,装饰模式是继承的一种代替模式,动态地为对象增加新的功能或者撤销功能。
- 缺点:会产生过多的相似的对象,不容易拍错;多层装饰比较复杂。
外观模式(decorator)
- LoD(迪米特原则):只与直接的朋友通信,而避免和陌生人通信。
享元模式(flyweight)
- 场景:
- 内存属于稀缺资源,不需要随便浪费。如果有很多个完全相同或相似的对象,我们可以通过享元模式,节省内存。
- 核心
- 享元模式以共享的方式高效低支持大量细粒度对象的重用。
- 享元对象能做到共享的关键是区分了内部状态和外部状态。
- 内部状态:可以共享,不会随环境变化而改变。 - 外部状态:不可以共享,会随环境变化而变化。
- 享元模式实现
- FlyWeightFactory享元工厂类
- 创建并管理享元对象,享元池一般设计成键值对。
- FlyWeight抽象享元类
- 通常是一个接口或抽象类,声明公共方法,这些方法可以向外界提供对象的内部状态,设置外部状态
- ConcreteFlyWeight 具体享元类
- 为内部状态提供成员变量进行存储。
- UnShareConcreteFlyWeight非共享享元类
- 不能被共享的子类可以设计为非共享类。
- FlyWeightFactory享元工厂类
- 在实际中的应用
- 数据库连接池,线程池等
- String类的设计也使用到了享元模式。
- 优点
- 如果有很多个完全相同或相似的对象,我们可以通过享元模式,节省内存。
- 元模式以共享的方式高效低支持大量细粒度对象的重用。
- 缺点
- 定义:
- 将能够处理同一类请求的对象练成一条链,所提交的请求沿着类传递,链上的对象逐个判断是否有能力处理该请求,如果能则处理,如果不能则
传给链的下一个对象。
- 将能够处理同一类请求的对象练成一条链,所提交的请求沿着类传递,链上的对象逐个判断是否有能力处理该请求,如果能则处理,如果不能则
- 场景:
- 打牌时,轮流出牌
- 接力赛跑
- 大学中,奖学金审批
- 公司中,公文审批
- 开发中常见的场景
= Java中,异常机制就是一种责任链模式。一个try可以对应于多个catch,当第一个catch不匹配类型时,则自动跳到第二个catch。
= JavaScript语言中,事件的冒泡与捕获机制。Java语言中,事件的处理采用采用观察者模式。
= Servlet开发中,过滤连的链式处理。
= Struts2中,拦截器的调用也是典型的责任链模式迭代器模式(iterator)
- 场景:
- 提供一种可以遍历聚合对象的方式。又称为;游标cursor模式
- 聚合对象:存储数据
- 迭代器:便利数据
- 基本案例
- 实现正向遍历的迭代器
- 实现逆向遍历的迭代器
- 开发中常见的场景
- JDK内置的迭代器(List/Set)
中介者模式(Mediator)
- 场景:
- 核心
- 如果一个系统中对象之间的联系呈现为网状结构,对象之间存在大量多对多关系,将导致关系及其复杂,这些对象称为“同事对象”。
- 我们可以引入一个中介者对象,使各个同事对象只跟中介者对象打交道,将复杂的网络结构化解为如下的星型结构。
- 中介者模式的本质
- 解耦多个同时之间的交互关系。每个对象都持有中介者对象的引用,只跟中介者对象打交道。我们通过中介者对象统一管理这些交互iu关系。
- 开发中常见的场景
- MVC模式(其中的C,控制器就是一个中介者模式。M和V都和他打交道)
- 窗口游戏程序,窗口软件开发中窗口对象也是一个中介者对象。
- 图形界面开发GUI中,多个组件之间的交互,可以通过引入一个中介者对象来解决,可以是整体的窗口对象或者DOM对象。
- java.lang.reflect.Method#invoke()
七大原则:开闭原则,理氏替换原则,依赖倒置原则,迪米特原则,单一职责原则,接口隔离原则,合成复用原则
命令模式
- 介绍:将一个请求封装为一个对象,是发出请求的责任和执行请求的责任分割开。这样两者之间通过命令对象进行沟通,这样方
便将命令对象进行存储,传递,调用,增加和管理。又可以称为动作Action模式、事务Transaction模式- 命令模式的主要优点如下:
- 降低系统的耦合度。命令模式能将调用操作的对象与实现该操作的对象解耦。降低系统的耦合度。命令模式能将调用操作的对象与实现该操作的对象解耦。
- 增加或删除命令非常方便。采用命令模式增加与删除命令不会影响其他类,它满足“开闭原则”,对扩展比较灵活。
- 可以实现宏命令。命令模式可以与组合模式结合,将多个命令装配成一个组合命令,即宏命令。
- 方便实现 Undo 和 Redo 操作。命令模式可以与后面介绍的备忘录模式结合,实现命令的撤销与恢复。
- 命令模式的结构
- 抽象命令类 Command
- 具体命令角色 ConcreteCommand
- 发起者/申请者 Invoker
- 接收者 Receiver
- 客户端 Client
- 开发中的常用场景
- Struts2中,action的整个调用过程中就有命令模式。
- 数据库事务机制的底层实现。
- 命令的撤销和恢复。
解释器模式(Interprete)
- 介绍
- 是一种不常见的设计模式
- 用于描述如何构成一个简单的语言解释器,主要用于使用面向对象语言开发的编译器和解释器设计。
- 当我们需要开发一种新的语言时,可以考虑使用解释器模式。
- 尽量不要使用解释器模式,后期维护有很大麻烦。在项目中,可以使用Jruby,Groovy,java的js引擎来替代解释器的作用,弥补Java语言的
不足。- 开发中常见的场景
- El表达式的处理。
- 正则表达式解释器。
- SQL语法解释器。
- 数学表达式解释器。
访问者模式(Visitor)
- 模式动机
- 对于存储在一个集合中的对象,他们可能具有不同的类型(即使有一个公共的接口),对于该集合中对象,可以接手一类称为
访问者的对象来访问,不同访问者其访问方式也有所不同。- 定义
- 将作用于某种数据结构中的各元素的操作分离出来封装成独立的类,使其在不改变数据结构的前提下可以添加作用于这些元素的新的操作,
为数据结构中的每个元素提供多种访问方式。它将对数据的操作与数据结构进行分离,是行为类模式中最复杂的一种模式.- 访问者(Visitor)模式是一种对象行为型模式,其主要优点如下。
- 扩展性好。能够在不修改对象结构中的元素的情况下,为对象结构中的元素添加新的功能。
- 复用性好。可以通过访问者来定义整个对象结构通用的功能,从而提高系统的复用程度。
- 灵活性好。访问者模式将数据结构与作用于结构上的操作解耦,使得操作集合可相对自由地演化而不影响系统的数据结构
- 符合单一职责原则。访问者模式把相关的行为封装在一起,构成一个访问者,使每一个访问者的功能都比较单一。
- 访问者(Visitor)模式的主要缺点如下:
- 增加新的元素类很困难。在访问者模式中,每增加一个新的元素类,都要在每一个具体访问者类中增加相应的具体操作,这违背了“开闭原则”.
- 破坏封装。访问者模式中具体元素对访问者公布细节,这破坏了对象的封装性。
- 违反了依赖倒置原则。访问者模式依赖了具体类,而没有依赖抽象类。
- 访问者模式的结构:
- 抽象访问者(Visitor)角色:定义一个访问具体元素的接口,为每个具体元素类对应一个访问操作 visit() ,该操作中的参数类型标识了被访问的具体元素。
- 具体访问者(ConcreteVisitor)角色:实现抽象访问者角色中声明的各个访问操作,确定访问者访问一个元素时该做什么.
- 抽象元素(Element)角色:声明一个包含接受操作 accept() 的接口,被接受的访问者对象作为 accept() 方法的参数.
- 具体元素(ConcreteElement)角色:实现抽象元素角色提供的 accept() 操作,其方法体通常都是 visitor.visit(this) ,另外具体元素中可能还包含本身业务逻辑的相关操作。
- 对象结构(Object Structure)角色:是一个包含元素角色的容器,提供让访问者对象遍历容器中的所有元素的方法,通常由 List、Set、Map 等聚合类实现。
- 开发中的场景
- XML文档解析器设计
- 编译器的设计
- 复杂集合对象的处理
策略模式(Strategy)
- 场景
- 某个市场人员接到单后的报价策略(CRM系统中常见的问题)。报价策略很复杂,可以简单做如下分类:、
- 普通客户小批量报价
- 普通客户大批量报价
- 老客户小批量报价
- 老客户大批量报价
- 具体选用哪个报价策略,这需要根据实际情况来决定。这时候,我们策略模式即可。
- 定义:策略模式对应于解决某一个问题的一个算法族,允许用户从该算法族中任选一个算法解决某一问题,同时可以方便的更换算法或者增加新的
算法。并且由客户端决定调用哪个算法。- 策略模式的主要优点:
- 多重条件语句不易维护,而使用策略模式可以避免使用多重条件语句。
- 策略模式提供了一系列可重用的算法族,恰当使用继承可以把算法族的公共代码转移到父类,从而避免重复代码。
- 策略模式可以提供相同的行为的不同的实现方式,客户可以根据不同的时间或空间要求选择不同的策略。
- 策略模式提供了开闭原则的完美支持,可以在不修改原代码的条件下,灵活增加新的算法。
- 策略模式把算法的使用放到环境中, 而算法的实现移到具体的实现策略类上,实现了二者的分离。
- 策略模式的缺点
- 客户端必须理解所有策略算法的区别,以便适时选择恰当的算法类。
- 策略模式造成了很多的策略类。
- 策略模式的结构
- 抽象策略(Strategy)类:定义了一个公共接口,各种不同的算法以不同的方式实现这个接口,环境角色使用这个接口调用不同的算法,一般
使用接口或抽象类实现。- 具体策略(Concrete Strategy)类:实现了抽象策略定义的接口,提供具体的算法实现。
- 环境(Context)类:持有一个策略类的引用,最终给客户端调用。
- 本质:分离算法
- 开发中常见的场景
- JAVASE中CUI编程中,布局管理
- Spring框架中,Resource接口,资源访问策略
- javax.servlet.http.HttpServlet#service();
模板方法模式(template method)
- 场景
- 客户到银行办理业务:。
- 取号排队。
- 办理具体现金/转账/企业/个人/理财业务。
- 给银行工作人员评分。
- 模板方法模式介绍
- 模板方法模式是编程中经常用到的模板。它定义了一个操作中的算法骨架,将某些步骤延迟到子类中实现。这样,新的子类可以在不改变一个算法
结构的前提下重新定义该算法的某些特定步骤。- 核心:
- 处理某个流程的代码已经具备,但是其中某个节点的代码暂时不能确定。因此我们采用工厂方法模式,将这个节点的代码实现转移给子类完成。即:
处理步骤父类中定义好,具体实现延迟到子类中定义。- 模板方法的优点:
- 它封装了不变的部分,扩展可变部分。它把认为是不变部分的算法封装到父类中实现,而把可变部分算法由子类继承实现,便于子类继续扩展。
- 它在父类中提取了公共的部分代码,便于代码复用。
- 部分方法是由子类实现的,因此子类可以通过扩展方式增加相应的功能,符合开闭原则。
- 模板方法模式的缺点
- 对每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象。
- 父类中的抽象方法由子类实现,子类执行的结果会影响父类的结果,这导致一种反向的控制结构,它提高了代码阅读的难度。
- 模板的结构
- 抽象方法:在抽象类中申明,由具体子类实现。(即是易变的部分)
- 具体方法:在抽象类中已经实现,在具体子类中可以继承或重写它。(即是易变部分的具体实现方式)
- 钩子方法:在抽象类中已经实现,包括用于判断的逻辑方法和需要子类重写的空方法两种。
- 方法回调(钩子方法)
- 子类不能调用父类
- 什么时候用到了模板方法模式
- 实现一个算法时,整体步骤很固定。但是,某些部分易变。易变部分可以抽象出来,供子类实现。
- 开发中常见的场景
- 数据库访问的封装
- Junit单元测试
- servlet中关于doGet和doPost方法的调用
- Hibernate中模板程序
- spring中JDBCTemplate、HibernateTemplate等。
状态模式(state)
- 场景
- 电梯的运行
- 维修,正常,自动关门,自动开门,向上运动,向下运动,消防状态
- 红绿灯
- 红灯,黄灯,绿灯
- 企业或政府系统
- 公文的审批状态
- 报销单据审批状态
- 假条审批
- 公文的审批状态
- 网上购物时,订单的状态
- 下单,已付款,已发货,送货中,已收货。
- 电梯的运行
- 核心
- 用于解决系统中复杂对象的状态装换以及不同状态下行为的封装问题。
- 结构:
- Context环境类
- 环境类中维护一个state对象,它是定义了当前状态
- State抽象状态类
- ConcreteState 集体状态类
- 每个类封装一个状态对应的行为
- Context环境类
- 状态模式是一种对象行为型模式,其主要优点如下
- 状态模式将与特定状态相关的行为局部化到一个状态中,并且将不同状态的行为分割开来,满足“单一职责原则”
- 减少对象间的相互依赖。将不同的状态引入独立的对象中会使得状态转换变得更加明确,且减少对象间的相互依赖。
- 有利于程序的扩展。通过定义新的子类很容易地增加新的状态和转换。
- 状态模式的主要缺点如下:
- 状态模式的使用必然会增加系统的类与对象的个数。
- 状态模式的结构与实现都较为复杂,如果使用不当会导致程序结构和代码的混乱。
- 通常在以下情况下可以考虑使用状态模式。
- 当一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为时,就可以考虑使用状态模式。
- 一个操作中含有庞大的分支结构,并且这些分支决定于对象的状态时。
观察者模式(Observer)
- 广播机制的场景
- 指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。这种模式有时又称作发布-订阅
模式、模型-视图模式,它是对象行为型模式。 - 核心:
- 观察者模式主要用于1:N的通知.当一个对象(目标对象Objservable)的状态变化时,所有依赖于它的对象(观察者对象Observer)都得到通知并被自动更新
- 通知观察者的方式:
= 推
= 拉- 每次都会把通知以广播方式发给所有观察者,所有观察者只能被动接收。- 观察者只要直到有情况即可,至于什么时候获取内容,获取什么内容,都可以自主决定。 - 开发中常见的场景
- 聊天室程序中,服务器转发给所有客户端
- 网络游戏(多人联机对战)场景中,服务器将客户端的状态进行了分发
- 邮件订阅。
- servlet,监听器的实现。
- android中,广播机制。
- 京东商城中,群发某商品打折消息。
- 观察者模式是一种对象行为型模式,其主要优点如下
- 降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系。
- 目标与观察者之间建立了一套触发机制。
- 观察者模式是一种对象行为型模式,其主要缺点如下
- 目标与观察者之间的依赖关系并没有完全解除,而且有可能出现循环引用。
- 当观察者对象很多时,通知的发布会花费很多时间,影响程序的效率。
- 观察者模式结构
= 抽象主题(Subject)角色:也叫抽象目标类,它提供了一个用于保存观察者对象的聚集类和增加、删除观察者对象的方法,以及通知所有观察者的抽象方法。
= 具体主题(Concrete Subject)角色:也叫具体目标类,它实现抽象目标中的通知方法,当具体主题的内部状态发生改变时,通知所有注册过的观察者对象。
= 抽象观察者(Observer)角色:它是一个抽象类或接口,它包含了一个更新自己的抽象方法,当接到具体主题的更改通知时被调用。
= 具体观察者(Concrete Observer)角色:实现抽象观察者中定义的抽象方法,以便在得到目标的更改通知时更新自身的状态。
备忘录模式(memento)
-场景
- 录入大批人员资料。正在录入当前人资料时,发现上一个人录错了,此时需要回复上一个人的资料,再进行修改。
- Word文档编辑时,忽然电脑死机或者是断电,再打开时,可以看到Word提示你回复到以前的文档。
- 管理系统中,公文撤回功能。公文发送出去,向撤回来。
- 核心
- 就是保存某个对象的内部状态的拷贝,这样以后就可以将该对象恢复到原先的状态。该模式又叫快照模式。
- 备忘录模式是一种对象行为型模式,其主要优点如下:
- 提供了一种可以恢复状态的机制。当用户需要时能够比较方便地将数据恢复到某个历史的状态
- 实现了内部状态的封装。除了创建它的发起人之外,其他对象都不能够访问这些状态信息。
- 简化了源发器类。发起人不需要管理和保存其内部状态的各个备份,所有状态信息都保存在备忘录中,并由管理者进行管理,这符合单一职责原则。
- 其主要缺点是:资源消耗大。如果要保存的内部状态信息过多或者特别频繁,将会占用比较大的内存资源。
- 结构
- 源发器类/发起人Originator:记录当前时刻的内部状态信息,提供创建备忘录和恢复备忘录数据的功能,实现其他业务功能,它可以访问备忘录里的所有信息。
- 备忘录类 Memento:负责存储发起人的内部状态,在需要的时候提供这些内部状态给发起人。
- 负责人类CareTake:对备忘录进行管理,提供保存与获取备忘录的功能,但其不能对备忘录的内容进行访问与修改。
- 当备忘录对象较多时,解决方法
- 压栈
- 通过序列化和反序列化
- 开发中常见的应用场景;
- 棋类游戏中,悔棋。
- 普通软件中,撤销操作。
- 数据库软件中,事务管理中的,回滚操作。
- Photo软件中,历史记录。