把书读薄 | 《设计模式之美》设计模式与范式(结构型-代理模式)(上)

简介: 本文对应设计模式与范式:结构型(48),代理模式 (Proxy Pattern)。

跟前面学的 创建型 设计模式:


用来解决对象创建问题 (封装复杂的创建过程,解耦对象的创建代码和使用代码)


不同,结构型 设计模式:


总结了一些类或对象组合在一起的经典结构 (用于解决特定应用场景的问题)


本节先来探探代理模式,知道模式是啥,应用场景,静态代理和动态代理就差不多了~


二手知识加工难免有所纰漏,感兴趣有时间的可自行查阅原文,谢谢。


0x1、定义和应用场景


代理,这个概念很好理解,举个形象化的例子:


以前国内访问不了Android官网,开发者需要 科学上网 才能访问,利用中间的桥梁就是 代理服务器


网络异常,图片无法展示
|


通过中间人 → 代理服务器,我们从 直接 访问Android官方变成 间接 访问。


而开发中的代理模式:在不改变原始类(被代理类)的情况下,通过引入代理类为原始类附加功能


角色有下面三个:


网络异常,图片无法展示
|


  • 抽象主题类:声明公用方法,给后面两个角色实现,一般是接口形式;


  • 实现主题类:实现抽象主题类的方法,最终的操作对象;


  • 代理类:实现抽象主题类,包含对实现主题类对象的操作;


看着很高深,其实不然,等下看看静态代理的例子就秒懂了,先说下代理模式的应用场景


  • 保护代理 → 控制目标对象的访问,给不同用户提供不同的访问权限;


  • 智能引用代理 → 访问对象时附加额外操作,业务系统开发非功能性需求(监控、统计、日志等)


  • 虚拟代理 → 延迟初始化,创建小对象代理大对象(包含大量IO资源)的创建,大对象真正需要时才创建;


  • 远程代理 → 需要本地执行远程服务代码的场景,中间件领域,如远程代理框架gRpc、Dubbo等;


  • 缓存代理 → 缓存客户端请求结果并对缓存生命周期进行管理的场景;

说完场景,接着说下静态代理和动态代理的区别:


  • 静态代理 → 代理类的字节码在 运行前 就编译好;


  • 动态代理 → 代理类的字节码在 运行时 由虚拟机中的程序自动创建;


0x2、静态代理示例


代理模式一般就是用静态代理实现的,写个简单的代购例子:


// 抽象主题
public interface ShoppingOverseas {
    void shopping(String thing);
}
// 主题实现类
public class OverseasShop implements ShoppingOverseas {
    @Override
    public void shopping(String thing) {
        System.out.println("海外商家卖出一件:" + thing);
    }
}
// 代理类
public class ShoppingProxy implements ShoppingOverseas {
    OverseasShop shop = new OverseasShop();
    @Override
    public void shopping(String thing) {
        System.out.println("代购收到你的下单信息");
        shop.shopping(thing);
        System.out.println("代购帮你在海外商家买了:" + thing);
    }
}
// 测试用例
public class ProxyTest {
    public static void main(String[] args) {
        ShoppingProxy proxy = new ShoppingProxy();
        proxy.shopping("鞋子");
    }
}


输出结果如下:


网络异常,图片无法展示
|


上面这种是中规中矩的代理模式:原始类和代理类都实现相同接口,实际开发中可没那么好的事,代码很多时候不是我们开发维护的(如第三方类库),没法直接修改原始类,给它重新定义一个接口。


如果硬是要上代理模式,可以采用 继承原始类重写方法 的方式变通,重写原始类方法或新增方法。


另外,静态代理还存在一个问题:类Double,要添加附加功能的原始类很多的话,直接爆炸,而且要写很多"重复"代码,增加了不必要的开发成本。这种情况可以通过 动态代理(Dynamic Proxy) 来解决:


在运行时,动态地创建对应的代理类,然后在系统中用代理类替换掉原始类。


0x2、Java中的动态代理


Java中,常见的动态代理实现方式有两种:


  • JDK动态代理 → 底层依赖反射机制,被代理类要实现接口方法,通过invokeHandler对所需方法进行增强;


  • CGLIB代理 → 利用ASM框架,修改字节码生成子类来处理;


① JDK动态代理示例


玩法如下:


  • ① 定义代理接口;


  • ② 真实对象实现这个代理接口;


  • ③ 定义动态代理类,实现 InvocationHandler 接口,重写invoke()方法,做些小动作;


  • ④ 调用Proxy类创建的代理类;


代码示例如下


// 代理接口
public interface Shopping {
    void shopping(String thing);
    void pay();
}
// 实现代理接口的真实对象(这里定义了2个)
public class FirstShoppingImpl implements Shopping {
    @Override
    public void shopping(String thing) { System.out.println("在一号商家购买:" + thing); }
    @Override
    public void pay() { System.out.println("在一号商家付款"); }
}
public class SecondShoppingImpl implements Shopping {
    @Override
    public void shopping(String thing) { System.out.println("在二号商家购买:" + thing); }
    @Override
    public void pay() { System.out.println("在二号商家付款"); }
}
// 动态代理类
public class DynamicShoppingProxy implements InvocationHandler {
    private Shopping object;  // 委托类对象
    public DynamicShoppingProxy(Shopping object) {
        this.object = object;
    }
    /**
     * @param proxy 被代理的对象
     * @param method 被代理对象的方法
     * @param args 方法的参数
     * */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (method.getName().equals("shopping")) {
            System.out.println("开始购物...");
            // 反射调用类里的实际方法,返回方法的返回值,没有返回值的话返回null
            method.invoke(object, args);    
            System.out.println("结束购物...");
        } else if (method.getName().equals("pay")) {
            System.out.println("开始付款...");
            method.invoke(object, args);
            System.out.println("结束付款...");
        }
        return null;
    }
}
// 测试用例:调用Proxy类创建的代理类
public class DynamicProxyTest {
    public static void main(String[] args) {
        // 生成的动态代理文件保存到当前项目的com/sun/proxy目录下
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        // 创建动态代理实例
        DynamicShoppingProxy proxy1 = new DynamicShoppingProxy(new FirstShoppingImpl());
        DynamicShoppingProxy proxy2 = new DynamicShoppingProxy(new SecondShoppingImpl());
        // newProxyInstance()动态生成代理类,参数依次为:类加载器(要仅限代理的类)、被代理类实现的接口,动态代理实例
        Shopping s1 = (Shopping) (Proxy.newProxyInstance(Shopping.class.getClassLoader(),
                new Class[]{Shopping.class}, proxy1));
        System.out.println(s1.getClass().getName());
        s1.shopping("鞋子");
        Shopping s2 = (Shopping) (Proxy.newProxyInstance(Shopping.class.getClassLoader(),
                new Class[]{Shopping.class}, proxy2));
        System.out.println(s2.getClass().getName());
        s2.shopping("衣服");
        s2.pay();
    }
}


相关文章
|
2天前
|
设计模式 缓存 安全
设计模式——代理模式
静态代理、JDK动态代理、Cglib 代理
设计模式——代理模式
|
29天前
|
设计模式 缓存 Java
【十一】设计模式~~~结构型模式~~~代理模式(Java)
文章详细介绍了代理模式(Proxy Pattern),这是一种对象结构型模式,用于给对象提供一个代理以控制对它的访问。文中阐述了代理模式的动机、定义、结构、优点、缺点和适用环境,并探讨了远程代理、虚拟代理、保护代理等不同代理形式。通过一个商务信息查询系统的实例,展示了如何使用代理模式来增加身份验证和日志记录功能,同时保持客户端代码的无差别对待。此外,还讨论了代理模式在分布式技术和Spring AOP中的应用,以及动态代理的概念。
【十一】设计模式~~~结构型模式~~~代理模式(Java)
|
1月前
|
设计模式
设计模式的基础问题之代理模式在工作中的问题如何解决
设计模式的基础问题之代理模式在工作中的问题如何解决
|
2月前
|
设计模式 算法 Go
iLogtail设计模式问题之代理模式在iLogtail中是如何应用的
iLogtail设计模式问题之代理模式在iLogtail中是如何应用的
|
2月前
|
设计模式 缓存 JavaScript
js设计模式【详解】—— 代理模式
js设计模式【详解】—— 代理模式
23 0
|
2天前
|
设计模式 算法 安全
设计模式——模板模式
模板方法模式、钩子方法、Spring源码AbstractApplicationContext类用到的模板方法
设计模式——模板模式
|
29天前
|
设计模式
设计模式-单一职责模式
设计模式-单一职责模式
|
29天前
|
设计模式 XML 存储
【二】设计模式~~~创建型模式~~~工厂方法模式(Java)
文章详细介绍了工厂方法模式(Factory Method Pattern),这是一种创建型设计模式,用于将对象的创建过程委托给多个工厂子类中的某一个,以实现对象创建的封装和扩展性。文章通过日志记录器的实例,展示了工厂方法模式的结构、角色、时序图、代码实现、优点、缺点以及适用环境,并探讨了如何通过配置文件和Java反射机制实现工厂的动态创建。
【二】设计模式~~~创建型模式~~~工厂方法模式(Java)
|
29天前
|
设计模式 XML Java
【一】设计模式~~~创建型模式~~~简单工厂模式(Java)
文章详细介绍了简单工厂模式(Simple Factory Pattern),这是一种创建型设计模式,用于根据输入参数的不同返回不同类的实例,而客户端不需要知道具体类名。文章通过图表类的实例,展示了简单工厂模式的结构、时序图、代码实现、优缺点以及适用环境,并提供了Java代码示例和扩展应用,如通过配置文件读取参数来实现对象的创建。
【一】设计模式~~~创建型模式~~~简单工厂模式(Java)
|
1月前
|
设计模式 uml C语言
设计模式----------工厂模式之简单工厂模式(创建型)
这篇文章详细介绍了简单工厂模式,包括其定义、应用场景、UML类图、通用代码实现、运行结果、实际应用例子,以及如何通过反射机制实现对象创建,从而提高代码的扩展性和维护性。
设计模式----------工厂模式之简单工厂模式(创建型)