创建模式
单例模式
什么是设计模式
- 设计模式:一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。
- 目的:可重用代码,让代码更容易被他人理解、保证代码可靠性。
- Java中的设计模式:23种:
- [x] 创建型模式:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式;
- [x] 结构型模式:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式;
- [x] 行为型模式:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式;
- [x] 其它模式:并发型模式和线程池模式。
单例模式:保证整个应用中某个实例有且只有一个。
举例:配置文件、工具类、线程池、缓存、日志对象等
单利模式实现
- 应用场合: 有些对象只需要一个就足够了.
- 作用: 保证整个应用程序中某个实例有且只有一个.
(1) 饿汉式
懒汉模式,先只是进行声明,当存在该实例的时候直接返回,不存在的时候再去新建
/*
* 单例模式Singleton
* 应用场合:有些对象只需要一个就足够了,如古代皇帝、老婆
* 作用:保证整个应用程序中某个实例有且只有一个
* 类型:饿汉模式、懒汉模式
*/
public class Singleton {
//1.将构造方法私有化,不允许外部直接创建对象
private Singleton(){
}
//2.创建类的唯一实例,使用private static修饰
private static Singleton instance=new Singleton();
//3.提供一个用于获取实例的方法,使用public static修饰
public static Singleton getInstance(){
return instance;
}
}
(2) 懒汉式
饿汉模式特点之一就是在类加载是这个类的实例就被加载了,无论之后是否用到这个实例
/*
* 懒汉模式
*/
public class Singleton2 {
//1.将构造方法私有化,不允许外边直接创建对象
private Singleton2(){
}
//2.声明类的唯一实例,使用private static修饰
private static Singleton2 instance;
//3.提供一个用于获取实例的方法,使用public static修饰
public static Singleton2 getInstance(){
if(instance == null){
instance = new Singleton2();
}
return instance;
}
}
饿汉式、懒汉式区别
饿汉模式:在加载类的时候就加载了实例,因此加载慢,但运行快,线程安全
懒汉模式:在加载类时没有加载实例,加载快,运行时加载实例,运行慢,线程不安全
工厂模式
工厂模式的概念、意图、应用场景、动机
-
工厂模式的概念
-
工厂模式的意图
-
工厂模式的应用场景
-
工厂模式的动机
-
工厂模式类图
-
抽象工厂模式类图
工厂模式的应用
-
常见的应用
(1) JDBC的工作流程
(2) spring beanfactory
-
对比:
-
实现了
-
适用场景
结构型模式
代理模式
1. 概念
- [x] 为其他对象提供一种代理,以控制对这个对象的访问(例如火车站代售处)。代理对象起到中介作用,可去掉功能服务或增加额外的服务。
2. 代理模式的分类:
- [ ] 远程代理模式: 为不同地理的对象提供局域网代表对象(例子:通过远程代理可以监控各个店铺,使之可以直观的了解店里的情况)
- [ ] 虚拟代理: 根据需要将资源消耗很大的对象进行延迟,真正需要的时候进行创建
- [ ] 保护代理: 控制用户的访问权限
- [ ] 智能引用代理: 提供对目标对象提供额外的服务(火车票代售处)
智能引用代理:
静态代理
- 静态代理:代理和被代理对象在【代理之前】都是【确定】的。他们都实现【相同的接口或者继承相同的抽象类】
- 代理实现方法:
(1)继承法:代理类直接【继承】被代理类,实现其原有方法,并添加一些额外功能
(2)聚合方法:代理类实现【相同的功能接口:很重要,事项相同接口,不同代理也可以进行相互代理】,并在内声明一个被代理类的对象(类似封装),通过内部对象实现其原有方法,并添加额外功能
3、静态代理的实现
(1)聚合代理优于继承代理。因为实现功能叠加的情况下,聚合代理通过相互代理可以实现功能重用,而继承代理必须写多个类来实现多功能叠加。
(2)静态代理只能代理一种类型的被代理类,换个类型的就不行了,这需要动态代理
4、静态代理的两种实现方式对比(继承方式和聚合方式)
案例--代理类功能的叠加
(1) 继承的方式:如果使用继承的方式来实现我们代理功能的叠加,我们的代理类会无限的膨胀下去。
(2) 聚合的方式:由于代理类和被代理类都实现了相同的接口,那么代理类的构造参数就可以传入该相同的接口,这样在后面功能叠加的时候就可以传入其他功能的代理类,因为他们都实现了相同的父接口。从而达到功能叠加的作用。
动态代理
- JDK动态代理
(1) 目的:动态产生代理,实现对【不同类】,【不同方法】的代理
(2) java动态代理类,位于java.lang.reflect包下,一般涉及两个类:
(1)Interface InvocationHandler:该接口中仅定义了一个方法public object invoke(Object obj,Method method,Object[] args)
参数:
obj:被代理类的对象
method:被代理的方法
args:被代理方法参数数组
返回值:
Object:方法返回值
(2)Proxy:该类即为动态代理类:
static Object newProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler h)
参数:
loader:被代理类的类加载器,通过被代理类.getClass().getClassLoader()得到
interfaces:实现的接口,通过getClass().getInterfaces()得到
h:invocationHandler
返回值:
返回代理类的一个实例
- 动态代理实现步骤:
(1)创建一个实现接口InvocationHandler的类,它必须实现invoke方法。
(2)创建被代理的类以及接口。
(3)调用Proxy的静态方法,创建一个代理类。
newProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler h)
(4)通过代理调用方法。
CGLIB动态代理
-
JDK动态代理与CGLIB动态代理之间的区别
- CGLIB:是一个强大的开源项目,可以在运行期扩展Java类与实现Java接口
- JDK动态代理实现思路
实现功能:通过Proxy的newProxyInstance返回代理对象
(1)声明一段源码(动态产生代理)
(2)编译源码(JDK Compiler API),产生新的类(代理类)
(3)将这个类load到内存当中,产生一个新的对象(代理对象)
(4)return 代理对象
3. 总结
1、代理概念、分类及应用场景
为其他对象设置总代理,以控制对这个对象的访问;
代理对象起到了中介的作用,去掉了某些功能,或增加了些额外的服务。
四类:
Remote Proxy -- 客户端服务器的模式
Virtual Proxy -- 资源消耗很大,或复杂的对象,需要延迟,需要时创建,
Protect Proxy -- 保护和控制权限
Smart Reference Proxy -- 提供额外服务。
为什么只讲智能引用代理?
使用得多:日志处理、权限管理、事务处理...
静态代理(继承、聚合)
JDK动态代理实现日志处理的功能
模拟JDK动态代理实现:在代理类Proxy和被代理类RealSubject之间,加入了invocationHandler。
调用jar包中某个类的方法,不能改源码,AOP面向切面,增加额外事务逻辑。
适配器模式
适配器模式定义:
将一个类的接口,转换成期望的另外一个借口,使得由于接口不兼容而不能一起工作的那些类可以一起工作。
适配器模式: 构成
构成:客户端、目标接口、原本接口、具体请求
适配器模式分类
- 组合
-
继承
比较
作用
行为型模式
观察者模式
1. 认识观察者模式
(1)定义:
定义对象件的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都会得到通知并且自动更新,
(2)流程图:
2. 实现的两种方式:推模型和拉模型
- 结构
- 观察者模式通用代码
步骤:
(1)目标对象的定义
(2)具体的目标对象的定义
(3)观察者接口的定义
(4)观察者的具体实现
3. 利用Java提供的观察者实现
1. 认识观察者模式
(1)目标与观察者之间的关系:一对一、一对多、多对一等。
(2)单向依赖:观察者依赖目标,而不是目标依赖观察者,观察者是被动的,目标是主动的。
(3)命名建议:
1、目标接口的定义,建议在名称后面跟Subject;
2、观察者接口的定义,建议在名称后面跟Observer;
3、观察者接口的更新方法,建议名称为update,参数的个数及类型不受限制。
(4)触发通知的时机:目标对象的状态发生维护之后触发。(比如:先赋值内容再通知是对的,而反过来就会出现问题)
(5)观察者模式调用时序:见观察者模式调用时序图I、II。
(6)通知的顺序:不确定,平行的,没有相互依赖关系。
-
准备阶段
-
运行阶段
2. 实现的两种方式:推模型和拉模型
观察者模式两种模式:
-
推模型:目标对象主动向观察者推送目标的详细信息 ; 推送的信息通常是目标信息的全部或部分信息。
-
拉模型:目标对象在通知观察者的时候,只传递少量信息 ;如果观察者需要更具体的信息,由观察者主动到目标对象中获取,相当于是观察者从目标对象中拉数据;一般这种模型的实现中,会把目标对象自身通过update方法传递给观察者。
两种模型的比较:
推模型是假定目标对象知道观察着需要的数据。
拉模型是目标对象不知道观察着具体需要什么数据,因此把自身传给观察者,由观察者来取值。
推模型会使观察者对象难于复用。
拉模型下,update方法的参数是目标对象本身,基本上可以适应于各种情况的需要。
Java 实现 VS 自己实现的对比四点:
(1)不需要再定义观察者和目标接口(JDK已经定义)。
(2)具体的目标实现里面不需要再维护观察者的注册信息,Java中的Observable类里面已经实现。
(3)触发通知的方式有一点变化,要先调用setChanged方法,这个是Java为了帮助实现更精确的触发控制而提供的功能。
(4)具体观察者的实现里面,update方法其实能同时支持推模型和拉模型,这个Java在定义的时候,已经考虑。
4. 简述观察者优缺点
优点: | 缺点点: |
---|---|
1. 观察者模式实现了观察者和目标之间的抽象耦合 2. 观察者模式实现了动态联动 3. 观察者模式支持广播通信 | 可能会引起无谓的操作。 |
5. 何时使用观察者模式
使用观察者模式的场景 --- 触发联动(本质)
1、当一个抽象模型有两个方面,其中一个方面的操作依赖于另一个方面的状态变化
2、如果更改一个对象的时候,需要同时连带改变其他的对象,而且不知道究竟应该有多少对象需要被连带改变
3、当一个对象必须通知其他的对象,但是你又希望这个对象和其他被通知的对象是松散耦合的
6. 观察者模式的衍生
观察者模式——区别对待观察者场景问题
- 需求总结
- 解决思路
(1)情况之一:如果天气是晴天,按照黄明的女朋友需要下雨的条件,黄明的老妈需要下雨或下雪的条件,则她们俩就都不需要通知了。
(2)情况之二:如果天气是下雨,则黄明的女朋友需要通知,而黄明的老妈也需要通知。
(3)情况之三:如果天气是下雪,则只需要通知黄明的老妈。 -
实现步骤:
区别对待观察者模式中,目标父类不实现通知方法,在子类中实现有区别的通知方法。
状态变更调用通知方法,通知方法调用观察者,同时回调目标对象
责任链模式
1. 什么是责任链模式
将接收者对象连成一条链,并在该链上传递请求,直到有一个接收者对象处理它。通过让更多对象有机会处理请求,避免了请求发送者和接收者之间的耦合。
2. 如何实现责任链模式
流程是 顾客申请折扣,触发priceHandler对请求价格的处理,在处理价格之前通过工厂方法创建了一个priceHandler的实例,如果实例处理不了折扣,触发后继,然后调用工厂设置后继并创建新的PriceHandler来处理折扣,还是处理不了继续申请,直到成功。
3. 责任链模式如何解耦
加入了新的能够折扣处理的成员lead类继承了PriceHandler
对工厂方法进行了改动 添加了lead的实例,以及给lead设置了后继Successor
1、OO的一些原则:
2、如 单一职责原则 : 设置一个接口时,应该只将与这个接口业务相关的方法放在接口之中。
3、工场方法的实质在用返回返回的是一个接口,而不是一个实例对象。
4、用到责任链,总会用到工厂
将指定的方法移到某个文件中的快捷键操作方式:
选中方法名——Refactor——Move——选择需要移动到的目标文件——确定
责任链模式:使用了自身对象后继
解耦---
责任链模式真的好吗
- 开闭原则(OO中的一个基本原则):
对扩展开放,对变更关闭,即是如果有一个业务变更,希望新增一个类,而非修改原有代码来满足业务需求.
- 责任链模式的执行性能:
当有请求到达时会从责任链头部开始遍历整条责任链,直到有一个处理器处理了请求,或者是整个链条遍历完成,在这过程中性能的损耗体现在两个方面.
(1)是时间,相对于单个Handler处理请求的时间而言,整个链条的遍历过程会消耗更多的时间.
(2) 是内存,使用责任链模式创建了大量对象来表示处理器对象,但仅仅使用了其中的少部分,剩余的大部分处理器都仅仅作为一个过客.
4. 责任链模式的应用
责任链模式在日常编码中可能不是经常用到的模式,但不管是前端还是后端工程师都可能天天接接触到责任链模式.
后端:Java中的异常处理;
前端:JavaScript Event Model;
Java Web:FilterChain in Web(不纯的COR)
1. JAVA中的异常处理机制是使用责任链模式
2. JavaScript的事件模式也是责任链模式
3. JAVAEE中的Filter
(1)JAVAEE中的Filter经常可以在请求到达核心代码之前对它进行拦截并作出一些操作,当多个Filter存在的时候就共同构成了一个FilterChain,FilterChain不是一个存的责任链,责任链模式中只能有一个对象来处理请求,而FilterChain中可以有多个对象同时处理请求.
5. 各个模式间的联系
设计模式的学习一定要结合OO的基本原则
面向对象的五大原则:
- 单一职责原则
- 开放封闭原则(对扩展开放,对变更封闭)
- 依赖倒置原则(核心是依赖抽象)
- 接口隔离原则
- Liskov替换原则(里氏替换原则)
策略模式
1. 什么是策略模式
策略模式将可变的部分从程序中抽象分离成算法接口,在该接口下分别封装一系列算法实现。
并使他们可以互相替换。
从而导致客户端程序独立于算法的改变。
2. 策略模式如何实现
鸭子应用的更新需求
鸭子飞行
方案一:继承
方案二:抽象方法
方案三:组合
1.Favor composition over inheritance:复合(组合)优先于继承。多用组合,少用继承。
2.组合定义:在类中增加一个私有域,引用另一个已有的类的实例,通过调用引用实例的方法从而获得新的功能,这种设计被称作组合(复合)(意思就是:得到其他类的对象,使用这个对象的方法。)
3. 策略模式总结
一、策略模式设计原则:
- 找出应用中需要变化的部分,把他们独立出来,不要和那些不需要变化的代码混在一起;
将不变的东西抽象为接口,而变化的部分交给实现去做,具体而言,鸭子飞行的行为是千变万化的,但是鸭子具有飞行行为本身是不变的,我们将不变的部分抽象为飞行策略接口,而将具体的飞行行为交给实现去处理。 - 面向接口编程,而不是面向实现编程。
- 多用组合,少用继承。
二、策略模式的实现:
- 通过分离变化得出策略接口Strategy。
- 编写Strategy的实现类。
- 客户端程序“有一个”Strategy。
- 在客户程序中选择/组装正确的Strategy实现。
三、策略模式的优点:
- 使用了组合,使架构更加灵活。
- 富有弹性,可以较好的应对变化(开闭原则)。
- 更好的代码复用性(相对于继承)。
- 消除了大量的条件语句。
四、策略模式的缺点:
- 客户代码需要了解每个策略实现的细节,不然就会使得实现有可能有不正确的行为。
- 随着时间的推移,策略接口会急剧膨胀,增加了对象的数目。
五、策略模式的适用场景:
- 许多相关的类仅仅是行为差异,将差异的共享分离出来成为一个策略接口,而这些相关的类便成为其算法家族的成员。
- 运行时选取不同的算法变体。
- 通过条件语句在多个分支中选取一个,使用策略模式使得代码更加简洁。
4. 实际案例分享
模板方法模式
1. 什么是模板方法模式
(1) 模板模式
定义了一个操作中的算法骨架,而将一些步骤延迟到子类中实现,使得子类在不改变一个算法结构的同时,就重新定义该算法的特定步骤.
=>
(2) 生活案例——饮料的调制方法
=>
2. 如何实现模板方法模式
- 定义抽象基类
(1)实现方法(通用共同属性)
(2)抽象方法(延迟方法)
(3)钩子方法(扩展点)
(4)模板方法(一定要用final因为要禁止子类对方法框架的覆写) - 子类
模板方法的基本实现
思想
1、一份算法框架,大家共同遵守
2、 算法框架中分离出变与不变的部分
3、将变化的算法,延迟实现(交由具体的子类实现)
基本实现
1、用一个抽象基类,一个public final方法定义好算法框架
2、不变的部分,用private方法加以实现。(基本方法)
3、变化的部分,用protected abstract加以定义(抽象方法)
使用
1、面向接口编程
2、传入实际的实现子类给接口变量
3、接口变量调用框架方法
用钩子(Hook)函数实现子类对算法框架个性化的扩展
1、思想
框架通过提供一个个的钩子,使框架具备了更大的灵活性。不想执行算法框架中的某些个步骤,我们可以脱钩,如果想执行的话,我们可以挂钩。
2、实现
在抽象类中,提供protected钩子方法。这是个实现的或空的方法。这样子类就可以选择覆写-持钩,也可以选择不覆写-脱勾。
3、使用
提供一个isXXX类型的钩子方法。用该方法控制算法框架中
4、某个步骤是否执行
子类不覆写这个方法,就是脱钩,仍按框架逻辑执行,一旦覆写,就是挂钩,将改变框架算法方向,按子类逻辑执行。
3. 模板方法模式的特点
一、模板方法模式的实现要素:
准备一个抽象类,将部分逻辑以具体方法的形式实现,然后声明一些抽象方法交由子类实现剩余逻辑,用钩子方法给予子类更大的灵活性。最后将方法汇总构成一个不可改变的模板方法。
二、从类的角度看:
(1)抽象基类
1、基本方法。
2、抽象方法【只知道具体原则,而不知道实现细节,需要将其延迟到子类中实现的一些步骤】。
3、可选钩子(Hook,钩子函数,提供一个默认或空的实现。具体的子类可以自行决定是否挂钩以及如何挂钩)。
4、Template方法(final 使其不能被子类所覆写 模板方法模式要遵循的原则:子类可以替换掉父类中的可变逻辑,但不能改变整体逻辑结构))。
(2)具体子类
1、实现基类中的抽象方法。
2、覆盖钩子方法。
三、模板方法的优点:
(1)封装性好。(2)复用性好。(3)屏蔽细节。(4)便于维护。
四、模板方法的缺点:
(1)继承限制(Java语言是单继承语言),单继承会使得更多情况不可用,新类引入困难。
五、模板方法模式的适用场景:
(1)算法或操作遵循相似的逻辑。
(2)重构时(把相同的代码抽取到父类中)。
(3)重要、复杂的算法,核心算法设计为模板方法。