Java设计模式Ⅴ
1.代理模式
1.1 代理模式概述
(1)基本介绍
①为一个对象提供一个替身,以控制对这个对象的访问,即通过代理对象
访问目标对象,这样做的好处:可以在目标对象实现的基础上,增强额
外的功能操作,即扩展目标对象的功能
②被代理的对象可以是远程对象,创建开销大的对象或者需要安全控制的对象
③代理模式有不同的形式,主要有三种,静态代理,动态代理(JDK代理或者
接口代理),Cglib代理(在内存中动态创建对象,而不需要实现接口)
(2)代理模式原理类图
1.2 静态代理
1.2.1 静态代理概述
(1)基本介绍
1)优点:在不修改目标对象功能的前提下,能通过代理对象对目标对像
功能扩展
2)缺点:因为代理对象需要和目标对象实现一样的接口,所以会有很多
代理类
3)一旦接口增加方法,目标对象和代理对象都需要维护
(2)类图
1.2.2 代码理解
1.接口类
package com.pattern.设计模式.代理模式.静态代理;
public interface ITeacherDao {
void teacher();
}
2.TeacherDao 对象类
package com.pattern.设计模式.代理模式.静态代理;
public class TeacherDao implements ITeacherDao{
@Override
public void teacher() {
System.out.println("老师在上课>>>>>>>>");
}
}
3.代理对像类
package com.pattern.设计模式.代理模式.静态代理;
public class TeacherDaoProxy implements ITeacherDao{
private ITeacherDao iTeacherDao; // 目标对象,通过接口聚合
public TeacherDaoProxy(ITeacherDao iTeacherDao){
this.iTeacherDao = iTeacherDao;
}
@Override
public void teacher() {
System.out.println("代理开始>>>>>");
iTeacherDao.teacher();
System.out.println("代理结束<<<<<");
}
}
4.测试类
package com.pattern.设计模式.代理模式.静态代理;
public class Test {
public static void main(String[] args) {
// 创建被代理对象
TeacherDao teacherDao = new TeacherDao();
// 创建代理对象 同时将被代理对象传递给代理对像
TeacherDaoProxy teacherDaoProxy = new TeacherDaoProxy(teacherDao);
// 通过代理对象,调用到被代理对象的方法
teacherDaoProxy.teacher();
}
}
1.3 动态代理之JDK代理
1.3.1 动态代理之JDK代理概述
(1)基本介绍
1)代理对象不需要实现接口,但是目标对象要实现接口,否则不能用
动态代理
2)代理对象的生产是利用JDK和API,动态的在内存中构建代理对象
3)动态代理也叫JDK代理,接口代理
(2)类图
1.3.2 代码理解
1.还是接口类
package com.pattern.设计模式.代理模式.动态代理;
public interface ITeacherDao {
void teacher();
}
2.还是TeacherDao 对象类
package com.pattern.设计模式.代理模式.动态代理;
public class TeacherDao implements ITeacherDao {
@Override
public void teacher() {
System.out.println("正在上课>>>>>>>>>>>");
}
}
3.代理对象类
package com.pattern.设计模式.代理模式.动态代理;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyFactory {
// 维护一个目标对象 Object
private Object object;
public ProxyFactory(Object object){
this.object = object;
}
// 给目标对象生成一个代理对象
public Object getProxyInstance(){
/**
* 第一个参数:指定当前目标对象使用的类加载器,获取加载器的方法固定
*
* 第二个参数:目标对像实现的接口类型,使用泛型方法确认类型
*
* 第三个参数:事情处理,执行目标对象的方法时,会触发事情处理器方法,
* 会把当前执行的目标对象方法作为参数传入
*/
return Proxy.newProxyInstance(object.getClass().getClassLoader(),
object.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("JDK代理开始");
// 反射机制调用目标对象的方法
Object invoke = method.invoke(object, args);
return invoke;
}
});
}
}
4.测试类
package com.pattern.设计模式.代理模式.动态代理;
public class Test {
public static void main(String[] args) {
// 创建目标对象
TeacherDao teacherDao = new TeacherDao();
// 给目标对象创建代理对象 可以转成 ITeacherDao
ITeacherDao proxyFactory = (ITeacherDao) new ProxyFactory(teacherDao).getProxyInstance();
System.out.println(proxyFactory);
proxyFactory.teacher();
}
}
1.4 动态代理之Cglib代理
1.4.1 动态代理之Cglib代理概述
(1)基本介绍
1)静态代理和JDK代理模式都要求目标对象实现一个接口,但是
有时候目标对象只是一个单独的对象,并没有实现任何的接口,
这个时候可以使用目标对象子类来实现代理,这就是Cglib代理
2)Cglib代理也叫做子类代理,它是在内存中构建一个子类对象从
而实现对目标对象功能扩展,Cglib也有人将其归属到动态代理
3)Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展
java类和实现java接口,它广泛的被许多AOP框架使用,比如
Spring AOP,实现方法拦截
4).a.目标对象需要实现接口:用JDK模式
b.目标对象不需要实现接口:用Cglib模式
5)Cglib包底层通过使用字节码处理框架ASM来转换字节码并生成新的类
(2)类图
1.4.2 代码理解
1.这个不同的区别就在不需要接口了,直接对象类
package com.pattern.设计模式.代理模式.Cglib代理;
public class TeacherDao {
public void teacher(){
System.out.println("老师正在上课");
}
}
2.代理对象类
package com.pattern.设计模式.代理模式.Cglib代理;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class ProxyFactory implements MethodInterceptor {
// 聚合一个目标对象
private Object object;
public ProxyFactory(Object object){
this.object = object;
}
// 返回一个代理对象 是object对象的代理对象
public Object getProxyInstance(){
// 1.创建一个工具类
Enhancer enhancer = new Enhancer();
// 2.设置父类
enhancer.setSuperclass(object.getClass());
// 3.设置回调函数
enhancer.setCallback(this);
// 4.创建子类对象,及代理对象
return enhancer.create();
}
// 重写 intercept方法 会调用目标对象的方法
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("Cglib代理农事开始");
Object invoke = method.invoke(object, objects);
System.out.println("Cglib代理提交");
return invoke;
}
}
3.测试
package com.pattern.设计模式.代理模式.Cglib代理;
public class Test {
public static void main(String[] args) {
// 创建目标对象
TeacherDao teacherDao = new TeacherDao();
// 获取到代理对象 并且将目标对象传给代理对象
TeacherDao proxyInstance = (TeacherDao) new ProxyFactory(teacherDao).getProxyInstance();
proxyInstance.teacher();
}
}
2.模板方法模式
2.1 模板方法模式概述
(1)基本介绍
①模板方法模式,又叫模板模式,在一个抽象类公开定义了执行它的
方法的模板,它的子类可以按需要重写方法实现,但调用将以抽象类
中定义的方式进行
②简单来说,模板方法定义一个操作中的树算法骨架,而将一些步骤延迟
到子类中,使得子类可以不改变一个算法的结构,就可以重定义该算法
的某些特定步骤
③这种类型的设计模式属于行为型模式
(2)原比类图
(3)代码类图
(3)模板方法模式在spring IOC容器初始化时运用到了。
(4)模板方法模式注意事项和细节
1.基本思想是算法只存在一个地方,也就是方法中,容易修改,需要修改
算法时,只需要修改父类的模板方法或者已经实现的某些步骤,子类就
会继承这些修改
2.实现了最大化代码复用。父类的模板方法和实现的某些步骤会被子类
继承而直接使用
3.即统一了算法,又提供了很大的灵活性,父类的模板方法确保了算法
的结构保持不变,同时由子类提供部分步骤的实现
4.该模式的不足之处:每一个不同实现都需要一个子类实现,导致类
的个数增加,使得系统更加庞大
5.一般模板方法都加上final关键字,防止子类重写模板方法
6.使用场景:当要完成某个过程,该过程要执行一系列步骤,这一系列
的步骤基本相同,但个别步骤在实现时可能不同,可以使用该模式
2.1.1 代码理解
1.模板方法类
package com.pattern.设计模式.模板方法模式.正常模板方法模式;
public abstract class SoyMilk {
// 模板方法 模板方法可以做成final, 不让子类去继承
final void make(){
select();
add();
soak();
beat();
}
void select(){
System.out.println("选择材料-黄豆");
}
// 添加配料--但是配料不固定,可以是任何口味,所以定义成抽象方法
abstract void add();
void soak(){
System.out.println("浸泡一段时间");
}
void beat(){
System.out.println("将材料放到豆浆机");
}
}
2.子类1
package com.pattern.设计模式.模板方法模式.正常模板方法模式;
public class RedSoy extends SoyMilk{
@Override
void add() {
System.out.println("加入红豆配料");
}
}
3.子类2
package com.pattern.设计模式.模板方法模式.正常模板方法模式;
public class GreenSoy extends SoyMilk {
@Override
void add() {
System.out.println("加入绿豆配料");
}
}
4.测试
package com.pattern.设计模式.模板方法模式.正常模板方法模式;
public class Test {
public static void main(String[] args) {
// RedSoy
SoyMilk redSoy = new RedSoy();
redSoy.make();
System.out.println(">>>>>>>>>>>>>>>>>>>>>>");
// GreenSoy
SoyMilk greenSoy = new GreenSoy();
greenSoy.make();
}
}
2.2 模板方法模式之钩子方法
(1)基本介绍
①本质跟前面的没什么关系,只是增加了一个方法,去判断中间某个
方法要不要实现
2.2.1 代码理解
1.模板方法-加上钩子方法
package com.pattern.设计模式.模板方法模式.钩子方法模板模式;
public abstract class SoyMilk {
// 模板方法 模板方法可以做成final, 不让子类去继承
final void make(){
select();
if (getGouZi()){
add();
}
soak();
beat();
}
void select(){
System.out.println("选择材料-黄豆");
}
// 添加配料--但是配料不固定,可以是任何口味,所以定义成抽象方法
abstract void add();
void soak(){
System.out.println("浸泡一段时间");
}
void beat(){
System.out.println("将材料放到豆浆机");
}
// 钩子方法
boolean getGouZi(){
return true;
}
}
2.子类 加配料
package com.pattern.设计模式.模板方法模式.钩子方法模板模式;
public class RedSoy extends SoyMilk {
@Override
void add() {
System.out.println("加入红豆配料");
}
}
3.子类 不加配料
package com.pattern.设计模式.模板方法模式.钩子方法模板模式;
public class Soy extends SoyMilk{
@Override
void add() {
// 因为不需要加配料,所以空实现
}
// 注意这里
@Override
boolean getGouZi() {
return false;
}
}
4.测试
package com.pattern.设计模式.模板方法模式.钩子方法模板模式;
public class Test {
public static void main(String[] args) {
// RedSoy
SoyMilk redSoy = new RedSoy();
redSoy.make();
System.out.println(">>>>>>>>>>>>>>>>>>>>>>");
// GreenSoy
SoyMilk greenSoy = new GreenSoy();
greenSoy.make();
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>");
// Soy
SoyMilk soy = new Soy();
soy.make();
}
}
3.命令模式
3.1 命令模式概述
(1)基本介绍
①在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的
接收对象是谁,也不知道被请求的操作是谁,我们只需要在程序运行时指
定具体的请求接受者即可。此时,可以使用命令模式来设计
②命令模式使得请求发送者和请求接受者消除彼此之间的耦合度,让对象之
间的调用更加灵活,实现解耦
③在命令模式中,会将一个请求封装成一个对象,以便使用不同的参数来表
示不同的请求,同时命令模式也支持取消的操作,
(2)原理类图
Invoker:调用者角色
Command:命令角色,需要执行的命令都在这里,可以是接口或者抽象类
Receiver:接受者角色,知道如何实施和执行一个请求象关的操作
ConcreteCommand:将一个接受者对象与一个动作,调用接收者相应的操作,实现execute
(3)代码类图
PS:JDBCTemplate中使用到了命令模式
3.2 代码理解
1.Command接口类
package com.pattern.设计模式.命令模式;
public interface Command {
public void execute(); // 打开
public void undo(); // 取消
}
2.LightOffCommand 类
package com.pattern.设计模式.命令模式;
public class LightOffCommand implements Command{
LightReceiver lightReceiver;
public LightOffCommand(LightReceiver lightReceiver) {
this.lightReceiver = lightReceiver;
}
@Override
public void execute() {
lightReceiver.off();
}
@Override
public void undo() {
lightReceiver.on();
}
}
3.LightOnCommand 类
package com.pattern.设计模式.命令模式;
public class LightOnCommand implements Command{
LightReceiver lightReceiver;
public LightOnCommand(LightReceiver lightReceiver) {
this.lightReceiver = lightReceiver;
}
@Override
public void execute() {
lightReceiver.on();
}
@Override
public void undo() {
lightReceiver.off();
}
}
4.NoCommand 类
package com.pattern.设计模式.命令模式;
public class NoCommand implements Command{
@Override
public void execute() {
}
@Override
public void undo() {
}
}
5.LightReceiver 类
package com.pattern.设计模式.命令模式;
public class LightReceiver {
public void on(){
System.out.println("电灯打开了");
}
public void off(){
System.out.println("电灯关闭了");
}
}
6.RemoteController 类
package com.pattern.设计模式.命令模式;
public class RemoteController {
// 开 命令数组
Command[] onCommands;
Command[] offCommands;
// 撤销的命令
Command undoCommand;
// 构造器 完成对按钮的初始化
public RemoteController(){
onCommands = new Command[5];
offCommands = new Command[5];
for (int i = 0; i < 5; i++){
onCommands[i] = new NoCommand();
offCommands[i] = new NoCommand();
}
}
// 给按钮设置需要的命令
public void setCommand(int no, Command onCommand, Command offCommand){
onCommands[no] = onCommand;
offCommands[no] = offCommand;
}
// 按下开的按钮
public void onButton(int no){
onCommands[no].execute();
// 记录这次的操作,用于撤销
undoCommand = onCommands[no];
}
// 按下关的按钮
public void offButton(int no){
offCommands[no].execute();
// 记录这次的操作,用于撤销
undoCommand = offCommands[no];
}
// 按下撤销按钮
public void undoButton(){
undoCommand.undo();
}
}
7.测试类
package com.pattern.设计模式.命令模式;
public class Test {
public static void main(String[] args) {
// 使用命令模式,完成通过遥控器对电灯的操作
// 创建电灯的对象
LightReceiver lightReceiver = new LightReceiver();
// 创建电灯打开的命令
LightOnCommand lightOnCommand = new LightOnCommand(lightReceiver);
LightOffCommand lightOffCommand = new LightOffCommand(lightReceiver);
// 创建遥控器
RemoteController remoteController = new RemoteController();
// 给遥控器设置相关的命令 no==0 是电灯的开关操作
remoteController.setCommand(0, lightOnCommand, lightOffCommand);
remoteController.onButton(0);
remoteController.offButton(0);
remoteController.undoButton();
}
}