设计模式系列:代理模式

一.名称

二.问题(为了解决什么问题)

我相信第一次接触代理模式的读者肯定非常郁闷,为什么要用代理呢?我们来想想现实世界,打官司为什么要找一个律师?因为你不想参与中间过程的是是非非,只要完成自己的答辩就可以了,其他的比如事前调查、事后追查都由律师来搞定,这就是为了减轻你的负担。代理模式的使用场景非常多,大家可以看看spring AOP,这是一个非常典型的动态代理。

在软件开发中,由于某些原因,客户端不想或不能直接访问某个对象,此时可以通过一个称之为“代理”的第三者来实现间接访问,该方案对应的设计模式就被成为代理模式。

当需要控制对一个对象的访问,为不同的用户提供不同级别的访问权限,可以使用保护代理。

三.解决方案(主要体现在uml和核心代码上)

代理模式:给某一个对象提供一个代理,并由代理对象控制对原对象的引用。代理模式是一种对象结构型模式。在代理模式中引入了一个新的代理对象,代理对象在客户端对象和目标对象之间起到中介的作用,它去掉客户不能看到的内容和服务或者增添客户需要的额外的新服务。

代理模式的结构比较简单,其核心是代理类,为了让客户端能够一致性地对待真实对象和代理对象,在代理模式中引入了抽象层。

需要修改或屏蔽某一个或若干个类的部分功能,复用另外一部分功能,可使用静态代理,若是需要拦截一批类中的某些方法,在方法的前后插入一些一致的操作,假设这些类有一致的接口,可使用JDK的动态代理,否则可使用cglib

1.两种方式实现代理

  1. 使用继承实现
    继承指的是一个类(称为子类,子接口)继承另外的一个类(称为父类,父接口)。在代理模式中,委托类作为父类,代理类作为子类。在代理类中可以重写父类中需要被代理的方法

  2. 使用组合实现
    组合也是关联关系的一种特例,它体现整体和部分之间的关系,但此时整体和部分是不可分的。在代理模式中,委托类作为部分,代理类作为整体。委托类是作为代理类的一部分存在,他们两个是不可分割的。在代码中表现为委托类作为代理类中德成员变量存在。

2.UML类图

这里写图片描述

由类图可知,代理模式包含如下三个角色:

  • Subject(抽象主题角色):它声明了真实主题和代理主题的共同接口,这样一来在任何使用真实主题的地方都可以使用代理主题,客户端通常需要针对抽象主题角色进行编程。

  • Proxy(代理主题角色):它包含了对真实主题的引用,从而可以在任何时候操作真实主题对象;在代理主题角色中提供一个与真实主题角色相同的接口,以便在任何时候都可以替代真实主题;代理主题角色还可以控制对真实主题的使用,负责在需要的时候创建和删除真实主题对象,并对真实主题对象的使用加以约束。通常,在代理主题角色中,客户端在调用所引用的真实主题操作之前或之后还需要执行其他操作,而不仅仅是单纯调用真实主题对象中的操作。

  • RealSubject(真实主题角色):它定义了代理角色所代表的真实对象,在真实主题角色中实现了真实的业务操作,客户端可以通过代理主题角色间接调用真实主题角色中定义的操作。

3.典型代码

抽象主题类声明了真实主题类和代理类的公共方法,它可以是接口、抽象类或具体类,客户端针对抽象主题类编程,一致性地对待真实主题和代理主题,典型的抽象主题类代码如下:

abstract class Subject  
{  
    public abstract void Request();  
}  

真实主题类继承了抽象主题类,提供了业务方法的具体实现,其典型代码如下:

class RealSubject : Subject  
{  
    public override void Request()  
    {  
        //业务方法具体实现代码  
    }  
}  

代理类也是抽象主题类的子类,它维持一个对真实主题对象的引用,调用在真实主题中实现的业务方法,在调用时可以在原有业务方法的基础上附加一些新的方法来对功能进行扩充或约束,最简单的代理类实现代码如下:

class Proxy : Subject  
{  
    private RealSubject realSubject = new RealSubject(); //维持一个对真实主题对象的引用  

    public void PreRequest()   
    {  
        …...  
    }  

    public override void Request()   
    {  
        PreRequest();  
        realSubject.Request(); //调用真实主题对象的方法  
         PostRequest();  
    }  

    public void PostRequest()   
    {  
         …...   
    }  
}  

4.代理模式的扩展

代理模式的分类

在实际的开发过程中,代理类的实现比上述代码要复杂的多。代理模式根据其目的和实现方法不同可分为很多种类,其中常见的几种代理模式简要说明如下:

  • 远程代理(Remote Proxy):为一个位于不同的地址空间的对象提供一个本地的代理对象,这个不同的地址空间可以是在同一台主机中,也可是在另一台主机中,远程代理又称为大使(Ambassador)。android中的aidl
  • 虚拟代理(Virtual Proxy):如果需要创建一个资源消耗较大的对象,先创建一个消耗相对较小的对象来表示,真实对象只在需要时才会被真正创建。
  • 保护代理(Protect Proxy):控制对一个对象的访问,可以给不同的用户提供不同级别的使用权限。没见过
  • 缓冲代理(Cache Proxy):为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果。没见过
  • 智能引用代理(Smart Reference Proxy):当一个对象被引用时,提供一些额外的操作,例如将对象被调用的次数记录下来等。没见过
  • 动态代理
  • AOP

在这些常用的代理模式中,有些代理类的设计非常复杂,例如远程代理类,它封装了底层网络通信和对远程对象的调用,其实现较为复杂。

代理模式和装饰者模式

代理模式和装饰者模式在实现时有些类似,但是代理模式主要是给真实主题增加一些全新的职责,例如权限控制、缓冲处理、智能引用、远程访问等,这些职责与原有职责不属于同一个问题域;而装饰者模式是通过装饰类为具体构件类增加一些相关的职责,是对原有职责的扩展,这些职责属于同一问题域。代理模式和装饰者模式的目的也不相同,前者控制对对象的访问,后者是为对象动态的增加功能。

普通代理和强制代理的区别

这里写图片描述

虚拟代理

这里写图片描述

代理可以有自己的个性

给代理一个基类或者接口来封装自己的个性,自己继承然后实现它即可

这里写图片描述

这里写图片描述

四.例子

1.普通代理示例

这里写图片描述

明显可以看到,client只知道代理类,不知道游戏者类
主题

public interface IGamePlayer {
    public void login();
    public void killBoss();
    public void upgrate();
}

public class GamePlayer implements IGamePlayer {
    private String name;
    private String passWord;

    public GamePlayer(String name, String passWord) {
        this.name = name;
        this.passWord = passWord;
    }

    @Override
    public void login() {

        System.out.println("用户" + this.name + "登陆成功");
    }

    @Override
    public void killBoss() {
        System.out.println("在打怪");
    }

    @Override
    public void upgrate() {
        System.out.println("有升了一级");
    }
}

代理

public class GamePlayerProxy implements IGamePlayer {
    private IGamePlayer gamePlayer = null;

    // 通过参数传递要对谁进行代练
    public GamePlayerProxy(String gamePlayerName, String passWord) {
        try {
            gamePlayer = new GamePlayer(gamePlayerName, passWord);
        } catch (Exception e) {
            // todo
        }
    }

    @Override
    public void login() {
        this.gamePlayer.login();
    }

    @Override
    public void killBoss() {
        this.gamePlayer.killBoss();
    }

    @Override
    public void upgrate() {
        this.gamePlayer.upgrate();
    }
}

client

public class Client {
    public static void main(String[] args) {
        // 我们找一个游戏代理,首先需要做的就是把用户名和密码告诉游戏代练
        IGamePlayer proxy = new GamePlayerProxy("张三", "1122");
        System.out.println("开始时间是:2016.04.07");
        proxy.login();
        proxy.killBoss();
        proxy.upgrate();
        System.out.println("结束时间是:2016.04.09");
    }
}

输出结果

这里写图片描述

2.强制代理示例

这里写图片描述

这里写图片描述

即首先需要创建一个真实角色,然后从真实角色中取到代理类,然后调用代理类的各种方法
真实对象

public interface IGamePlayer {
    public void login();
    public void killBoss();
    public void upgrate();
    public IGamePlayer getProxy();
}

public class GamePlayer implements IGamePlayer {
    private String name;
    private String passWord;
    private IGamePlayer proxy = null;

    public GamePlayer(String name, String passWord) {
        this.name = name;
        this.passWord = passWord;
    }

    @Override
    public void login() {
        if (this.isProxy()) {
            System.out.println("用户" + this.name + "登陆成功");
        } else {
            System.out.println("请使用指定的代理访问");
        }
    }

    @Override
    public void killBoss() {
        if (this.isProxy()) {
            System.out.println("在打怪");
        } else {
            System.out.println("请使用指定的代理访问");
        }
    }

    @Override
    public void upgrate() {
        if (this.isProxy()) {
            System.out.println("有升了一级");
        } else {
            System.out.println("请使用指定的代理访问");
        }
    }

    @Override
    public IGamePlayer getProxy() {
        this.proxy = new GamePlayerProxy(this);
        return this.proxy;
    }

    public boolean isProxy() {
        if (this.proxy == null) {
            return false;
        } else {
            return true;
        }
    }
}

代理

public class GamePlayerProxy implements IGamePlayer {
    private IGamePlayer gamePlayer = null;

    // 通过参数传递要对谁进行代练
    public GamePlayerProxy(IGamePlayer player) {
       this.gamePlayer = player;
    }

    @Override
    public void login() {
        this.gamePlayer.login();
    }

    @Override
    public void killBoss() {
        this.gamePlayer.killBoss();
    }

    @Override
    public void upgrate() {
        this.gamePlayer.upgrate();
    }

    @Override
    public IGamePlayer getProxy() {
        return this;
    }
}

client

public class Client {
    public static void main(String[] args) {
//        IGamePlayer gamePlayer = new GamePlayer("张三", "1122");
//        System.out.println("开始时间是:2016.04.07");
//        gamePlayer.login();
//        gamePlayer.killBoss();
//        gamePlayer.upgrate();
//        System.out.println("结束时间是:2016.04.09");

        IGamePlayer gamePlayer = new GamePlayer("张三", "1122");
        IGamePlayer proxy = gamePlayer.getProxy();
        System.out.println("开始时间是:2016.04.07");
        proxy.login();
        proxy.killBoss();
        proxy.upgrate();
        System.out.println("结束时间是:2016.04.09");

    }
}

五.效果(有啥优缺点)

优点:

  1. 职责清晰。真实的角色就是实现实际的业务逻辑,不用关心其他非职责的事务,通过后期的代理完成一件事务,附带的结果就是编程简洁清晰。
  2. 高扩展性。

常见案例

收费商务信息查询系统。该系统的需求如下:
1.在进行商务信息查询之前用户需要通过身份验证,只有合法用户才能够使用该查询系统。
2.在进行商务信息查询时,系统需要记录查询日志,以便根据查询次数收取查询费用。
商务信息查询模块开发已完成,他们希望以一种松耦合的方式向原有系统增加身份验证和日志记录功能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值