首页 > 其他分享 >GoF之代理模式之静态代理

GoF之代理模式之静态代理

时间:2023-06-09 12:33:20浏览次数:35  
标签:begin end 静态 GoF 代理 System 对象 public

1. 对代理模式的理解  92

GoF之代理模式之静态代理_代理类

1.1 生活场景1:

牛村的牛二看上了隔壁村小花,牛二不好意思直接找小花,于是牛二找来了媒婆王妈妈。这里面就有一个非常典型的代理模式。牛二不能和小花直接对接,只能找一个中间人。其中王妈妈是代理类,牛二是目标类。王妈妈代替牛二和小花先见个面。(现实生活中的婚介所)【在程序中,对象A和对象B无法直接交互时。】

1.2 生活场景2:

你刚到北京,要租房子,可以自己找,也可以找链家帮你找。其中链家是代理类,你是目标类。你们两个都有共同的行为:找房子。不过链家除了满足你找房子,另外会收取一些费用的。(现实生活中的房产中介)【在程序中,功能需要增强时。】

1.3 西游记场景:

八戒和高小姐的故事。八戒要强抢民女高翠兰。悟空得知此事之后怎么做的?悟空幻化成高小姐的模样。代替高小姐与八戒会面。其中八戒是客户端程序。悟空是代理类。高小姐是目标类。那天夜里,在八戒眼里,眼前的就是高小姐,对于八戒来说,他是不知道眼前的高小姐是悟空幻化的,在他内心里这就是高小姐。所以悟空代替高小姐和八戒亲了嘴儿。这是非常典型的代理模式实现的保护机制。代理模式中有一个非常重要的特点:对于客户端程序来说,使用代理对象时就像在使用目标对象一样。【在程序中,目标需要被保护时】

1.4 业务场景:

系统中有A、B、C三个模块,使用这些模块的前提是需要用户登录,也就是说在A模块中要编写判断登录的代码,B模块中也要编写,C模块中还要编写,这些判断登录的代码反复出现,显然代码没有得到复用,可以为A、B、C三个模块提供一个代理,在代理当中写一次登录判断即可。代理的逻辑是:请求来了之后,判断用户是否登录了,如果已经登录了,则执行对应的目标,如果没有登录则跳转到登录页面。【在程序中,目标不但受到保护,并且代码也得到了复用。】

代理模式是GoF23种设计模式之一。属于结构型设计模式。

1.5 代理模式的作用是:

为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个客户不想或者不能直接引用一个对象,此时可以通过一个称之为“代理”的第三者来实现间接引用。代理对象可以在客户端和目标对象之间起到中介的作用,并且可以通过代理对象去掉客户不应该看到的内容和服务或者添加客户需要的额外服务。 通过引入一个新的对象来实现对真实对象的操作或者将新的对象作为真实对象的一个替身,这种实现机制即为代理模式,通过引入代理对象来间接访问一个对象,这就是代理模式的模式动机。

1.6 代理模式中的角色:

● 代理类(代理主题)

● 目标类(真实主题)

● 代理类和目标类的公共接口(抽象主题):客户端在使用代理类时就像在使用目标类,不被客户端所察觉,所以代理类和目标类要有共同的行为,也就是实现共同的接口。

代理模式的类图:

GoF之代理模式之静态代理_静态代理_02

1.7 代理模式在代码实现上,包括两种形式:  92

● 静态代理

● 动态代理

2. 静态代理  93

现在有这样一个接口和实现类:

OrderService接口

package com.powernode.proxy.service;

/**
 * 订单业务接口  93
 **/
public interface OrderService { // 代理对象和目标对象的公共接口。

    /**
     * 生成订单
     */
    void generate();

    /**
     * 修改订单信息
     */
    void modify();

    /**
     * 查看订单详情
     */
    void detail();

}

OrderServiceImpl接口实现类

package com.powernode.proxy.service;

/**
 * 接口实现类  93
 **/
public class OrderServiceImpl implements OrderService{ // 目标对象

    @Override
    public void generate() { // 目标方法
        // 模拟生成订单的耗时
        try {
            Thread.sleep(1234);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("订单已生成.");
    }

    @Override
    public void modify() { // 目标方法
        // 模拟修改订单的耗时
        try {
            Thread.sleep(456);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("订单已修改.");
    }

    @Override
    public void detail() { // 目标方法
        // 模拟查询订单的耗时
        try {
            Thread.sleep(111);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("请看订单详情.");
    }
}

其中Thread.sleep()方法的调用是为了模拟操作耗时。

2.1 需求  93

项目已上线,并且运行正常,只是客户反馈系统有一些地方运行较慢,要求项目组对系统进行优化。于是项目负责人就下达了这个需求。首先需要搞清楚是哪些业务方法耗时较长,于是让我们统计每个业务方法所耗费的时长。如果是你,你该怎么做呢?

2.1.1 第一种方案:  93

直接修改Java源代码,在每个业务方法中添加统计逻辑,如下

package com.powernode.mall.service.impl;

import com.powernode.mall.service.OrderService;

/**
 * @author 动力节点
 * @version 1.0
 * @className OrderServiceImpl
 * @since 1.0
 **/
public class OrderServiceImpl implements OrderService {
    @Override
    public void generate() {
        long begin = System.currentTimeMillis();
        try {
            Thread.sleep(1234);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("订单已生成");
        long end = System.currentTimeMillis();
        System.out.println("耗费时长"+(end - begin)+"毫秒");
    }

    @Override
    public void detail() {
        long begin = System.currentTimeMillis();
        try {
            Thread.sleep(2541);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("订单信息如下:******");
        long end = System.currentTimeMillis();
        System.out.println("耗费时长"+(end - begin)+"毫秒");
    }

    @Override
    public void modify() {
        long begin = System.currentTimeMillis();
        try {
            Thread.sleep(1010);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("订单已修改");
        long end = System.currentTimeMillis();
        System.out.println("耗费时长"+(end - begin)+"毫秒");
    }
}

GoF之代理模式之静态代理_System_03

需求可以满足,但显然是违背了OCP开闭原则。这种方案不可取。

2.1.2 第二种方案:  93

编写一个子类继承OrderServiceImpl,在子类中重写每个方法,代码如下:

package com.powernode.proxy.service;

/**
 * 接口实现类OrderServiceImpl的子类  93
 **/
public class OrderServiceImplSub extends OrderServiceImpl{

    @Override
    public void generate() {
        long begin = System.currentTimeMillis();
        super.generate();
        long end = System.currentTimeMillis();
        System.out.println("耗时"+(end - begin)+"毫秒");
    }

    @Override
    public void modify() {
        long begin = System.currentTimeMillis();
        super.modify();
        long end = System.currentTimeMillis();
        System.out.println("耗时"+(end - begin)+"毫秒");
    }

    @Override
    public void detail() {
        long begin = System.currentTimeMillis();
        super.detail();
        long end = System.currentTimeMillis();
        System.out.println("耗时"+(end - begin)+"毫秒");
    }
}
OrderService orderService = new OrderServiceImplSub();
        orderService.generate();
        orderService.detail();
        orderService.modify();

GoF之代理模式之静态代理_代理类_04

这种方式可以解决,但是存在两个问题:

● 第一个问题:假设系统中有100个这样的业务类,需要提供100个子类,并且之前写好的创建Service对象的代码,都要修改为创建子类对象。

● 第二个问题:由于采用了继承的方式,导致代码之间的耦合度较高。

这种方案也不可取。

2.1.3 第三种方案:  94

2.1.3.1 拓展类和类之间的关系  94

GoF之代理模式之静态代理_静态代理_05

使用代理模式(这里采用静态代理)

可以为OrderService接口提供一个代理类。

OrderServiceProxy代理类

package com.powernode.proxy.service;

/**
 * 静态代理  94
 **/
// 代理对象(代理对象和目标对象要具有相同的行为,就要实现同一个或同一些接口。)
// 客户端在使用代理对象的时候就像在使用目标对象一样。
public class OrderServiceProxy implements OrderService{

    // 将目标对象作为代理对象的一个属性。这种关系叫做关联关系。比继承关系的耦合度低。
    // 代理对象中含有目标对象的引用。关联关系。
    // 注意:这里要写公共接口类型。因为公共接口耦合度低。
    private OrderService target; // 这就是目标对象。目标对象一定是实现了OrderService接口的。

    // 通过构造器赋值,创建代理对象的时候,传一个目标对象OrderServiceImpl给代理对象。
    public OrderServiceProxy(OrderService target) {
        this.target = target;
    }

    @Override
    public void generate() { // 代理方法
        // 增强
        long begin = System.currentTimeMillis();
        // 调用目标对象的目标方法
        target.generate();
        long end = System.currentTimeMillis();
        System.out.println("耗时"+(end - begin)+"毫秒");
    }

    @Override
    public void modify() { // 代理方法
        // 增强
        long begin = System.currentTimeMillis();
        // 调用目标对象的目标方法
        target.modify();
        long end = System.currentTimeMillis();
        System.out.println("耗时"+(end - begin)+"毫秒");
    }

    @Override
    public void detail() { // 代理方法
        // 增强
        long begin = System.currentTimeMillis();
        // 调用目标对象的目标方法
        target.detail();
        long end = System.currentTimeMillis();
        System.out.println("耗时"+(end - begin)+"毫秒");
    }
}
// 创建目标对象
        OrderService target = new OrderServiceImpl();
        // 创建代理对象
        OrderService proxy = new OrderServiceProxy(target);
        // 调用代理对象的代理方法
        proxy.generate();
        proxy.modify();
        proxy.detail();

GoF之代理模式之静态代理_代理类_06

这种方式的优点:符合OCP开闭原则,同时采用的是关联关系,所以程序的耦合度较低。所以这种方案是被推荐的

以上就是代理模式中的静态代理,其中OrderService接口是代理类和目标类的共同接口。OrderServiceImpl是目标类。OrderServiceProxy是代理类。

大家思考一下:如果系统中业务接口很多,一个接口对应一个代理类,显然也是不合理的,会导致类爆炸。怎么解决这个问题?

动态代理可以解决。因为在动态代理中可以在内存中动态的为我们生成代理类的字节码。代理类不需要我们写了。类爆炸解决了,而且代码只需要写一次,代码也会得到复用。

标签:begin,end,静态,GoF,代理,System,对象,public
From: https://blog.51cto.com/u_15784725/6446750

相关文章

  • TLE6208-6G-ASEMI代理英飞电机驱动芯片TLE6208-6G
    编辑:llTLE6208-6G-ASEMI代理英飞电机驱动芯片TLE6208-6G型号:TLE6208-6G品牌:Infineon(英飞凌)封装:SOP-28类型:LED驱动、汽车芯片TLE6208-6G产品概述TLE6208-6G是一款完全保护的六角半桥驱动器,专为汽车和工业运动控制应用而设计。该部件基于英飞凌智能电源技术SPT®,该技术允......
  • 代理IP出现错误代码429如何解决
    代理IP出现错误代码429主要是因为请求过多,即客户端向服务端发送请求过于频繁,超出服务器设置的请求限制,导致连接失败。这种问题通常出现在需要频繁获取数据或者进行大量操作时。那么,如何解决代理IP出现错误代码429呢?以下是一些可能的解决方法:1.增加请求间隔时间:可以尝试......
  • 代理IP出现错误代码504如何解决
    代理IP出现错误代码504是一种常见的网络错误,意思是网关超时,即在客户端向服务端发送请求后,代理服务器过了一段时间都没有响应,导致连接失败。这个问题通常会出现在需要从服务器获取大量数据或者处理复杂请求时。那么,如何解决代理IP出现错误代码504呢?以下是一些可能的解决方法......
  • Vue脚手架配置代理
    前言前端在向后端请求资源时通常会遇到跨域问题,当我们是用vue脚手架构建项目时,可以通过配置代理解决跨域问题参考文档:devServer.proxy方法一:在vue.config.js中添加如下配置:module.exports={devServer:{proxy:'http://localhost:4000'}}说明:(1)、优点:配置简......
  • TLE4250-2G-ASEMI代理英飞凌汽车芯片TLE4250-2G
    编辑:llTLE4250-2G-ASEMI代理英飞凌汽车芯片TLE4250-2G型号:TLE4250-2G品牌:Infineon(英飞凌)封装:SCT-595-5特性:驱动芯片、汽车芯片温度范围-40°C~150°C最大输入电压:-42V~45VTLE4250-2G产品特性50毫安输出电流能力最小热阻的微型SMD封装PG-SCT595-5低输出跟踪容差小......
  • TLE4250-2G-ASEMI代理英飞凌汽车芯片TLE4250-2G
    编辑:llTLE4250-2G-ASEMI代理英飞凌汽车芯片TLE4250-2G型号:TLE4250-2G品牌:Infineon(英飞凌)封装:SCT-595-5特性:驱动芯片、汽车芯片温度范围-40°C~150°C最大输入电压:-42V~45VTLE4250-2G产品特性50毫安输出电流能力最小热阻的微型SMD封装PG-SCT595-5低输出跟踪容差小型陶瓷输出电容......
  • 51静态数码管实验
    一、实验目的1、了解数码管共阴极和共阳极接法。2、了解数码管的段选和位选方式。3、了解定时器初始化设置的方法。二、实验内容1、完成数码管显示数字每秒加一,八个数码管全亮;完成数码管八段全亮,八个数码管全亮。1)实验要求:完成数码管八段全亮,八个数码管全亮,以此检验数码管是......
  • 01-mybatis-快速入门-代理开发、配置文件
    文章目录MybatisMybatis入门案例1、创建User表,添加数据2、创建模块,搭建框架2.1创建模块注意:完善项目目录2.2导入坐标2.3编写MyBatis核心配置文件2.4编写sql映射文件2.5编码3、解决SQL映射文件的警告提示Mapper代理开发1、定义同名接口2、设置namespace并修改核心配置mappe......
  • 深入浅出模板模式和动态代理
    模板模式模板模式是使用最频繁的设计模式之一,如果能正确的使用好模板模式,能使代码更加优雅,同时也便于后期维护和扩展。另外也是面试常问的设计模式之一。本次Chat分享的是易懂版模板模式,首先了解模板模式,然后会例举多个生活实例,再写一个案例,最后介绍Spring和Mybatis源码......
  • Java面试题查缺补漏习题,锁的升级,动态代理
    之前我们总结了Java面试题目中的关于计算机网络,操作系统,以及JVM虚拟机,以及Java的相关特性。今天又看了很多面试的视频,对面试的题目进行一下仔细的补充。1.对称加密与非对称加密的区别:非对称加密和对称加密在加密和解密过程、加密解密速度、传输的安全性上都有所不同,具体介绍如下:......