《Head First 设计模式》:代理模式[通俗易懂]

《Head First 设计模式》:代理模式[通俗易懂]代理模式为另一个对象提供一个替身或占位符以控制对这个对象的访问。 代理模式为一个对象创建了代理对象,让代理对象控制对该对象的访问。被代理的对象可以是远程的对象、创建开销大的对象或者需要安全控制的对象。 代理类型:远程代理、虚拟代理、保护代理等。 远程代理:控制访问远程对象。 虚…

正文

一、定义

代理模式为另一个对象提供一个替身或占位符以控制对这个对象的访问。

要点:

  • 代理模式为一个对象创建了代理对象,让代理对象控制对该对象的访问。被代理的对象可以是远程的对象、创建开销大的对象或者需要安全控制的对象。
  • 代理类型:远程代理、虚拟代理、保护代理等。
    • 远程代理:控制访问远程对象。
    • 虚拟代理:控制访问创建开销大的资源。
    • 保护代理:基于权限控制对资源的访问。

二、实现步骤

1、创建主题接口

/** * 主题接口 */
public interface Subject {

    /** * 请求主题执行某种动作 */
    void request();
}

2、创建真实主题(被代理类),并继承主题接口

真实主题是真正做事的主题对象。

/** * 真正做事的真实主题(被代理类) */
public class RealSubject implements Subject {

    @Override
    public void request() {
        System.out.println("RealSubject do something...");
    }
}

3、创建主题代理(代理类),并继承主题接口

主题代理持有真正做事的真实主题,并控制对真实主题的访问。

/** * 主题代理(代理类) */
public class SubjectProxy implements Subject {

    /** * 持有真实主题 */
    RealSubject realSubject;
    
    @Override
    public void request() {
        System.out.println("SubjectProxy receives and controls request, and entrust request to RealSubject...");
        // 代理并控制对真实主题的访问,比如权限控制、访问资源控制等
        if (realSubject == null) {
            realSubject = new RealSubject();
        }
        // ...
        
        // 将请求委托给真实主题
        realSubject.request();
    }
}

4、使用主题代理访问主题

public class Test {

    public static void main(String[] args) {
        // 代理
        SubjectProxy proxy = new SubjectProxy();
        // 通过代理请求主题
        proxy.request();
    }
}

三、举个栗子

1、背景

我们打算建立一个应用程序,用来展示你最喜欢的 CD 封面。CD 封面的图我们可以从一些网站的在线服务中获取。

唯一的问题是,限于连接带宽和网络负载,下载可能需要一些时间,所以在等待图像加载时,应该显示一些东西。我们也不希望在等待图像时,整个应用程序被挂起。一旦图像被加载完成,刚才显示的东西应该消失,然后图像显示出来。

2、实现

使用虚拟代理管理图片的加载、显示。

(1)创建图片接口

/** * 图片接口 */
public interface Image {

    /** * 绘图 */
    void paint();
}

(2)创建真正做事的图片类

/** * 真正进行图片操作的真实图片类 */
public class RealImage implements Image {

    String url;
    String description;
    
    public RealImage(String url, String description) {
        this.url = url;
        this.description = description;
        load(url);
    }
    
    @Override
    public void paint() {
        System.out.println("Image is painted");
    }

    /** * 模拟加载图片 */
    private void load(String url) {
        try {
            System.out.println("Loading image from: " + url);
            for (int i = 0; i < 10; i++) {
                System.out.print("===");
                Thread.sleep(200);
            }
            System.out.println("===>100%");
            System.out.println("Loading image is finished!");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

(3)创建图片代理

/** * 图片代理 */
public class ImageProxy implements Image {
    
    RealImage realImage;
    String imageUrl;
    /** * 是否已加载图片 */
    boolean hasLoaded = false;
    
    public ImageProxy(String imageUrl) {
        this.imageUrl = imageUrl;
    }

    @Override
    public void paint() {
        if (realImage != null) {
            // 有图片,则直接绘图
            realImage.paint();
        } else {
            // 没有图片,则先显示加载中的信息,再去加载并绘制图片
            System.out.println("Loading CD cover, please wait...");
            loadAndPaint();
        }
    }
    
    /** * 加载并绘制图片 */
    private void loadAndPaint() {
        if (hasLoaded) {
            return;
        }
        hasLoaded = true;
        // 加载图片是一个比较耗时的操作,为了避免程序阻塞,采用异步处理
        new Thread(new Runnable() {
            @Override
            public void run() {
                realImage = new RealImage(imageUrl, "CD Cover");
                // 加载完后,再进行绘图
                realImage.paint();
            }
        }).start();
    }
}

(4)使用图片代理进行绘图

public class Test {

    public static void main(String[] args) throws InterruptedException {
        // 图片代理
        ImageProxy imageProxy = new ImageProxy("https://www.jingqueyimu.com/images/xxx.jpg");
        // 使用图片代理进行绘图
        System.out.println("首次绘图:");
        imageProxy.paint();
        Thread.sleep(3000);
        System.out.println("再次绘图:");
        imageProxy.paint();
    }
}

四、Java 动态代理

1、创建主题接口

/** * 主题接口 */
public interface Subject {

    /** * 请求主题执行某种动作 */
    void request();
}

2、创建真实主题(被代理类),并继承主题接口

/** * 真正做事的真实主题 */
public class RealSubject implements Subject {

    @Override
    public void request() {
        System.out.println("RealSubject do something...");
    }
}

3、使用 Java 动态代理,代理真实主题

public class Test {

    public static void main(String[] args) {
        // 真实主题(被代理类)
        Subject realSubject = new RealSubject();
        
        // 调用处理器
        InvocationHandler handler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                // 代理实例的方法被调用时,最终会执行该方法
                System.out.println("InvocationHandler start to invoke...");
                // do something...
                // 调用真实主题(被代理类)的方法
                method.invoke(realSubject, args);
                // do something...
                return null;
            }
        };
        
        // 使用 Proxy 创建代理
        Subject subjectProxy = (Subject) Proxy.newProxyInstance(
                realSubject.getClass().getClassLoader(), 
                realSubject.getClass().getInterfaces(), 
                handler);
        
        // 通过代理发起请求
        subjectProxy.request();
    }
}

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
转载请注明出处: https://daima100.com/12981.html

(0)

相关推荐

  • Python实现字符串包含判断,生成

    Python实现字符串包含判断,生成Python是一门高级编程语言,拥有灵活性和强大的数据处理能力。Python实现字符串包含判断,生成h1/h1标题,是Python编程中的基础知识之一。本文将详细介绍Python实现字符串包含判断,生成h1/h1标题的方法与技巧。

    2024-07-26
    35
  • cf僵尸怎么切换角色_数据库组件

    cf僵尸怎么切换角色_数据库组件Data Guard环境中数据库的角色转换有两种,分别为Switchover和Failover,通过名称可知,前者是正常的主备库之间的角色切换,该切换方式不会丢失数据;后者是故障切换,即主库不能继续提

    2023-03-05
    153
  • mysql启动流程[通俗易懂]

    mysql启动流程[通俗易懂]配置流程实例展示 阅读本文前的知识准备: mysql程序启动流程.7z SET MYSQL_HOST="127.0.0.1"SET MYSQL_PWD="123456&q

    2023-02-23
    143
  • random_shuffle(python中shuffle函数)

    random_shuffle(python中shuffle函数)

    2023-09-21
    141
  • 面向个人开发者应该打造的CICD部署系统[亲测有效]

    面向个人开发者应该打造的CICD部署系统[亲测有效]作为个人开发者,打造一套属于自己的Cicd自动化部署系统是一个不错的选择,可以让自己更加深入的了解自动化部署流程,可以方便部署自己的个人项目,让更多的时间投入到开发中去,本文带你快速进入自动化部署流程

    2023-07-19
    131
  • 面向Vue新人:写一个简单的倒计时按钮[通俗易懂]

    面向Vue新人:写一个简单的倒计时按钮[通俗易懂]为了更快显示出效果,我把时间设成了5秒。按钮在点击之后会出现倒计时,同时按钮变为不可点击状态,样式也发生变化,鼠标悬浮上的样子也会发生变化。 在data里加了两条数据,一条用来记录时间,一条用来盛放倒计时按钮的具体内容。在countDown函数里我们用了setInterval定…

    2023-08-01
    142
  • 利用Python计算Sin(40)的值

    利用Python计算Sin(40)的值在Python中,计算Sin(40)可以使用math库中的sin函数,但是该函数的参数是以弧度为单位的。因此在计算Sin(40)之前,需要首先将40°转换成弧度。一个圆的周长是2π,因此一个角度所对应的弧度值可以计算如下:

    2024-03-27
    76
  • Redis系列(三)Redis的事务和Spring Boot整合「建议收藏」

    Redis系列(三)Redis的事务和Spring Boot整合「建议收藏」NoSQL 开发中或多或少都会用到,也是面试必问知识点。最近这几天的面试每一场都问到了。但是感觉回答的并不好,还有很多需要梳理的知识点。这里通过几篇 Redis 笔记整个梳理一遍,后面再加上面试题。

    2023-02-28
    145

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注