第二梦 设计模式

Posted yrml

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第二梦 设计模式相关的知识,希望对你有一定的参考价值。

什么是代理

具体的做某一件事情的时候,不通过具体做事的人来完成,而是通过某一个中间人来完成,但实际上做事的人还是具体的人。这就是代理。比如有A和B两个人,B能购买亚瑟王手办,但是想买手办的C并不能联系到B。但是A可以联系到B,C又可以联系到A,这个时候C对A说我想买亚瑟王手办,A说我可以找B帮你,A这个时候就起到代理的作用

代理的应用场景

在实际生活中的代理有好多种应用场景,在不同场景下,我们使用的代理方式及代理过程也不尽相同,具体如下:

  • 远程代理:为一个对象在不同的地址空间提供局部代表
  • 虚拟代理:当需要创建开销非常大的对象时,通过使用一个小的对象代理一个大对象

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

  • 智能引用代理:当需要在访问对象时附加额外的操作时(耗时操作、计算访问次数)

  • 防火墙代理:保护目标不让恶意的用户靠近

  • Cache代理:为结果提供临时的存储空间,以便其他客户端调用

代码实战

  • 静态代理 

    可以做到在不修改目标对象的功能前提下,对目标功能进行扩展
    代理类和委托类实现了相同的接口,同时要实现相同的方法,重复代码。接口新增方法,所有的实现类和代理类都需要实现此方法,增加了代码维护的复杂度

1、首先定义一个商店性质的接口<IStore>

 1     /**
 2      * 卖书
 3      */
 4     void saleBook();
 5 
 6     /**
 7      * 卖吃的
 8      */
 9     void saleFood();
10 
11     /**
12      * 开发票
13      */
14     void invocing();

2、完成具体干事情的接口实现类<TaoBao、JingDong>

public class TaoBao implements IStore {
@Override
public void saleBook() {
System.out.println("【掏宝】Java书籍应有尽有...");
}

@Override
public void saleFood() {
System.out.println("【掏宝】Java造型食物应有尽有...");
}

@Override
public void invocing() {
System.out.println("【掏宝】我的发票带Java字样哟...");
}
}

public class JingDong implements IStore {

@Override
public void saleBook() {
System.out.println("【竞东】C++书籍走过路过不要错过...");
}

@Override
public void saleFood() {
System.out.println("【竞东】C++食物走过路过不要错过...");
}

@Override
public void invocing() {
System.out.println("【竞东】我们的发票自带C++字样哟...");
}
}

3、完成一个干代理工作的代理类,这里也实现商店性质接口<ProxyStore>,这里通过批人构造函数实现默认代理,通过提供带参构造函数实现指定代理。具体请查看测试。

 1 
2
3 /** 4 * 默认代理出售掏宝的东西 5 */ 6 public ProxyStore() { 7 this.store = new TaoBao(); 8 } 9 10 /** 11 * 扩展出售指定渠道的货物 12 * @param store 渠道 13 */ 14 public ProxyStore(IStore store) { 15 this.store = store; 16 } 17 18 @Override 19 public void saleBook() { 20 store.saleBook(); 21 } 22 23 @Override 24 public void saleFood() { 25 store.saleFood(); 26 } 27 28 @Override 29 public void invocing() { 30 store.invocing(); 31 }

4、消费者购买东西<SomeOne>,请注意上一步3提到的指定代理

 1     ProxyStore store;
 2     store = new ProxyStore();
 3     System.out.println("------买代理者默认代理的------");
 4     store.saleBook();
 5     store.saleFood();
 6     store.invocing();
 7     System.out.println("\\n");
 8 
 9     store = new ProxyStore(new JingDong());
10     System.out.println("------指定代理者某渠道的货物------");
11     store.saleBook();
12     store.saleFood();
13     store.invocing();

5、输出结果

 1 ------买代理者默认代理的------
 2 【掏宝】Java书籍应有尽有...
 3 【掏宝】Java造型食物应有尽有...
 4 【掏宝】我的发票带Java字样哟...
 5 
 6 
 7 ------指定代理者某渠道的货物------
 8 【竞东】C++书籍走过路过不要错过...
 9 【竞东】C++食物走过路过不要错过...
10 【竞东】我们的发票自带C++字样哟...
  • 动态代理

前面的静态代理,是在代码编译的时候就写好代理类,而动态代理则是在程序运行过程中动态的生成代理类。而且比静态类好的是不用写一个与具体动作完全相同的封装类。使用动态代理还可以在不改变原有实现的情况下,对原有实现进行增强。

  • JDK动态代理

1、定义具体实现类

1 public class BianLiDian {
2 
3     public void sale(){
4         System.out.println("【商店】这是一瓶原生牛奶...");
5     }
6 }

2、定义代理类

 1 public class ProxyFactory {
 2 
 3     /**
 4      * 维护目标对象
 5      */
 6     private Object target;
 7 
 8     public ProxyFactory(Object target) {
 9         this.target = target;
10     }
11 
12     /**
13      * 给目标对象生成代理对象
14      */
15     public Object getProxyInstance(){
16         return Proxy.newProxyInstance(
17                 //获取目标对象的类加载器
18                 target.getClass().getClassLoader(),
19                 //获取目标对象的接口信息
20                 target.getClass().getInterfaces(),
21                 /*
22                  * InvocationHandler is the interface implemented by the
23                  * invocation handler of a proxy instance.
24                  * Each proxy instance has an associated invocation handler.
25                  * When a method is invoked on a proxy instance,
26                  * the method invocation is encoded and dispatched to the
27                  * invoke method of its invocation handler.
28                  */
29                 new InvocationHandler() {
30                     @Override
31                     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
32                         //这里可以通过获取方法的name,实现对不同方法不同的增强处理
33                         System.out.println("买东西前确认银行卡余额...");
34                         method.invoke(target, args);
35                         System.out.println("确认收货");
36                         return null;
37                     }
38                 }
39         );
40     }
41 }

3、消费者测试

 1 public static void main(String[] args) {
 2     IStore target = new TaoBao();
 3     //对象的原始类型
 4     System.out.println(target.getClass());
 5 
 6     IStore proxy = (IStore) new ProxyFactory(target).getProxyInstance();
 7     //内存中动态代理生成的对象类型
 8     System.out.println(proxy.getClass());
 9 
10     //指定具体需要代理的对象
11     proxy.saleFood();
12 
13 }

4、执行结果

1   class com.yrml.proxy.store.impl.TaoBao
2   class com.sun.proxy.$Proxy0
3   买东西前确认银行卡余额...
4   【掏宝】Java造型食物应有尽有...
5   确认收货
  • CGLIB动态代理

采用字节码技术,通过目标类的字节码为目标类创建子类,在子类中采用方法拦截技术对父类方法的调用进行拦截,顺势织入横切逻辑
底层采用自己处理框架ASM,来转换字节码并生成新类

1、代理类变更

package com.yrml.proxy.cglib;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * @des
 * @author John Tan
 * @date 2018/5/22
 */
public class ProxyCglib implements MethodInterceptor {

    private Object target;

    public ProxyCglib(Object target) {
        this.target = target;
    }

    /**
     * 获取目标对象的代理对象
     */
    public Object getProxyInstance(){
        //字节码增强器工具类
        Enhancer enhancer = new Enhancer();
        //设置父类,继承代理类
        enhancer.setSuperclass(target.getClass());
        //设置回调函数
        enhancer.setCallback(this);
        //创建子类(代理对象)
        return enhancer.create();

    }

    /**
     * 回调方法
     * @param obj 代理对象
     * @param method 委托方法
     * @param args 方法参数
     * @param proxy 代理方法的MethodProxy对象
     * @return 方法执行结果
     * @throws Throwable 异常
     */
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("【代理】我们将为您送上一杯热牛奶...");
        System.out.println("【代理】首先从商店购买...");
        method.invoke(target, args);
        System.out.println("【代理】加热...");
        System.out.println("【代理】这是您的热牛奶...");
        return null;
    }
}

2、消费者测试

 1 public class ProxyTest3 {
 2 
 3     public static void main(String[] args) {
 4 
 5         BianLiDian bianLiDian = new BianLiDian();
 6         System.out.println(bianLiDian.getClass());
 7 
 8         BianLiDian proxy = (BianLiDian) new ProxyCglib(bianLiDian).getProxyInstance();
 9         proxy.sale();
10     }
11 }

3、执行结果

1 class com.yrml.proxy.store.impl.BianLiDian
2 【代理】我们将为您送上一杯热牛奶...
3 【代理】首先从商店购买...
4 【商店】这是一瓶原生牛奶...
5 【代理】加热...
6 【代理】这是您的热牛奶...

代码托管

该实例托管在我的 GitHub上,为gradle项目,小伙伴可以直接检出构建运行测试

参考文章

  1. 代理模式原理及实例讲解
  2. Java的三种代理模式
  3. 设计模式学习笔记(十三)----Proxy代理模式

以上是关于第二梦 设计模式的主要内容,如果未能解决你的问题,请参考以下文章

分享几个实用的代码片段(第二弹)

分享几个实用的代码片段(第二弹)

使用导航抽屉旋转时的片段更改

第二次在对话框中膨胀片段时出错

将片段用于横向视图和 ViewPager

用片段替换时操作栏向下移动