用代理模式优雅地写代码

Java的代理模式在开发中经常使用,作为设计模式的一种,在很多场景下都有应用。

代理模式通常分为两种 · 静态代理 · 动态代理 关于代理模式,今天先由浅到深说一下静态代理。

没有代理的世界

通常来说,代理模式是用来解耦的,可以把代理模式认为是加入了中介的 Producer/Consumer 模式。 在没有代理的情况下,生产者直接和消费者耦合,这会导致一些问题,比如对某一方的逻辑调整会导致大面积的修改代码。

举一个场景,有个网页向用户提供阅读的功能,产品说你们先把阅读功能上线。 好了现在我们知道有一个统一的动作 read(),抽象出来做为接口吧。

interface Func {
    void read();
}

每个用户都来实现下这个接口,

public class User implements Func {
    void read(){
    ....
    }
}

现在当用户进入我们的网页的时候,我们就可以实例化一个用户对象,然后调用它的read方法。 为了初步解耦,我们用接口来声明,

Func user = new User();
user.read();

so far so good… 虽然我们的代码中到处充斥着这种样板代码,重复的实例化和调用接口,但是需求算是实现了。 然而这样的耦合程度会给自己挖很深的坑,特别是当需求发生变更的时候。 比如下面这样…

引入代理

第一版上线后,用户广泛好评,产品说,既然如此,我们不能免费给用户使用啊,不如我们插个广告吧!

好了,现在我们有两个选择 · 在项目中每个实例调用read()的地方前面插一句 playAdv() · 修改 User的 read()实现逻辑,在前面加个 playAdv()

第一个明显会加大我们的工作量,实例化的地方少还好,要是项目里有几百处地方直接调用实例的接口,我们估计要跪… 第二种虽然相对优雅,可是坑也不小,因为"播放广告"这个行为并不是用户的,我们希望功能尽可能的纯粹。 如果考虑到以后还要区分注册用户/付费用户,第二种修改方式带来的问题也很明显。

其实我们还有第三种选择,引入代理。 先上代理的代码

public class Proxy implements Func {
    private Func mUser;

    public Proxy(Func user) {
        this.mUser = user;
    }

    public void read() {
        mUser.read();
    }
}

上面就是一个简单的代理类的代码了,它也实现了Func的功能, 重点是它的通过构造方法传入了一个被代理的 User对象。 现在我们来看看引入代理有什么好处。

我们的之前调用 User并实例化的地方会变成这样

Proxy proxy = new Proxy(new User());
proxy.read();

咋一看,没什么变化,只是从对 User的调用变成对代理的调用。

但只要仔细思考一下就明白,这样一来,我们就不用关心具体对象的行为了,所有的事情都交给了代理。 当产品说要加个广告时,我们也不需要往用户类里加新逻辑(因为那不是用户行为),我们只需要修改一下代理类,

public class Proxy implements Func {
    private Func mUser;

    public Proxy(Func user) {
        this.mUser = user;
    }

    public void read() {
        AdvUtil.playAdv(); <- 播放广告
        mUser.read();
    }
}

至于调用 Proxy的地方,啥都不用改。 我们用 Proxy,在Producer和Consumer之间加了一层中介,这样一来即使要对Consumer的行为进行干预,也不用到处去修改代码了。

现在因为 Proxy的存在,就算产品再怎么改需求也不怕了。 想加个广告?改一下代理就行。 想加个会员免广告?改一下代理就行。

总结

代理模式总结起来就是通过一层 Proxy层,把 Producer和Consumer之间隔开,让他们之间尽可能的少耦合。 这样当需要操作 Consumer的行为时,只需要修改 Proxy层,而不需要到处去调整 Producer的代码。

但是静态代理的弊端也是很明显的。 当接口的实现类变多时,每次的接口调整也需要修改很多代码。 可以想象一下,加入现在有 免费用户/包月用户/包年用户等类型的用户,包括 Proxy,他们都实现了 Func接口, 此时如果说要给 Func加一个接口,或者给 read()接口加一个参数, 需要调整的代码量也是很可怕的。 所以相对静态代理,就有了动态代理这种神奇的东西。 我们以后再接着分析动态代理的实现和原理。

原文发布于微信公众号 - Android每日一讲(gh_f053f29083b9)

原文发表时间:2018-04-27

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏owent

Rust的第二次接触-写个小服务器程序

蛮久前入门了一下 Rust 语言。它的设计模型非常地吸引C/C++的开发者。但是学习语言嘛还是要练习一下,之前也用它给我们项目写了个命令行小工具。这回拿来写个小...

1.1K3
来自专栏程序员的知识天地

用 Python 抓网页,你想问的都帮答好了,你还有不懂的吗?

近年来,随着大数据、人工智能、机器学习等技术的兴起,Python 语言也越来越为人们所喜爱。但早在这些技术普及之前,Python 就一直担负着一个重要的工作:自...

1373
来自专栏林冠宏的技术文章

php isset( $test ) 的神奇之处。

这次总结下 php 的一个 函数 :  boolean isset($test), 返回值:boolean类型,传入参数不为空,返回true,反之,false ...

19110
来自专栏程序员宝库

给Python新手的一些编码建议

每天你都应该努力提升自己的编码技能,今天我给Python新手带来了一些编程建议。 Python箴言 打开Python交互终端并运行下面命令 ? 然后命令会有一...

38310
来自专栏子勰随笔

SDK开发经验之开发习惯

25910
来自专栏Java社区

Java核心技术讲解学习

1503
来自专栏瓜大三哥

UVM(七)之phase及objection

UVM(七)之phase及objection 这两个概念与UVM验证平台息息相关,phase就好比铁轨,让UVM这趟列车在铁轨上向前运行,不会脱轨,不...

4708
来自专栏帮你学MatLab

Robotics System Toolbox路径规划

filePath = fullfile(fileparts(which('PathPlanningExample')),'data','exampleMaps....

2372
来自专栏向治洪

qq安全原理

    故事总要有缘由,那么这个故事的缘由就是,当我以前写了一个获取其它进程密码框密码的时候(前几篇博客中有描述),我抱着试一试的心情去试探了一下能不能得到 Q...

1768
来自专栏撸码那些事

我看依赖注入

1483

扫码关注云+社区

领取腾讯云代金券