代理模式(Proxy Pattern)是一个使用率非常高的模式。
定义:Provide a surrogate or placeholder for another object to control access to it. (为其对象提供一种代理以控制这个对象的访问)
代理模式也叫委托模式,它是一项基本的设计技巧。许多其他的模式,如状态模式、策略模式、访问者模式本质上是在更特殊的场合采用了委托模式,而且在日常应用中,代理模式可以提供非常好的访问控制。
三个角色的定义
- Subject抽象主题角色
抽象主题类可以是抽象类也可以是接口,是一个最普通的业务类型定义,无特殊要求。
- RealSubject具体的主题角色
也叫做被委托的角色、被代理的角色。是业务逻辑的具体执行者。
- Proxy代理主题角色
也叫委托类、代理类。它负责对真实角色的应用,把所以抽象主题定义的方法限制委托给真实主题角色实现,并且在真实主题角色处理完毕前后做预处理和善后工作。
抽象主题类:
public interface Subject {
//定义一个方法
public void request();
}
真实主题类:
public class RealSubject implements Subject {
//实现方法
@Override
public void request() {
//业务逻辑处理
}
}
代理类:
public class Proxy implements Subject {
//要代理哪个实现类
private Subject subject = null;
//默认被代理者
public Proxy() {
this.subject = new Proxy();
}
//通过构造函数传递代理者
public Proxy(Object object){
}
//实现接口中的方法
@Override
public void request() {
this.before();
this.request();
this.after();
}
//预处理
private void before() {
}
//善后处理
private void after() {
}
}
一个代理类可以代理多个被委托者或者被代理者,因此一个代理者具体代理哪个真实主题角色,是由场景类决定的。在通常情况下,一个接口只需要一个代理类就可以了,具体代理哪个实现类由高层模块决定,也就是在代理类的构造函数中传递被代理者。
代理模式的应用
代理模式的优点
-
职责清晰
真实的角色就是实现实际的业务逻辑,不用关心其他非本职责的事务,通过后期的代理完成一件事物,附带的结果就是编程简洁清晰。 -
高扩展性
具体主题角色是随时会发生变化的,只要它实现了接口,无论它如何变化都逃不脱接口,那我们的代理类完全就可以在不做任何修改的情况下使用。 -
智能化
动态代理的典型应用
代理模式的使用场景
类似现实生活中买房子的中介,打官司的律师。你不想参与中间过程的是是非非。减轻用户的负担。代理模式使用场景非常多。最典型的是Spring AOP中的动态代理。
代理模式的扩展
普通代理
普通代理就是我们要知道代理的存在,也就是类似GamePlayerProxy这个代理类的存在,然后才能访问。
普通代理的要求就是客户端只能访问代理角色,而不能访问真实角色。以游戏代练为例子,
游戏者接口
public interface IGamePlayer {
//登陆
public void login(String user,String password);
//杀怪
public void killBoss();
//升级
public void upgrade();
}
GamePlayer的构造函数增加了_gamePlayer参数,而代理角色则只要传入代理者名字即可,而不需要说是替哪个对象做代理
普通代理的游戏者
public class GamePlayer implements IGamePlayer {
private String name="";
//构造函数限制谁能创建对象,并同时传递姓名
public GamePlayer(IGamePlayer _gamePlayer,String _name) throws Exception {
if(_gamePlayer==null){
throw new Exception("不能创建真实角色");
}else{
this.name = _name;
}
}
@Override
public void login(String user, String password) {
System.out.println("登录名为"+user+"的用户"+this.name+" 登录成功");
}
@Override
public void killBoss() {
System.out.println(this.name+"在击杀小怪");
}
@Override
public void upgrade() {
System.out.println(this.name+"又升一级");
}
}
在构造函数中,传来进来一个IGaemPlayer对象,检查谁能创建真实的角色,或者做出别的限制。
普通代理的代理者
public class GamePlayProxy implements IGamePlayer {
private IGamePlayer gamePlayer = null;
//通过构造函数传递要对谁进行代练
public GamePlayProxy(String name) {
try {
gamePlayer = new GamePlayer(this, name);
} catch (Exception e) {
//异常处理
}
}
@Override
public void login(String user, String password) {
this.gamePlayer.login(user, password);
}
@Override
public void killBoss() {
this.gamePlayer.killBoss();
}
@Override
public void upgrade() {
this.gamePlayer.upgrade();
}
}
仅仅修改了构造函数,传递进来一个代理者名称,即可进行代理,在这种改造下,系统更加简洁,调用者只知道代理的存在就行,不用知道代理了谁。
普通代理的场景
public class Client {
public static void main(String[] args) {
//定义一个代练者
IGamePlayer proxy = new GamePlayProxy("代练玩家");
//开始游戏 ,记下时间
System.out.println("开始时间:2018-8-1 13:10");
proxy.login("Yang", "123123");
//开始杀怪
proxy.killBoss();
//开始升级
proxy.upgrade();
//记录结束游戏时间
System.out.println("结束时间:2018-8-1 15:10");
}
}
结果:
开始时间:2018-8-1 13:10
登录名为Yang的用户代练玩家 登录成功
代练玩家在击杀小怪
代练玩家又升一级
结束时间:2018-8-1 15:10
在该模式下,调用者只知道代理而不知道真实的角色是谁,屏蔽了真实角色的变更对高层模块的影响,真实的主题角色想怎么修改都可以,对高层次的模块没有任何的影响,只要你实现了接口所对应的方法,该模式非常适合对扩展性要求较高的场合。
强制代理
强制代理是要“强制”,必须通过真实角色查找到代理角色,否则不能进行访问。无论是通过代理类还是直接new一个主题角色类,都不能访问,只有通过真实角色指定的代理类才能访问,也就是说由真实的角色管理代理角色。例如你和一个明星比较熟,你直接找明星帮忙要见导演,但是明星说她比较忙,让你找她的经纪人。你本来想绕过她的代理,谁知道她返回的还是她的代理,这就是强制代理。你可以不知道代理的存在,但是你的所作所为还是需要代理为你提供服务。
在接口上增加了一个getProxy()方法,真实角色GamePlayer可以指定一个自己的代理,除了代理外谁都不能访问。
强制代理的接口
public interface IGamePlayer {
//登陆
public void login(String user,String password);
//杀怪
public void killBoss();
//升级
public void upgrade();
//每个人都可以找自己的代理
public IGamePlayer getProxy();
}
强制代理的真实角色
public class GamePlayer implements IGamePlayer{
private String name="";
//自己代理是
private IGamePlayer proxy =null;
public GamePlayer(String _name){
this.name =_name;
}
//找到自己的代理
@Override
public IGamePlayer getProxy() {
this.proxy = new GamePlayerProxy(this);
return this.proxy;
}
//登录
@Override
public void login(String user, String password) {
if(this.isProxy()){
System.out.println("登录名为"+user+"的用户"+this.name+" 登录成功");
}else{
System.out.println("请使用指定代理进行访问");
}
}
//杀怪
@Override
public void killBoss() {
if(this.isProxy()){
System.out.println(this.name+"击杀小怪");
}else{
System.out.println("请使用指定代理进行访问");
}
}
//升级
@Override
public void upgrade() {
if(this.isProxy()){
System.out.println(this.name+"又升一级");
}else{
System.out.println("请使用指定代理进行访问");
}
}
//校验是否是代理访问
private boolean isProxy() {
if(this.proxy == null){
return false;
}else{
return true;
}
}
}
强制代理的代理角色
public class GamePlayerProxy implements IGamePlayer {
private IGamePlayer gamePlayer = null;
//构造函数传递用户名
public GamePlayerProxy(IGamePlayer _gamePlayer){
this.gamePlayer = _gamePlayer;
}
@Override
public void login(String user, String password) {
this.gamePlayer.login(user, password);
}
@Override
public void killBoss() {
this.gamePlayer.killBoss();
}
@Override
public void upgrade() {
this.gamePlayer.upgrade();
}
//代理的代理暂时没有,就是自己
@Override
public IGamePlayer getProxy() {
return this;
}
}
强制代理的场景
public class Client {
public static void main(String[] args) {
//定义一个真实角色
IGamePlayer player = new GamePlayer("张三");
//获得指定的代理
IGamePlayer proxy = player.getProxy();
//开始游戏
System.out.println("开始时间:2018-8-1 13:10");
proxy.login("yang", "123123");
//开始杀怪
proxy.killBoss();
//开始升级
proxy.upgrade();
//记录结束游戏时间
System.out.println("结束时间:2018-8-1 15:10");
}
}
结果:
开始时间:2018-8-1 13:10
登录名为yang的用户张三 登录成功
张三击杀小怪
张三又升一级
结束时间:2018-8-1 15:10
强制代理的概念就是要从真实的角色查找到代理角色,不允许直接访问真实角色。高层模块只要调用getProxy()就可以访问真实角色的所以方法,它根本不需要产生一个代理出来,代理的管理已经由真实角色自己完成。
代理的有自己的个性
一个类可以实现多个接口,完成不同任务的整合。也就是说代理类不仅仅可以实现主题接口,也可以实现其他接口完成不同的任务,而且代理的目的是在目标对象方法的基础上作增强,这种增强的本质通常就是对目标对象的方法进行拦截合过滤。