23种设计模式7_代理模式之一静态代理

23种设计模式7_代理模式之一静态代理

1 基本介绍

代理模式:为其他对象提供一种代理以控制对这个对象的访问

代理模式也叫委托模式,它是一项基本设计技巧。许多其他的模式,如状态模式、策略模式、访问者模式本质上都是在更加特殊的场合采用了代理模式。在日常开发应用种,代理模式能够提供非常好的访问控制。

代理可分为两种:一种是静态代理,由程序员创建或由特定工具自动生成源代码,再对其编译。在程序运行前,代理类的**.class**文件就已经存在了;另一种是动态代理:在程序运行时,运用反射机制动态创建而成。

静态代理通常只代理一个类,动态代理是代理一个接口下的多个实现类。

静态代理事先知道自己要代理的是什么,而动态代理不知道要代理什么,只有在运行时才知道。
动态代理参考:23种设计模式7_代理模式之二动态代理

代理模式的UML类图:

在这里插入图片描述

从上图可以看到代理模式有3种角色:

Subject抽象主题角色 抽象主题角色可以是抽象类也可以是接口,是一个最普通的业务类型定义,无特殊要求。

RealSubject具体主题角色 也叫做被代理角色、被委托角色,它就是个冤大头,是业务逻辑的具体执行者。

Proxy代理角色 也叫做代理类、委托类,它负责对真实角色的应用,把所有抽象主题类定义的方法限制委托给真实主题角色来实现,并且在真实主题角色处理业务逻辑前后做预处理和善后工作。

2 代理模式的通用代码

抽象主题角色

// 抽象主题角色
public interface Subject {
    public void request();
}

具体主题角色

// 具体主题角色
public class RealSubject implements Subject{
    @Override
    public void request() { /*业务逻辑代码*/ }
}

代理角色

// 代理角色
public class Proxy implements Subject{
    private Subject subject = null;

    @Override
    public void request() {
        if (subject == null) subject = new RealSubject();
        this.before();
        subject.request();
        this.after();
    }

    // 前置处理
    private void before() {}

    // 后置处理
    private void after() {}
}

3 案例演示

3.1 需求

玩游戏打怪升级,找代练。

3.2 代码演示

抽象主题类:抽象玩家接口

public interface IGamePlayer {
    // 登录
    void login(String playName, String password);
    // 打怪
    void killMonster();
    // 升级
    void levelUp();
}

具体主题角色:游戏玩家

public class GamePlayer implements IGamePlayer{
    // 游戏玩家名称
    private String playName;

    public GamePlayer(String playName) { this.playName = playName; }

    @Override
    public void login(String playName, String password) {
        // 验证用户名密码,纯演示就省略了。。。。
        System.out.println(this.playName + "登录游戏成功!");
    }

    @Override
    public void killMonster() { System.out.println(this.playName + "正在打怪!"); }

    @Override
    public void levelUp() { System.out.println("恭喜" + this.playName + "又升了一级!"); }
}

代理角色:游戏代练

public class GamePlayerProxy implements IGamePlayer {

    // 对谁进行代练
    private IGamePlayer gamePlayer;
    public GamePlayerProxy(IGamePlayer gamePlayer) { this.gamePlayer = gamePlayer; }

    @Override
    public void login(String playName, String password) { this.gamePlayer.login(playName,password); }

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

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

客户端演示

public class Client {
    public static void main(String[] args) {
        // 定义一个玩家
        IGamePlayer gamePlayer = new GamePlayer("zhangsan");
        // 定义一个代练,并指定对谁进行代练
        IGamePlayer gamePlayerProxy = new GamePlayerProxy(gamePlayer);
        // 代练登录玩家账号
        gamePlayerProxy.login("zhangsan","123456");
        // 代练帮玩家打怪
        gamePlayerProxy.killMonster();
        // 玩家的账号升级了
        gamePlayerProxy.levelUp();
    }
}

演示结果

在这里插入图片描述

这个叫“zhangsan”的玩家全程自己都没有登录游戏,全部都是代练在玩,这个玩家的游戏账号同样也升级了。自己不需要动手操作,别人帮你把活都干完了,这就是代理模式。

4 代理模式的应用

4.1 代理模式的优点

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

②***高扩展性*** 具体主题角色是随时都会发生变化的,只要它实现了接口,甭管它如何变化,都逃不脱接口的控制,那么我们的代理类就可以在不做任何修改的情况下使用。

智能化*** 上面的案例没有体现出来,这个优点会在动态代理*的演示种体现出来。

4.2 应用场景

开发中的使用场景非常多,比如Spring AOP就是典型的动态代理,生活中比如演员替身、律师帮你打官司等等。

5 代理模式的扩展

这里解释两个概念:普通代理透明代理(强制代理)。

普通代理 用户要知道代理的存在,比如上述的GamePlayerProxy这个类,调用者需要知道它来进行访问

强制代理 调用者不需要知道代理是否存在,它直接调用真实角色,它的代理的产生是由真实角色自己决定的

5.1 普通代理

前面的案例中调用者是依赖被代理角色的,在这里就不能new一个GanmePlayer对象了,它必须由代理角色通过代理来访问了。修改类图如下:

在这里插入图片描述

对GamePlayer类进行修改:

// 具体主题角色:游戏玩家
public class GamePlayer implements IGamePlayer{
    // 游戏玩家名称
    private String playName;

    public GamePlayer(GamePlayerProxy proxy, String playName) throws Exception {
        // 必须通过代理
        if (proxy == null) {
            throw new Exception("不能创建角色");
        } else {
            this.playName = playName;
        }
    }

    @Override
    public void login(String playName, String password) {
        // 验证用户名密码,纯演示就省略了。。。。
        System.out.println(this.playName + "登录游戏成功!");
    }

    @Override
    public void killMonster() { System.out.println(this.playName + "正在打怪!"); }

    @Override
    public void levelUp() { System.out.println("恭喜" + this.playName + "又升了一级!"); }
}

对GamePlayerProxy进行修改:

// 代理角色:游戏代练
public class GamePlayerProxy implements IGamePlayer {

    // 对谁进行代练
    private IGamePlayer gamePlayer;
    public GamePlayerProxy(String playName) {
        try {
            gamePlayer = new GamePlayer(this, playName);
        }catch(Exception e) {
            // TODO ....
        }
    }

    @Override
    public void login(String playName, String password) { this.gamePlayer.login(playName,password); }

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

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

修改CLient:

public class Client {
    public static void main(String[] args) {
        // 定义一个代练,并指定对谁进行代练
        IGamePlayer gamePlayerProxy = new GamePlayerProxy("zhangsan");
        // 代练登录玩家账号
        gamePlayerProxy.login("zhangsan","123456");
        // 代练帮玩家打怪
        gamePlayerProxy.killMonster();
        // 玩家的账号升级了
        gamePlayerProxy.levelUp();
    }
}

演示结果:

在这里插入图片描述

分析 运行结果完全相同。在该模式下,调用者只知道代理而不知道真实的角色是谁,屏蔽了真实角色的变更对高层模块的影响,只要你实现了接口对应的方法,真实角色的修改不影响高层次模块的使用,该模式较适合对扩展性要求较高的场合。当然了,在实际开发中,一般都是用过约定来禁止new真实的角色。普通代理模式的约束问题,尽量通过团队内的编程规范类约束,因为每一个主题类是可被重用和可维护的,使用代码约束的方式对后期维护也是非常不利的。

5.2 强制代理

强制代理就是调用者必须通过真实角色查找到代理角色,否则不能访问。不管你是通过代理类还是new一个真实角色类,都不能访问,只有通过真实角色指定的代理类才可以访问,也就是说真实角色管理代理角色。生活中的例子:找明星,得找他的经纪人吧,找他自己或者找别的经纪人都没有用,只有找他指定的经纪人才行;有时候找老板也是的,得找他的秘书。。。

UML类图如下:
在这里插入图片描述

对IGamaPlayer进行修改:

public interface IGamePlayer {
    // 登录
    void login(String playName, String password);
    // 打怪
    void killMonster();
    // 升级
    void levelUp();
    // 强制代理,必须找自己的代理
    IGamePlayer getProxy();
}

对GamePlayer进行修改:

public class GamePlayer implements IGamePlayer{
    // 游戏玩家名称
    private String playerName;
    private IGamePlayer gamePlayerProxy;

    public GamePlayer(String playerName) {
        this.playerName = playerName;
    }

    // 指定自己的代理
    @Override
    public IGamePlayer getProxy() {
        gamePlayerProxy = new GamePlayerProxy(this);
        return gamePlayerProxy;
    }

    @Override
    public void login(String playName, String password) {
        if (isMyProxy()) {
            System.out.println(this.playerName + "登录游戏成功!");
        }
    }

    @Override
    public void killMonster() {
        if (isMyProxy()) {
            System.out.println(this.playerName + "正在打怪!");
        }
    }

    @Override
    public void levelUp() {
        if (isMyProxy()) {
            System.out.println("恭喜" + this.playerName + "又升了一级!");
        }
    }

    private boolean isMyProxy(/*IGamePlayer proxy*/) {
        // 按照逻辑还需要判断下是否是自己返回的那个代理类,即proxy == gamePlayerProxy?我这里纯演示就不写了
        if (this.gamePlayerProxy != null) {
            return true;
        }
        System.out.println("请联系我的代理");
        return false;
    }
}

对GamePlayerProxy进行修改:

public class GamePlayerProxy implements IGamePlayer {

    // 对谁进行代练
    private IGamePlayer gamePlayer;
    public GamePlayerProxy(IGamePlayer gamePlayer) {
        this.gamePlayer = gamePlayer;
    }

    // 指定自己的代理
    @Override
    public IGamePlayer getProxy() {
        // 代理的代理,这里就是自己
        return this;
    }

    @Override
    public void login(String playName, String password) { this.gamePlayer.login(playName,password); }

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

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

情景一:没有通过代理,直接new对象访问,演示结果如下:
在这里插入图片描述

情景二:调用者自己new的代理,不是自己指定的代理,演示结果如下:
在这里插入图片描述

情景三:通过真实角色指定的代理,演示结果如下:
在这里插入图片描述

5.3 有个性的代理

一个类可以实现多个接口,完成不同任务的整合。即代理类不仅仅可以实现主题接口,也可以实现其他接口完成不同的任务,或者理解为对目标对象的方法进行拦截和过滤。从上面的案例,我们修改下需求:并不是玩家找我代练就可以了,你得付钱我才帮你代练。

修改UML类图:
在这里插入图片描述

添加代理类的过滤接口IProxy:

// 代理类的控制接口
public interface IProxy {
    boolean pay();
}

修改代理类GamePlayerProxy:

public class GamePlayerProxy implements IGamePlayer,IProxy {

    // 对谁进行代练
    private IGamePlayer gamePlayer;
    public GamePlayerProxy(IGamePlayer gamePlayer){
        if (pay()) {
            this.gamePlayer = gamePlayer;
        } else {
            // 需要添加未付款时gamePlayer为null的处理
            System.out.println("付钱。。。");
        }
    }

    // 指定自己的代理
    @Override
    public IGamePlayer getProxy() {
        // 代理的代理,这里就是自己
        return this;
    }

    @Override
    public void login(String playName, String password) { this.gamePlayer.login(playName,password); }

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

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

    @Override
    public boolean pay() {
        System.out.println("代练共收费500元,已付款。");
        return true;// 默认就算受过费用了,对是否收费可以自行实现
    }
}

演示结果如下:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

奔跑吧,高同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值