【重学设计模式】代理模式

这是我参与新手入门的第1篇文章。

前言

使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序的重用性。


代理模式介绍

  代理模式(Proxy Pattern)也称之为委托模式,其实代理模式在生活中随处可见,你想吃雪糕,但是呢你自己不想出门,并叫你女朋友去给你买冰激凌吃。这时女朋友帮你代理了去商店买的这个动作,女朋友就成了一个代理对象,而商店则是一个真实的对象。现实生活亦是如此,是否让你想到了一句话“艺术源于生活,而高于生活”

代理模式的定义

  为其他对象提供一种代理以控制对这个对象的访问。

理解代理模式

  代理模式呢分为静态代理和动态代理,模式的定义总是枯燥无味的,接下来我们以一个静态代理的简单实例。"女朋友帮我买冰激凌"来理解代理模式。

角色介绍

代理模式(Proxy)结构图.png

这里可以在敲完代码之后再回来看。

静态代理

  1. 在故事中,女朋友是代理人,需要代理的人是,首先定义好一个商店的接口
public interface Shop {
    IceCream getIceCream(@NotNull int type);
}
复制代码
  1. 定义商店实体类实现接口以及定义IceCream实体类
public class ShopImpl implements Shop{
    @Override
    public IceCream getIceCream(int type) {
        return new SingletonUtils<IceCream>(){
            @Override
            public IceCream create() {
                return new IceCream(type);
            }
        }.getInstance();
    }
}
复制代码
public class IceCream {

    private int type;

    public static final int MENGNIU = 1;
    public static final int BAXY = 1;

    public IceCream(int type) {
        this.type = type;
    }

    public int getType() {
        return type;
    }

    public static int getMoney(int type) {
        if(type == MENGNIU){
            return 10;
        }else if(type == BAXY){
            return 12;
        }
        return 0;
    }

    public static String getName(int type){
        if(type == MENGNIU){
            return "MENGNIU";
        }else if (type == BAXY){
            return "BAXY";
        }
        return "";
    }

    public void setType(int type) {
        this.type = type;
    }

    @Override
    public String toString() {
        return "IceCream{" +
                "type=" + type +
                '}';
    }
}

复制代码

SingletonUtils是参照/frameworks/base/core/java/android/util/Singleton.java封装的单例工具类。

public abstract class SingletonUtils<T> {
    private T instance;

    public abstract T create();

    public final T getInstance(){

        if(Objects.isNull(instance)){
            synchronized (this){
                if(Objects.isNull(instance)) instance = create();
            }
        }
        return instance;
    }
}
复制代码
  1. 定义女朋友代理类,他需要去商店购买冰激凌,所以他是代理对象并且同样实现接口,在内部持有目标对象的实例。
public class GirlFriend implements Shop {

    private ShopImpl shop;
    private int monkey;

    public GirlFriend(ShopImpl shop, int monkey) {
        this.shop = shop;
    }

    @Override
    public IceCream getIceCream(int type) {
        //买雪糕需要付钱呐,付款和买衣服属于前置增强,我们可以在需要代理的方法前后添加额外的操作.
        payment(IceCream.getMoney(type));
        buyClothes();
        IceCream iceCream = shop.getIceCream(type);
        return iceCream;
    }

    private void payment(int money){
    }
    private void buyClothes(){
        System.out.println("gril friend buy Clothes");
    }
}
复制代码
  1. 我是一个需求者,被代理者,我需要冰激凌,让女朋友去购买。
public class SaltedFish {

    private static ShopImpl shop = new ShopImpl();

    public static void main(String[] args) {
        //new 一个女朋友出来
        GirlFriend girlFriend = new GirlFriend(shop,100);
        //在女朋友那里拿到雪糕
        IceCream iceCream = girlFriend.getIceCream(IceCream.MENGNIU);
        System.out.println("I had " + iceCream.getName(iceCream.getType()));
    }
}
复制代码
  1. 我们来看运行结果,女朋友买了衣服,我拥有MENNIU冰激凌。

image.png

动态代理

JDK提供了一套的代理机制,它设计到俩个核心类,InvocationHandlerProxy

  Proxy类就是用来创建一个代理对象的类,它提供了很多方法,但是我们最常用的是newProxyInstance方法。

image.png

   InvocationHandler接口是proxy代理实例的调用处理程序实现的一个接口,每一个proxy代理实例都有一个关联的调用处理程序;在代理实例调用方法时,方法调用被编码分派到调用处理程序的invoke方法。

image.png

  上边我们用静态代理的方式去实现了”女朋友帮我买冰激凌的操作”,既然有静态必然存在动态。俩这优缺点用代码实现完之后讲。

public class ProxyFactory<T> implements InvocationHandler {

    private T t;

    public ProxyFactory(T t) {
        this.t = t;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        buyClothes();
        Object invoke = method.invoke(t, args);
        //TODO
        return invoke;
    }

    private void payment(int money){
    }
    private void buyClothes(){
        System.out.println("gril friend buy Clothes");
    }
}

复制代码
public class SaltedFish {

    private static ShopImpl shop = new ShopImpl();

    public static void main(String[] args) {
        Shop shopImpl = new ShopImpl();
        ProxyFactory girlFriend = new ProxyFactory(shop);
        Shop shop = (Shop)Proxy.newProxyInstance(shopImpl.getClass().getClassLoader(),
                shopImpl.getClass().getInterfaces(),girlFriend);
        IceCream iceCream = shop.getIceCream(IceCream.BAXY);
        System.out.println("I had " + iceCream.getName(iceCream.getType()));
    }
}
复制代码

上诉代码可以看到,我唯一改变了代理对象,ProxyFactory替换掉GirlFriend(小声哔哔女朋友怎么可能去给你买冰激凌吃,当然是点外卖了,可以将ProxyFactory想向成各个外卖平台)。

总结:代理对象在程序运行中,有代理工厂动态生产,代理对象本身不存在实体类。

动态代理有什么好的

   在静态代理中,女朋友去买雪糕,但是我又想和可乐怎么办呢?

  • 创建可乐售卖接口
  • GirlFriend和ShopImpl都需要实现该接口
  • SaltedFish访问GirlFriend买可乐

这样似乎也是可以的,那为什么需要动态代理呢?

   OK!我们来看一下六大设计原则中的开闭原则。

在面向对象编程领域中,开闭原则规定“软件中的对象(类,模块,函数等等)应该对于扩展是开放的,但是对于修改是封闭的”,这意味着一个实体是允许在不改变它的源代码的前提下变更它的行为。该特性在产品化的环境中是特别有价值的,在这种环境中,改变源代码需要代码审查,单元测试以及诸如此类的用以确保产品使用质量的过程。遵循这种原则的代码在扩展时并不发生改变,因此无需上述的过程。

所以,为了提高类的可扩展性和可维护性,满足开闭原则,Java 提供了动态代理机制。

结尾

其实在Android源码中也有很多用到代理模式,ActivityManagerProxy、Bingder等,如果你有去看ActivityManagerProxy这部分相关的源码就会看到Singleton.java。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享