Java设计模式之代理模式

142 阅读3分钟

在某种情况下,A对象不方便或者不能直接引用B对象时,可以创建一个代理提供给A对象,来控制对B对象的访问。

让我们用示例来演示代理模式的应用。

示例场景:

water对象可以被喝掉,它提供一个drink()方法,现在提供一个WaterProxy代理,由这个代理去执行drink()的动作。

在这种简单的场景下为什么还要写代理呢?
因为假如water对象有其他拓展性的功能,又不想给外部调用,由代理去做的话,外部就只能间接的调用drink()方法,这也起到了对water对象的保护作用。

创建一个Drink接口:

package com.cc.proxy;
​
/**
 * 代理类和被代理类需要共同实现的接口
 * @author cc
 * @date 21-12-18 18:05
 */
public interface Drink {
    void drink();
}

然后是water对象:

package com.cc.proxy;
​
/**
 * water对象
 * @author cc
 * @date 21-12-18 21:49
 */
public class Water implements Drink {
    @Override
    public void drink() {
        System.out.println("drink water");
    }
}

现在创建一个WaterProxy代理:

package com.cc.proxy;
​
/**
 * water代理
 * @author cc
 * @date 21-12-18 21:50
 */
public class WaterProxy implements Drink {
    private Water water;
​
    @Override
    public void drink() {
        // 这里只是模拟场景,不考虑高并发的情况
        if (water == null) {
            water = new Water();
        }
        water.drink();
    }
​
    public Water getWater() {
        return water;
    }
​
    public void setWater(Water water) {
        this.water = water;
    }
}

测试一下:

package com.cc.proxy;
​
import java.lang.reflect.Proxy;
​
public class Main {
    public static void main(String[] args) {
        WaterProxy waterProxy = new WaterProxy();
        waterProxy.drink();
    }
}

结果:

drink water

可以看到,drink()的动作由WaterProxy代理去执行,调用者不知道被代理的对象是water,代理在这个过程中起到了中介的作用。

优点(摘自百度百科):

  • 职责清晰,被代理的对象如示例中的water只需要关心自己的业务,通过将自身的部分功能提供给外部代理,让代理去完成一个业务,这使得各自的工作职责清晰,代码也显得简洁
  • 代理对象作为中间者,可以起到保护被代理对象的作用
  • 拓展性高

不足:一个Water对象需要有一个WaterProxy代理,现在我们新增了一个Milk对象,有需要有一个MilkProxy代理,这使得我们的代理类变多,并且这些代理类负责的工作都是一样的。还好,Java有动态代理。

静态代理和动态代理

上面的例子中,water对象的代理类需要提前写好,这种就叫做静态代理。在静态代理中,新增milk对象就要新增新的代理类,当我们的业务庞大起来后,代理类的部分也会继续庞大起来。

像water、milk又或者Coffee,他们的代理类做的工作都是一样的,既然如此,我们可以用Java提供的java.lang.reflect.Proxy类去动态生成代理对象,减少这些重复的代理对象类的编写,这种就叫动态代理

示例代码

代码摘自简书:java中的代理模式

创建一个实现了InvocationHandler接口的实现类:

package com.cc.proxy;
​
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
​
public class CommonInvocationHandler implements InvocationHandler {
    // 被代理的对象
    private Object proxied;
​
    public CommonInvocationHandler(Object proxied) {
        this.proxied = proxied;
    }
​
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 在调用被代理对象的方法前做一些事情
        System.out.println("before doing something");
        // 调用被代理对象的方法
        Object result = method.invoke(proxied, args);
        // 在调用被代理对象的方法后做一些事情
        System.out.println("after doing something");
        return result;
    }
}

新增一个Milk对象来增强演示效果:

package com.cc.proxy;
​
/**
 * milk对象
 * @author cc
 * @date 21-12-18 22:07
 */
public class Milk implements Drink {
    @Override
    public void drink() {
        System.out.println("drink milk");
    }
}

测试一下:

package com.cc.proxy;
​
import java.lang.reflect.Proxy;
​
public class Main {
    public static void main(String[] args) {
        {
            Water water = new Water();
            Drink proxy = (Drink) Proxy.newProxyInstance(water.getClass().getClassLoader(), water.getClass().getInterfaces(), new CommonInvocationHandler(water));
            proxy.drink();
        }
​
        {
            Milk milk = new Milk();
            Drink proxy = (Drink) Proxy.newProxyInstance(milk.getClass().getClassLoader(), milk.getClass().getInterfaces(), new CommonInvocationHandler(milk));
            proxy.drink();
        }
    }
}

结果:

before doing something
drink water
after doging something
before doing something
drink milk
after doging something

可以看到,我们不再需要创建像WaterProxy、MilkProxy这样功能重复的代理类,全部都是动态生成的。