目 录
- 代理模式
- 定义
- 3个角色
- 代理模式的种类
- 代理模式的优点
- 示例解析:玩游戏打怪、升级示例
-
- UML类图
- [1. 真实主题RealSubject:GamePlayer实现IGamePlayer接口中的方法](#1. 真实主题RealSubject:GamePlayer实现IGamePlayer接口中的方法)
- 抽象主题Subject:IGamePlayer接口对游戏玩家进行抽象
- 代理主题ProxySubject:GamePlayerProxy是代理类
- 测试类
代理模式
定义
英文原话
Provide a surrogate or placeholder for another object to control access to it.
直译
为另一个对象提供代理或占位符以控制对它的访问
如何理解?
字面上不好理解的话可以结合代码进一步理解下,从具体实现上分析:
抽象主题角色,是对被代理类的抽象,被代理类就看作是对抽象主题角色类的实现,被代理类的实现就是核心功能的实现
同时,代理对象中持有被代理对象的引用,
代理类也实现了被代理类相同的接口,
这样,通过代理类可以增强被代理类的逻辑,
在被代理对象的逻辑前后可以增加一些逻辑,比如获取请求参数,添加日志等,这些非核心的逻辑,可以在代理类中进行增强。
代理模式应用非常广泛,大到一个系统框架、企业平台,小到事务处理、代码片段,随处可见代理模式的使用,例如,Java RMI的远程调用就是一种代理模式的应用,AOP也可以通过代理模式实现。
3个角色
UML类图
1. 抽象主题(Subject)角色
该角色是真实主题和代理主题的共同接口,以便在任何可以使用真实主题的地方都可以使用代理主题。
2. 代理类:代理主题(Proxy Subject)角色
也叫做委托类、代理类,该角色负责控制对真实主题的引用,负责在需要的时候创建或删除真实主题对象,并且在真实主题角色处理完毕前后做预处理和善后处理工作。
3. 被代理类:真实主题(Real Subject)角色
该角色也叫做被委托角色、被代理角色,是业务逻辑的具体执行者。
代码示例
1. 真实主题,被代理类
java
package com.polaris.designpattern.list2.structural.pattern1.proxy.proto;
public class Target implements AbstractSubject {
@Override
public void m1() {
System.out.println("target implements. Core business logic");
}
}
2. 抽象主题
抽取核心业务逻辑接口方法m1()
出来,该方法源自被代理对象,因此被代理类的m1()
方法就可以看作是对它的实现,被代理类添加 implements AbstractSubject
表示对抽象主题的实现;
java
package com.polaris.designpattern.list2.structural.pattern1.proxy.proto;
public interface AbstractSubject {
void m1();
}
3. 代理类
代理被代理的类,对被代理类的逻辑(抽象主题进行了抽象)进行增强,
前提是代理类实现了抽象主题的抽象方法,同时持有被代理类的对象(可通过构造函数传入)
注意 这里的构造函数传入的是Target实例,更通用的可以使用抽象主题角色AbstractSubject
类型;
java
package com.polaris.designpattern.list2.structural.pattern1.proxy.proto;
public class Proxy implements AbstractSubject {
private Target target;
public Proxy(Target target) {
this.target = target;
}
@Override
public void m1() {
before();
target.m1();
after();
}
private void before() {
System.out.println("enhanced logic: before...");
}
private void after() {
System.out.println("enhanced logic: after...");
}
}
4. 测试类
java
package com.polaris.designpattern.list2.structural.pattern1.proxy.proto;
public class DemoTest {
public static void main(String[] args) {
Target target = new Target();
Proxy proxy = new Proxy(target);
proxy.m1();
}
}
/* Output:
enhanced logic: before...
target implements. Core business logic
enhanced logic: after...
*///~
代理模式的种类
简单了解一下:
- 远程(Remote)代理:为一个位于不同的地址空间的对象提供一个局部代表对象。这个不同的地址空间可以是在本机器中,也可以在另一台机器中。
- 虚拟(Virtual**)**代理:有时需要创建一些消耗较多资源的对象,可以首先创建代理对象,而将真实对象的创建延迟。例如,加载一个很大的图片,可以通过图片的代理来代替真正的图片。
- 保护(Protect or Access)代理:控制对一个对象的访问,如果需要,可以给不同的用户提供不同级别的使用权限。
- 缓存(Cache)代理:为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果。
- 同步(Synchronization)代理:使几个用户能够同时使用一个对象而没有冲突。
- 智能引用(Smart Reference)代理:当一个对象被引用时,提供一些额外的操作,例如,记录访问的流量和次数等。
代理模式的优点
- 职责清晰 :真实的角色实现实际的业务逻辑,不用关心其他非本职的事务,通过后期的代理完成附加的事务,附带的结果就是编程简洁清晰。
- 高扩展性 :具体主题角色随需求不同可能有很多种,但只要实现了接口,代理类 就完全可以 在不做任何修改的情况下代理各种真实主题角色。
- 智能化:代理类可以在运行时才确定需要去代理的真实主题,这是一种强大的功能。
示例解析:玩游戏打怪、升级示例
UML类图
- 主题类和代理类同时实现接口
- 主题类,就是被代理类,完成主要业务逻辑
- 附加业务在代理类中完成
- 客户端调用:
- 声明接口的两个示例分别指向主题类与代理类的实例对象;
- 其中,在代理类示例创建时传入主题类的实例(参数类型可以放大,参数定义为接口类型,当然主题类实现类此接口,属于此接口类型,可以被传入)。
1. 真实主题RealSubject:GamePlayer实现IGamePlayer接口中的方法
被代理类的具体实现,是最核心的业务逻辑,这里玩游戏最核心的就是打怪,升级;
java
package com.polaris.designpattern.list2.structural.pattern1.proxy;
public class GamePlayer implements IGamePlayer {
private String name = "";
public GamePlayer(String name) {
this.name = name;
}
@Override
public void killBoss() {
System.out.println(this.name + "在打怪!");
}
@Override
public void upGrade() {
System.out.println(this.name + "成功升了1级!");
}
}
抽象主题Subject:IGamePlayer接口对游戏玩家进行抽象
抽象主题,抽象出
打怪
、升级
两个核心业务逻辑,方便代理类进行实现,增强被代理类的逻辑
java
package com.polaris.designpattern.list2.structural.pattern1.proxy;
public interface IGamePlayer {
//杀怪
void killBoss();
//升级
void upGrade();
}
代理主题ProxySubject:GamePlayerProxy是代理类
代理主题,在被代理类的核心业务逻辑实现(打怪、升级)的基础上++增加非核心业务逻辑:加日志、计时,++
因此代理类是对被代理类的增强、强化
他持有被代理类的引用,通过构造函数初始化
java
package com.polaris.designpattern.list2.structural.pattern1.proxy;
import java.util.Date;
public class GamePlayerProxy implements IGamePlayer {
private IGamePlayer player = null;
public GamePlayerProxy(IGamePlayer player) {
this.player = player;
}
//记录打怪的时间
private void log() {
System.out.println("打怪时间 " + new Date().toString());
}
@Override
public void killBoss() {
this.log();
player.killBoss();
}
@Override
public void upGrade() {
player.upGrade();
this.countTime();
}
//计时
private void countTime() {
System.out.println("升1级耗时50小时");
}
}
测试类
java
package com.polaris.designpattern.list2.structural.pattern1.proxy;
public class DemoTest {
public static void main(String[] args) {
IGamePlayer player = new GamePlayer("李逍遥");
IGamePlayer proxy = new GamePlayerProxy(player);
proxy.killBoss();
proxy.upGrade();
}
}
/* Output:
打怪时间 Mon May 20 23:35:58 CST 2024
李逍遥在打怪!
李逍遥成功升了1级!
升1级耗时50小时
*///~