跳至主要內容

【设计模式】结构型-④代理模式

holic-x...大约 7 分钟设计模式设计模式

【设计模式】结构型-④代理模式

学习核心

  • 代理模式核心
    • 概念:使用代理对象(proxy object)来代替对真实对象(real object)的访问,在不修改原目标对象的基础上提供额外的功能操作,扩展目标对象的功能
    • 组成:代理对象、被代理对象
      • 主题(Subject):抽象主题(Abstract Subject)、真实主题(Real Subject)、代理主题(Proxy Subject)
        • 抽象主题:定义了真实主题和代理主题的公共接口
        • 真实主题:基础业务功能实现
        • 代理主题:在不改变原有业务代码的基础上进行扩展实现
      • 客户端(Client):通过抽象主题来操作真实主题或代理主题
  • 应用场景分析
    • 【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();
    }

}
评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v3.1.3