一、模式定义
- 代理模式为另一个对象提供一个替身或占位符以控制对这个对象的访问。
- 相关组件
- Subject:Proxy和RealSubject都实现这个接口,以供客户端可以像处理RealSubject一样处理Proxy。
- RealSubject:真正做事的对象,Proxy会控制对RealSubject的访问。
- Proxy:代理类,持有Subject的引用,必要时可将请求转发给Subject。另外,创建RealSubject对象,通常由Proxy负责。
- 三种代理控制方式:
a. 远程代理控制访问远程对象。
b. 虚拟代理控制访问创建开销大的资源。
c. 保护代理基于权限控制对资源的访问。(动态代理就是这种方式)
二、举例说明
- 场景:在不侵入业务代码的情况下,需要做一些统一的操作时使用代理模式。如搜集操作日志、事务开启与提交等。
- 需求:每个英雄有普攻、技能两种对地方造成伤害的方式,现在需要在不影响已有代码的情况下,分别统计两种方式造成的总伤害值。
三、代码实现
- 定义公共接口
public interface Hero {
void attack(Integer num);
void skill(Integer num);
}
- 实际类
public class RealHero implements Hero {
@Override
public void attack(Integer damage) {
System.out.println("使用普攻,对敌人造成伤害值:" + damage);
}
@Override
public void skill(Integer damage) {
System.out.println("使用技能,对敌人造成伤害值:" + damage);
}
}
- 代理类
public class HeroInvocationHandler implements InvocationHandler {
private Hero hero;
private Integer attackDamage = 0;
private Integer skillDamage = 0;
public HeroInvocationHandler(Hero hero) {
this.hero = hero;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object invoke = method.invoke(hero, args);
if("attack".equals(method.getName())){
attackDamage += (Integer) args[0];
}
if("skill".equals(method.getName())){
skillDamage += (Integer) args[0];
}
System.out.println("--------------------该英雄普攻共造成总伤害:" + attackDamage);
System.out.println("--------------------该英雄技能共造成总伤害:" + skillDamage);
return invoke;
}
public Hero getProxyHero(){
ClassLoader classLoader = hero.getClass().getClassLoader();
Class<?>[] interfaces = hero.getClass().getInterfaces();
return (Hero) Proxy.newProxyInstance(classLoader, interfaces, this);
}
}
- 客户端执行
public class RealHeroClient {
public static void main(String[] args) {
// 1.创建英雄实例
RealHero realHero = new RealHero();
// 2.获取英雄实例的代理对象
HeroInvocationHandler heroInvocationHandler = new HeroInvocationHandler(realHero);
Hero proxyHero = heroInvocationHandler.getProxyHero();
// 调用代理的普攻、技能方法
proxyHero.attack(new Double(Math.random() * 100).intValue());
proxyHero.skill(new Double(Math.random() * 100).intValue());
proxyHero.attack(new Double(Math.random() * 100).intValue());
proxyHero.skill(new Double(Math.random() * 100).intValue());
}
}
执行结果