设计模式——代理模式

代理模式(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这个代理类的存在,然后才能访问。
普通代理的要求就是客户端只能访问代理角色,而不能访问真实角色。以游戏代练为例子,

普通代理类图.jpg

游戏者接口

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一个主题角色类,都不能访问,只有通过真实角色指定的代理类才能访问,也就是说由真实的角色管理代理角色。例如你和一个明星比较熟,你直接找明星帮忙要见导演,但是明星说她比较忙,让你找她的经纪人。你本来想绕过她的代理,谁知道她返回的还是她的代理,这就是强制代理。你可以不知道代理的存在,但是你的所作所为还是需要代理为你提供服务。

强制代理类图.jpg

在接口上增加了一个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()就可以访问真实角色的所以方法,它根本不需要产生一个代理出来,代理的管理已经由真实角色自己完成。

代理的有自己的个性

一个类可以实现多个接口,完成不同任务的整合。也就是说代理类不仅仅可以实现主题接口,也可以实现其他接口完成不同的任务,而且代理的目的是在目标对象方法的基础上作增强,这种增强的本质通常就是对目标对象的方法进行拦截合过滤。


动态代理

动态代理

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342