【设计模式】结构型-④代理模式
【设计模式】结构型-④代理模式
注
学习核心
- 代理模式核心
- 概念:使用代理对象(proxy object)来代替对真实对象(real object)的访问,在不修改原目标对象的基础上提供额外的功能操作,扩展目标对象的功能
- 组成:代理对象、被代理对象
- 主题(Subject):抽象主题(Abstract Subject)、真实主题(Real Subject)、代理主题(Proxy Subject)
- 抽象主题:定义了真实主题和代理主题的公共接口
- 真实主题:基础业务功能实现
- 代理主题:在不改变原有业务代码的基础上进行扩展实现
- 客户端(Client):通过抽象主题来操作真实主题或代理主题
- 主题(Subject):抽象主题(Abstract Subject)、真实主题(Real Subject)、代理主题(Proxy Subject)
- 应用场景分析
【MyBatis框架】:定义接口,通过XML配置文件或者自定义注解中的SQL语句进行CRUD操作
【中间件】:例如RPC框架,在获取到jar包对接口的描述之后,中间件会在服务启动的时候生成相应的代理类,当调用接口时实际是通过代理类发出的socket信息进行调用
JS的【图片加载】场景、图片二次处理场景:
- 缓存代理:一些网络图片交互场景下,先将图片预先加载到本地缓存中,当要进行访问的时候直接访问本地缓存信息,而不需重复从服务器中拉取;
- 虚拟代理:例如一些图片加载比较大的情况下,先用loading图片进行占位然后异步加载图片信息,待图片信息加载完成之后再刷新图片数据
- 图片二次处理场景:通过代理对图片进行二次处理(水印渲染、加解密、图片权限访问控制等)
【AOP】场景:所谓AOP面向切面编程,其底层本质也是通过代理方式实现对功能的横向扩展
学习资料
概念说明
代理模式:使用代理对象(proxy object)来代替对真实对象(real object)的访问,在不修改原目标对象的基础上提供额外的功能操作,扩展目标对象的功能。代理模式可以应用于不同的场景(如网络请求、图片加载、数据操作等)
1.代理模式基础版本
- 代理模式基础版本核心
- 代理目标(
Target):被代理对象 - 代理类(
Proxy):代理类,提供增强方法(通过提供代理隐藏对目标对象的调用)
- 代理目标(
public class SimpleProxyDemo {
// 代理目标
static class Target {
public void doSth() {
System.out.println("do sth...");
}
}
// 代理方
static class Proxy {
public void doProxy() {
// 定义代理目标
Target target = new Target();
// 代理内容:执行代理方法,并额外扩展自己的一些内容
System.out.println("i am proxy");
target.doSth();
System.out.println("proxy end.....");
}
}
public static void main(String[] args) {
Proxy proxy = new Proxy();
proxy.doProxy(); // 代理的目的是对使用者隐藏对目标对象的调用
}
}
2.代理模式进阶版本
- 代理模式进阶版本核心
- 抽象主题(
Subject):定义了真实主题和代理主题的公共接口 - 真实主题(
RealSubject,被代理对象):基础业务功能实现 - 代理主题(
ProxySubject,代理对象):在不改变原有代码逻辑的基础上进行业务扩展
- 抽象主题(
基于上述核心,将基础版代理模式进行代码结构优化
/**
* 进阶版代理模式:
* Subject、RealSubject、ProxySubject
*/
public class AdvanceProxyDemo {
// ① 定义Subject(抽象主题):公共接口定义
static interface Subject{
public void doSth();
}
// ② 定义RealSubject(真实主题,被代理对象)
static class RealSubject implements Subject{
@Override
public void doSth() {
System.out.println("real do sth......");
}
}
// ③ 定义ProxySubject(代理主题,代理对象)
static class ProxySubject implements Subject{
// 定义被代理对象
RealSubject realSubject = new RealSubject();
@Override
public void doSth() {
System.out.println("proxy start......");
realSubject.doSth();
System.out.println("proxy end.....");
}
}
public static void main(String[] args) {
ProxySubject proxy = new ProxySubject();
proxy.doSth();
}
}
3.代理模式 VS 装饰器模式
从模式的定位来看,代理模式和装饰器模式都是可以实现对目标方法的增强,此处为了避免概念混淆,可以从**"目标对象"**这个点进行切入:
代理模式:通过提供代理,完全隐藏了对真实目标的访问,从代码实现上来看Target对象是由Proxy进行管理和创建的,外部对Proxy代理的真实对象是无感知的。这点有点类似VPN的正向代理概念,一般通过翻墙去访问外网,使用者不需要关注具体是通过哪个外网IP访问的
/** * 代理模式:通过提供代理隐藏对目标对象的调用,一般用作方法的增强 */ public class ProxyDemo1 { public static void main(String[] args) { Proxy proxy = new Proxy(); proxy.doProxy(); } } // 代理目标(被代理对象) class Target{ public void doSth(){ System.out.println("i am target, do something"); } } // 定义代理类 class Proxy{ public void doProxy(){ Target target = new Target(); System.out.println("i am proxy"); target.doSth(); System.out.println("proxy end....."); } }装饰器模式:强调的是对真实目标的增强,代码上体现是通过构造函数接收真实目标,然后对指定目标进行增强,返回的是增强后的对象
/** * 装饰器模式 */ public class DecorateDemo1 { public static void main(String[] args) { // 定义装饰目标 Target target = new Target(); // 通过装饰者修饰目标方法(也可以自定义逻辑返回被装饰后的对象),此处设定目标方法增强(对标代理模式应用) Decorater decorater = new Decorater(target); decorater.doSth(); } } // 装饰目标(被装饰对象) class Target{ public void doSth(){ System.out.println("i am target"); } } // 装饰者(用于装饰) class Decorater{ // 定义被装饰对象(通过提供构造方法初始化被装饰对象) private Target target; Decorater(Target target){ this.target = target; } // 定义方法增强 public void doSth(){ System.out.println("i am decorate start"); target.doSth(); System.out.println("i am decorate end"); } } 那么类似地,装饰者模式的进阶版本也可以进行代码结构优化,拆分为多种设计,但本质上是对接口方法的规范,实际组成还是"装饰目标"、"装饰者"这两个核心(参考案例:writer的修饰)
/** * 装饰者模式:流的扩展 */ public class DecorateWriteDemo { public static void main(String[] args) { // 定义装饰目标 TextBaseWriter writer = new TextBaseWriter(); // 定义装饰类 BufferTextBaseWriter bufferTextBaseWriter = new BufferTextBaseWriter(writer); bufferTextBaseWriter.write(); } } // 定义流(接口规范) interface BaseWriter{ public void write(); } // 定义不同的流及操作(例如文本、图片、影像等) class TextBaseWriter implements BaseWriter{ @Override public void write() { System.out.println("i am TextBaseWriter"); } } class PictureBaseWriter implements BaseWriter{ @Override public void write() { System.out.println("i am PictureBaseWriter"); } } class VideoBaseWriter implements BaseWriter{ @Override public void write() { System.out.println("i am VideoBaseWriter"); } } // 流扩展(在原有的不同处理流基础上需要做功能增强),例如引入buffer(缓冲流)概念等,因此引入装饰者模式进行装饰 class BufferTextBaseWriter implements BaseWriter{ // 定义装饰目标 private TextBaseWriter textBaseWriter; // 构造函数初始化(接收装饰目标用于初始化) public BufferTextBaseWriter(TextBaseWriter textBaseWriter) { this.textBaseWriter = textBaseWriter; } @Override public void write() { System.out.println("i am decorate start"); textBaseWriter.write(); System.out.println("i am decorate end"); } } // 类似地,以此类推,其他的装饰类也是通过接收被装饰对象来实现装饰动作
场景案例
1.图片加载场景
目标:设定一个代理,控制对真实图片的访问(例如开关图片访问权限、图片二次处理等)
核心:定义Image接口,分别定义RealImage、ProxyImage,通过Client客户端调用抽象主题进行操作
代码实现说明
- 主题定义(抽象主题接口、真实主题、代理主题)
/**
* 抽象主题接口
* 定义真实主题和代理主题的公共接口
*/
public interface Image {
public void display();
}
/**
* 真实主题:实现抽象主题接口
*/
public class RealImage implements Image {
@Override
public void display() {
System.out.println("show real image");
}
}
/**
* 代理主题:实现抽象主题接口
* 目的:控制对真实图片的访问,例如水印渲染、加密处理等
*/
public class ProxyImage implements Image {
// 定义被代理对象
private RealImage realImage = new RealImage();
@Override
public void display() {
realImage.display();
System.out.println("show proxy image: 输出水印渲染后的图片信息");
}
}
- 客户端定义(客户端调用)
/**
* 客户端访问
*/
public class Client {
public static void main(String[] args) {
Image proxy = new ProxyImage();
proxy.display();
}
}
