Skip to content

代理模式

定义

为其他对象提供一种代理以控制对这个对象的访问。
也叫委托模式。许多其他模式,如状态模式、策略模式、访问者模式本质上是在更特殊的场合采用了委托模式。

主要有三个角色:

  • Subject抽象主题角色。 可以是抽象类或接口,是一个最普通的业务类型定义,无特殊要求。
  • RealSubject具体主题角色。 也叫被委托角色、被代理角色。是具体业务的执行者。
  • Proxy代理主题角色。 也叫委托类、代理类。负责对真实角色的应用,可以添加自定义的操作,例如预操作和善后处理。

RealSubject和Proxy都实现或继承Subject抽象主题角色。(个人倾向于接口,但要看实际情况来定)

优点

  • 职责清晰 - 真实角色只负责实现实际的业务逻辑,不用关系其他事务。代理可以完成更多工作。
  • 高扩展性 - 只要实现了接口,具体主题角色的实现可以高度地改变,而不用改代理。

扩展

普通代理

要求客户端只能访问代理角色,而不能访问真实角色。
实际工作中,一般通过约定来禁止new一个真实角色。

普通代理示例

文件目录

IBaseFunction.java
RealUser.java
UserProxy.java

抽象主题角色 IBaseFunction.java

public interface IBaseFunction {
    void talk();
}

具体主题角色RealUser.java

public class RealUser implements IBaseFunction {
    @Override
    public void talk() {
        System.out.println("Real user talk sth.");
    }
}

代理主题角色 UserProxy.java
可以执行自定义的预操作和后续处理操作。

public class UserProxy implements IBaseFunction {

    private RealUser realUser;

    public UserProxy() {
        if (this.realUser == null) {
            this.realUser = new RealUser();
        }
    }

    @Override
    public void talk() {
        prepare();
        this.realUser.talk();
        finishWork();
    }

    private void prepare() {
        System.out.println("Proxy is preparing...");
    }

    private void finishWork() {
        System.out.println("Proxy has done");
    }
}

测试示例

    UserProxy userProxy = new UserProxy();
    userProxy.talk();

输出

Proxy is preparing...
Real user talk sth.
Proxy has done

强制代理

从真实角色找到代理角色,不允许直接操作真实角色。真实角色管理代理角色。
高层模块只要调用getProxy(获取代理)就可以访问真实角色的所有方法。

强制代理代码示例

文件目录。这里以Robot类为真实角色。

IRobot.java
Robot.java
RobotProxy.java

IRobot 定义了3个行为

public interface IRobot {

    void login(String name, String password);

    void walk();

    void fly();
}

RobotProxy 代理中有一些准备工作。

public class RobotProxy implements IRobot {

    private IRobot realRobot = null;

    public RobotProxy(IRobot _robot) {
        this.realRobot = _robot;
    }

    @Override
    public void login(String name, String password) {
        this.realRobot.login(name, password);
    }

    @Override
    public void walk() {
        System.out.println("Proxy prepares to walk");
        this.realRobot.walk();
    }

    @Override
    public void fly() {
        System.out.println("Proxy prepares to fly");
        this.realRobot.fly();
    }
}

真实角色Robot,会检查是否给自己设置了代理。如果没有代理,则不能操作。
真实角色Robot有自己的name,提供用户登录功能login(String name, String password)

public class Robot implements IRobot {

    private String name = "";
    private IRobot proxy = null;

    public Robot(String _name) {
        this.name = _name;
    }

    public String getName() {
        return name;
    }

    // get own proxy
    public IRobot getProxy() {
        this.proxy = new RobotProxy(this);
        return this.proxy;
    }

    private boolean isProxyWorking() {
        return null != this.proxy;
    }

    @Override
    public void login(String name, String password) {
        if (isProxyWorking()) {
            printf("user login: " + name);
        } else {
            outError();
        }
    }

    @Override
    public void walk() {
        if (isProxyWorking()) {
            printf(this.name + " is walking.");
        } else {
            outError();
        }
    }

    @Override
    public void fly() {
        if (isProxyWorking()) {
            printf(this.name + " is flying.");
        } else {
            outError();
        }
    }

    private void printf(String msg) {
        System.out.println(msg);
    }

    private void outError() {
        printf(this.name + ": DENY! Please use proxy!");
    }

}

测试代码:new一个名为"Wall-E"的Robot,不同的用户试图操作这个Robot
第一次尝试直接操作,会得到失败信息。然后获取robot的代理robotProxy进行操作。

    private static void testRobotProxy() {
        Robot robot = new Robot("Wall-E");
        robot.login("Tom", "pwd");
        robot.walk();
        robot.fly();
        System.out.println("");
        RobotProxy robotProxy = (RobotProxy) robot.getProxy();
        robotProxy.login("Jerry", "123");
        robotProxy.walk();
        robotProxy.fly();
    }

输出

Wall-E: DENY! Please use proxy!
Wall-E: DENY! Please use proxy!
Wall-E: DENY! Please use proxy!

user login: Jerry
Proxy prepares to walk
Wall-E is walking.
Proxy prepares to fly
Wall-E is flying.

代理扩展实现不同任务

代理类不仅仅可以实现主体接口,也可以实现其他接口完成不同的任务。也可以实现自己的职责。

基于强制代理扩展代理类的功能代码

新增接口

public interface IMessage {
    String say(String msg);
}

代理类增加实现接口

public class RobotProxy implements IRobot, IMessage {

    private IRobot realRobot = null;

    public RobotProxy(IRobot _robot) {
        this.realRobot = _robot;
    }

    @Override
    public void login(String name, String password) {
        this.realRobot.login(name, password);
    }

    @Override
    public void walk() {
        System.out.println("Proxy prepares to walk");
        this.realRobot.walk();
    }

    @Override
    public void fly() {
        System.out.println("Proxy prepares to fly");
        this.realRobot.fly();
        say("Proxy: Flying high!");
    }

    @Override
    public String say(String msg) {
        System.out.println(msg);
        return msg;
    }
}

运行结果(部分)

user login: Jerry
Proxy prepares to walk
Wall-E is walking.
Proxy prepares to fly
Wall-E is flying.
Proxy: Flying high!

动态代理

在实现阶段不用关心代理谁,而在运行阶段才指定代理哪一个对象。
AOP(Aspect Oriented Programming)的核心

动态代理代码示例

文件目录

IVehicle
Tank
VehicleIH

定义车辆接口 IVehicle

public interface IVehicle {

    void getIn(String username);

    void forward();

    void shoot();
}

定义一个类 Tank 实现 IVehicle 接口。这是真实角色。

public class Tank implements IVehicle {

    private String name = "";

    public Tank(String _name) {
        this.name = _name;
    }

    @Override
    public void getIn(String username) {
        System.out.println(username + " got in tank");
    }

    @Override
    public void forward() {
        System.out.println(this.name + " forward");
    }

    @Override
    public void shoot() {
        System.out.println(this.name + " shoot!");
    }
}

动态代理类,实现InvocationHandler接口
判断使用的方法名,并发出自定义通知

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class VehicleIH implements InvocationHandler {
    Class cls = null;
    Object obj = null;

    public VehicleIH(Object _obj) {
        this.obj = _obj;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String methodName = method.getName();
        if (methodName.equalsIgnoreCase("getIn")) {
            System.out.println("VehicleIH: Somebody got in!");
        } else if (methodName.equalsIgnoreCase("forward")) {
            System.out.println("VehicleIH: Forward!");
        }
        return method.invoke(this.obj, args);
    }
}

测试代码

IVehicle tank = new Tank("59");
InvocationHandler handler = new VehicleIH(tank);
ClassLoader cl = handler.getClass().getClassLoader();
IVehicle proxy = (IVehicle) 
    Proxy.newProxyInstance(cl, new Class[]{IVehicle.class}, handler);
proxy.getIn("Conductor Liu");
proxy.forward();
proxy.shoot();

输出

VehicleIH: Somebody got in!
Conductor Liu got in tank
VehicleIH: Forward!
59 forward
59 shoot!

参考: 《设计模式之禅》 秦小波