设计模式—代理模式

代理模式的定义

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

代理的有自己的个性

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


ps:
个人网站:https://fanfanstudio.top
简书传送门:https://www.jianshu.com/u/02166aa314ee

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值