一、代理模式概念
所谓代理模式,看名字大家就知道是通过代理类来间接的访问目标对象。
在足球比赛中一个比较形象的反映就是:主教练把队长(或其他队员,这里就指定为队长吧)叫到场边告诉他:叫 XXX 参与防守或叫 XXX 压上进攻。这时:队长就是代理类,而 XXX 就是主教练想要访问的目标对象。下面就基于以上场景来理解代理模式。
二、使用场景
在一个对象不适合或者不能直接引用另一个对象时,我们可以使用代理模式来创建代理类在客户端和目标对象之间起到中介的作用。
三、结构
代理模式由三个部分组成:
1. 接口(Action):代理类与目标对象共同实现同一个接口。
2. 代理类(Captain):由他来代理向两个目标对象传递指令。
3. 目标对象(Player):具体执行命令的对象。
四、实现
开始上干货了。
1. 定义接口
public interface Action {
void attack(); //进攻方法
void control(); //控制球权方法
void defend(); //防守方法
}
接口中定义了三个球员的行动:进攻,控制和防守。
2. 定义实际访问对象
public class PlayerA implements Action {
@Override
public void attack() {
System.out.println("playerA 开始进攻");
}
@Override
public void control() {
System.out.println("playerA 控制球权");
}
@Override
public void defend() {
System.out.println("playerA 参与防守");
}
}
public class PlayerB implements Action {
@Override
public void attack() {
System.out.println("playerB 开始进攻");
}
@Override
public void control() {
System.out.println("playerB 控制球权");
}
@Override
public void defend() {
System.out.println("playerB 参与防守");
}
}
这里我们定义了两个访问对象,球员 A 和球员 B,他们都实现了 Action 接口,并对接口方法做了重写。
3. 定义代理类
public class Captain implements Action {
private Action action;
Captain(Action action) {
this.action = action;
}
@Override
public void attack() {
System.out.println("教练下命令了,叫你进攻。");
action.attack();
}
@Override
public void control() {
System.out.println("教练下命令了,叫你控制球权。");
action.control();
}
@Override
public void defend() {
System.out.println("教练下命令了,叫你参与防守。");
action.defend();
}
}
队长是我们的代理人,他也实现了 Action 接口,并且提供了一个构造方法,传入 Action 并将其赋值到成员变量。他所要实行的 Action 方法实际是通过成员变量 action 来实现的。
4. 外部访问对象
public class Coach {
public static void main(String args[]) {
PlayerB playerB = new PlayerB();
PlayerA playerA = new PlayerA();
//队长,你让 B 开始进攻
Captain captain = new Captain(playerB);
captain.attack();
//队长,你让 A 参与防守
captain = new Captain(playerA);
captain.defend();
}
}
好了,现在教练要开始下命令了。先看一下执行结果:
可以看出我们访问的方法都来自 captian,但是实际执行这些方法都确实球员 A 和球员 B,catpian 很好的扮演了传话筒的角色,并且我们还可以看出 caption 在原方法的基础上还做了一些添加。
这就是最简单的静态代理,这时候有人会有疑问了,如果传话的不是队长而是助理教练怎么办?助理教练又不具备 Action 三个方法,没有实现 Action 接口的话该怎么做。那么我们就需要要用到动态代理了。
五、动态代理
动态代理的特点就是代理类不需要实现接口,利用反射来动态的在内存中构建访问对象。JDK 中的 Proxy 为我们提供了生成代理对象的 API。球员 A,球员 B 和 Action 不做任何改变,下面我们直接开始制作新的代理类
public class Assistant<T> {
//维护目标对象的 Class
private Class<? extends T> tClass;
public Assistant(Class<? extends T> tClass) {
this.tClass=tClass;
}
//给目标对象生成代理对象
@SuppressWarnings("unchecked")
public T getProxyInstance(){
return (T)Proxy.newProxyInstance(
tClass.getClassLoader(),
tClass.getInterfaces(),
(proxy, method, args) -> {
System.out.println("开始传达指令");
//执行目标对象方法
return method.invoke(tClass.newInstance(), args);
}
);
}
}
现在代理者变成了助理,他没法在场上的行动所有没有实现 Action 方法,在构造方法中传入一个目标对象的 Class,然后通过 Proxy 的 newProxyInstance 方法动态代理生成目标对象。
好了,现在教练要下达指令了。
public class Coach {
public static void main(String args[]) {
//助理,准备给 A 球员下达指令。
Assistant<Action> actionAssistant = new Assistant<>(PlayerA.class);
Action action = actionAssistant.getProxyInstance();
//让 A 球员进攻
action.attack();
}
}
可以看出,我们给 Assistant 了一个目标对象的 Class 然后通过 getProxyInstance 拿到目标对象实现的接口,通过接口得到想要的操作。我们来看一下结果。
代理成功,动态代理要注意的一点是,目标对象必须要实现一个接口,否则动态代理无效。
六、优势
前文我们说到,在一个对象不适合或者不能直接引用另一个对象时我们可以通过代理模式建立一个中介类来将两个对象联系起来,并且对目标对象起到了保护作用。我们还可以通过代理模式对原有方法进行加工具有很好的扩展性。
七、局限
由于在客户端和真实对象之间增加了代理对象,可能导致访问速度变慢。代理模式实现起来比直接访问目标对象复杂,增加工作量。
当然,还是那句话:世界上没有十全十美的模式,每个设计模式都有它适用的地方,只要我们的使用方式得当,那么装饰者模式可以帮助我们写出漂亮优雅的代码。
转自 https://ift.tt/2GHmG5G
The post 深入浅出设计模式——从球赛中悟代理模式 appeared first on Linuxeden开源社区.
https://ift.tt/2pWushp
没有评论:
发表评论