静态代理
UML图

问题引入
- 要获取坦克的运行时间
- move方法为Movable接口中的方法
- Tank实现了Movable接口并重写了move方法
- 问题:如何在不改变move内部逻辑(即无法改变方法源码时)的情况下获取其运行时间? ```java package com.mashibing.dp.proxy.v01;
import java.util.Random;
/**
- 问题:我想记录坦克的移动时间
简单的办法:修改代码,记录时间 */ public class Tank implements Movable {
/**
模拟坦克移动了一段儿时间 */ @Override public void move() {
// long start = System.currentTimeMillis();
System.out.println(“Tank moving claclacla…”); try {
Thread.sleep(new Random().nextInt(10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
// long end = System.currentTimeMillis(); // System.out.println(end - start);
} }
interface Movable { void move(); }
- benchmark:性能测试,找到性能最差的点(可能是整个程序中的某个方法);但我们是拿不到源码- 方案1:用继承,调用super.move(); 代码如下===>这种方法不好(慎用继承,因为耦合度太高)```javapackage com.mashibing.dp.proxy.v04;import java.util.Random;/*** 问题:我想记录坦克的移动时间* 最简单的办法:修改代码,记录时间* 问题2:如果无法改变方法源码呢?* 用继承?*/public class Tank implements Movable {/*** 模拟坦克移动了一段儿时间*/@Overridepublic void move() {System.out.println("Tank moving claclacla...");try {Thread.sleep(new Random().nextInt(10000));} catch (InterruptedException e) {e.printStackTrace();}}public static void main(String[] args) {new Tank2().move();}}// 继承Tank重写move,并在其中通过super调用父类的moveclass Tank2 extends Tank {@Overridepublic void move() {long start = System.currentTimeMillis();super.move();long end = System.currentTimeMillis();System.out.println(end - start);}}interface Movable {void move();}
- 方案2:在main中测试(不好,因为对于有很多方法的大程序来说依然不方便)
- 方案3:用聚合(静态代理,将tank对象聚合到中间代理人TankProxy中去)
- 代理:买茅台,不直接去厂商买,而是代理人去厂商买,加价后再卖给我们消费者
- 代理人帮忙多做了一点事,也做了一点自己的事,但是做的都是一件事,所以实现的都是同一个接口Movable(代理茅台,不能变成五粮液) ```java package com.mashibing.dp.proxy.v05;
import java.util.Random;
/**
- 问题:我想记录坦克的移动时间
- 最简单的办法:修改代码,记录时间
- 问题2:如果无法改变方法源码呢?
- 用继承?
- v05:使用代理
代理有各种类型:时间time、日志log */ public class Tank implements Movable {
/**
模拟坦克移动了一段儿时间 */ @Override public void move() { System.out.println(“Tank moving claclacla…”); try {
Thread.sleep(new Random().nextInt(10000));
} catch (InterruptedException e) {
e.printStackTrace();
} }
public static void main(String[] args) { new TankTimeProxy(new Tank()).move(); new TankLogProxy(new Tank()).move(); } }
// 用聚合代替继承===>解耦 // 记录时间 class TankTimeProxy implements Movable {
Tank tank;// 传入tank对象public TankTimeProxy(Tank tank) {this.tank = tank;}@Overridepublic void move() {long start = System.currentTimeMillis();tank.move();long end = System.currentTimeMillis();System.out.println(end - start);}
}
// 记录日志 class TankLogProxy implements Movable { Tank tank; @Override public void move() { System.out.println(“start moving…”); tank.move(); System.out.println(“stopped!”); } }
interface Movable { void move(); }
<a name="MXGQu"></a>## 将代理对象改成Movable类型(聚合对象改为接口类型)- 静态代理- 越来越像Decorator模式- 实现代理的各种组合(装饰器模式、cor责任链?)- 继承的组合无穷无尽,但是组合通过实现接口可以一个套一个,方便一些- 即一个代理对另一个代理进行代理- **代理的代理,多层代理**- 设计模式学到后面就是多态的简单运用了(融会贯通、相互融合)- 只是语义上有区分,本质上用的都是各种各样的多态```javapackage com.mashibing.dp.proxy.v07;import java.util.Random;/*** 问题:我想记录坦克的移动时间* 最简单的办法:修改代码,记录时间* 问题2:如果无法改变方法源码呢?* 用继承?* v05:使用代理* v06:代理有各种类型* 问题:如何实现代理的各种组合?继承?Decorator?* v07:代理的对象改成Movable类型-越来越像decorator了**/public class Tank implements Movable {/*** 模拟坦克移动了一段儿时间*/@Overridepublic void move() {System.out.println("Tank moving claclacla...");try {Thread.sleep(new Random().nextInt(10000));} catch (InterruptedException e) {e.printStackTrace();}}public static void main(String[] args) {Tank t = new Tank();TankTimeProxy ttp = new TankTimeProxy(t);TankLogProxy tlp = new TankLogProxy(ttp);tlp.move();// new TankLogProxy(// new TankTimeProxy(// new Tank()// )// ).move();}}class TankTimeProxy implements Movable {Movable m;public TankTimeProxy(Movable m) {this.m = m;}@Overridepublic void move() {long start = System.currentTimeMillis();m.move();long end = System.currentTimeMillis();System.out.println(end - start);}}class TankLogProxy implements Movable {Movable m;public TankLogProxy(Movable m) {this.m = m;}@Overridepublic void move() {System.out.println("start moving...");m.move();long end = System.currentTimeMillis();System.out.println("stopped!");}}interface Movable {void move();}
需求升级—->静态代理的不足
- 如果有stop方法需要代理…===>需要修改的太多(改接口、该实现、改代理类)===>需要新增方法这就要修改代码了,违反了开闭原则
- 让LogProxy可以重用,不仅可以代理Tank,还可以代理任何其他可以代理的类型 ===> Object
- 日志记录===>时间计算是很多方法都需要的东西
- 不能直接将Movable接口类型改为Object类型,因为使用接口的目的就是为了将代理方法和被代理方法对应起来,将接口改为Object之后,调用原方法不光要进行强制类型转换,而且不容易看出关系
- 解决方案: 详见下一篇
- 分离代理行为与被代理对象
- 使用jdk的动态代理
- cglib的动态代理
