设计模式之—代理模式

1,481 阅读4分钟

概述

在有些情况下,客户不能或者不想直接访问目标对象,这时候需要通过代理进行访问目标对象,例如:客户通过火车站代销点购买车票,客户通过“代理”或者“中介”进行海淘,这就是代理模式的原型已经在现实生活的例子

定义和特点

定义:为一个目标对象提供一个代理以提供对这个类的访问

优点

  • 代理在客户和目标对象之间,使客户和目标对象分离,降低了系统的耦合度
  • 代理对象可以扩充目标对象的功能

缺点:

  • 在客户端和目标对象间增加了一个代理对象,会造成请求速度变慢
  • 增加了额外的类,增加了系统的复杂度

实现

(代理模式的UML类图)

如上图所示我们对各个类进行一个说明

  • Cliect(客户):用户,使用代理类的入口
  • subject (抽象目标类):抽象或者接口类,目标类和代理类的共同接口方法
  • RealSubject(真实目标类):实现了抽象目标类,定义了代理所表示的真实对象,负责具体的业务实现
  • Proxy(代理类):持有真实目标类的引用,在所实现的接口方法中调用真实目标类中的方法

下面写一个demo

//抽象购物类
public interface Shopping {
    Object[] doShopping(long money);
}
/**
 * 真实购物类
 */
public class ShoppingImpl implements Shopping {
    public static final String TAG = ShoppingImpl.class.getSimpleName();
    @Override
    public Object[] doShopping(long money) {
        Log.e(TAG,"逛淘宝 ,逛商场,买买买!!");
        Log.e(TAG,"花了"+money+"元");
        Log.e(TAG,"买了  鞋子,衣服,零食");
        return new Object[]{"鞋子", "衣服", "零食" };
    }
}
/**
 * 代理购物类
 */
public class ShoppingProxy implements Shopping {
    private Shopping base;

    public ShoppingProxy(Shopping shopping){
        this.base = shopping;
    }



    @Override
    public Object[] doShopping(long money) {
        // 帮忙买东西
        Object[] things = base.doShopping(money);

        // 偷梁换柱(修改返回值)
        if (things != null && things.length > 1) {
            things[0] = "被掉包的东西!!";
        }

        return things;
    }
}
     /**
     * 客户类
     */
    private void operate2(){
        Shopping shopping = new ShoppingProxy(new ShoppingImpl());
        Log.e(TAG,Arrays.toString(shopping.doShopping(100)));
    }

如上所以就是一个简单的代理类,这是一个静态代理

动态代理

静态代理模式需要为每一个需要代理的类写一个代理类,如果需要代理的类有几百个那不是要累死?为了更优雅地实现代理模式,JDK提供了动态代理方式

/**
 * 动态代理类
 */
public class ShopHandler implements InvocationHandler {
    public static final String TAG = ShopHandler.class.getSimpleName();

    public Object base;

    public ShopHandler(Object base){
        this.base = base;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if ("doShopping".equals(method.getName())) {
            // 这里是代理Shopping接口的对象

            // 先黑点钱(修改输入参数)
            Long money = (Long) args[0];
            long readCost = (long) (money * 0.5);
            Log.e(TAG,"花了"+readCost+"元");
            // 帮忙买东西
            Object[] things = (Object[]) method.invoke(base, readCost);

            // 偷梁换柱(修改返回值)
            if (things != null && things.length > 1) {
                things[0] = "被掉包的东西!!";
            }

            return things;
        }

        if ("doSomething".equals(method.getName())) {
            // 可以代理别的,做些别的事情
            return null;
        }

        if ("doSomethingElse".equals(method.getName())) {
            // 做些别的事情
            return null;
        }

        return null;
    }
}
 /**
     * 客户端类
     */
    private void operate() {
        Shopping shop = new ShoppingImpl();
        // 正常购物
        Log.e(TAG, Arrays.toString(shop.doShopping(100)));
        shop = (Shopping) Proxy.newProxyInstance(Shopping.class.getClassLoader(), shop.getClass().getInterfaces(),new ShopHandler(shop));
        Log.e(TAG, Arrays.toString(shop.doShopping(100)));

    }

如上所示就实现了动态代理

静态代理与动态代理

  • 静态代理:程序在运行前代理类就已经存在了
  • 动态代理:代理类在程序运行期间根据反射动态生成

应用场景

当一个对象不能或者不想直接访问另一个对象时,可以通过一个代理对象来间接访问。为保证客户端使用的透明性,委托对象和代理对象要实现同样的接口

被访问的对象不想暴露全部内容时,可以通过代理去掉不想被访问的内容

根据适用范围,代理模式可以分为以下几种:

  • 远程代理:为一个对象在不同的地址空间提供局部代表,这样系统可以将Server部分的事项隐藏。

  • 虚拟代理:如果要创建一个资源消耗较大的对象,可以先用一个代理对象表示,在真正需要的时候才真正创建。

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

  • 智能引用:在引用原始对象的时候附加额外操作,并对指向原始对象的引用增加引用计数