静态代理
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(); 代码如下===>这种方法不好(慎用继承,因为耦合度太高)
```java
package com.mashibing.dp.proxy.v04;
import java.util.Random;
/**
* 问题:我想记录坦克的移动时间
* 最简单的办法:修改代码,记录时间
* 问题2:如果无法改变方法源码呢?
* 用继承?
*/
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 Tank2().move();
}
}
// 继承Tank重写move,并在其中通过super调用父类的move
class Tank2 extends Tank {
@Override
public 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;
}
@Override
public 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责任链?)
- 继承的组合无穷无尽,但是组合通过实现接口可以一个套一个,方便一些
- 即一个代理对另一个代理进行代理
- **代理的代理,多层代理**
- 设计模式学到后面就是多态的简单运用了(融会贯通、相互融合)
- 只是语义上有区分,本质上用的都是各种各样的多态
```java
package com.mashibing.dp.proxy.v07;
import java.util.Random;
/**
* 问题:我想记录坦克的移动时间
* 最简单的办法:修改代码,记录时间
* 问题2:如果无法改变方法源码呢?
* 用继承?
* v05:使用代理
* v06:代理有各种类型
* 问题:如何实现代理的各种组合?继承?Decorator?
* v07:代理的对象改成Movable类型-越来越像decorator了
*
*/
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) {
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;
}
@Override
public 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;
}
@Override
public 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的动态代理