【设计模式】牛掰格拉斯的代理模式


代理的本质



无论任何时候,只要谈到设计模式,大脑中一定要蹦出这四个字“活学活用”。

要想对某个事物做到活学活用,必须要对它足够了解,甚至要剖析到本质才行。

总是会有些人说,我干嘛要知道原理,干嘛要去看源码?会用就行了。对于这种情况,我只有五个字相送,“你开心就好”。

不可否认,认识一个陌生事物,大部分情况还是要从定义开始。

代理模式为某对象提供一种代理以控制对该对象的访问,从而限制、增强或修改该对象的一些特性。

如果对代理模式本身就很熟悉的人,一眼就明白什么意思,甚至连代码怎么写都会浮现在脑海中。

关键是对代理模式一点都不熟悉的人,看到定义后绝对一脸懵。下面通过简单的图形来揭开迷惑。

没有使用代理模式,如下图01:


”直接访问“目标”。

使用了代理模式,如下图02:


”访问代理代理访问“目标”。


人群中一定会有两种声音:

1)设计模式是很高深莫测的东西,有这么简单吗?

2)这怎么没有代码实现啊?

来听听作者的看法:

1)高深莫测和简单不一定都是对冲的。幸福绝对是高深莫测的,那什么是幸福呢,最多的答案恐怕就是,“一亩土地两头牛,老婆孩子热炕头”。

多么简单朴实的答案,可惜,包括我在内的很多人都追求不到。主要原因是我们人为(主观)的把很多事情搞复杂了。下面这个事情可以说明这一点。

中秋节放假时到附近的一个小景区去看一看,我发现有一种果树的果子挂满枝头,又大又圆,让人看了之后特别有欲望,甚至垂涎欲滴,可惜没有一个人去摘。

我的第一反应就是果子肯定不能吃。过了一会儿,终于抑制不住好奇心,就找了一个比较矮的果树,摸了摸枝头上的果子。果然硬如磐石。

2)对于一上来就说代码实现的人,只能认为是你拥有了一把锤子之后,看什么都像钉子。

代码实现永远都是最后一步,但在它之前,要找出问题,分析问题,给出方案,论证方案可行性。如果这些都OK了,代码就是水到渠成的事了。

记住这句话:

认清原理,搞清本质,永远都是最重要的,不单单是在写代码上,在社会上依然如此。

那代理的本质是什么呢?就两个字,“加层”,即增加一(多)层。这就是本质。

像其它的什么“控制访问”啦,“增强或修改特性”啦,只不过是这个“层”产生的一些(副)作用罢了。


生活中充满着代理


虽然大部分人都没有参与过诉讼,但作为常识我们都知道,当事人可以通过协议把自己的一些权力授权给律师,律师就可以在法庭上行使这些权利,此时律师就是代理。

现在网购已成为生活的一部分,但是收快递却比较麻烦,因为通常家中无人。此时菜鸟驿站(或妈妈驿站)出现了,它帮我们签收和暂存包裹,所以它就是代理。

现在生活压力大,每个人都要上班,所以中午和下午都没有时间去接送自己上小学的孩子,此时只能选择午托,我们交了托费之后就等于给了它授权,它代替我们去接送孩子,可见午托也是代理。

还有各种产品的代理商,有大区代理,省级代理,市级代理,等等。还有就是微商/代购,微商自己没有货品,只是发发朋友圈,最终是别人发货。代购就更直接了,代替你去购买,然后再邮寄给你。

明星艺人都有自己的经纪人,可以替自己接一些活动,讨价还价,安排日程等。此时经纪人就是代理。还有大BOSS也会请一到多个秘书,来代替自己做一些事情。

可见,生活中充满着很多代理,他们以各种各样的形式存在着,发挥着各种各样的功能和作用,也确实解决了很多社会和生活的问题。

但从本质来看,代理大都以“层”的形式呈现,站在老板的前面,替老板做事情。


和计算机相关的代理


大部分人可能都听过这样一句话,凡是遇到不好解决的问题,大都可以通过加一层得到有效的解决。加的这个层很多时候和代理有关。

为了网络安全问题,可以加一层防火墙,防火墙虽然不完全是代理,但却用到许多代理的理论和技术。

为了扩充本地局域网络,可以加一层交换机或路由器,它用来代理和转发网络请求。还会有一些附带的其它功能。

为了平衡多个服务器的处理能力,我们在前面加一个请求路由层,也就是负载均衡器了,如Nginx,它可以代理请求,并按规则转发。

再说说CRUD,原来是我们写代码直接使用JDBC访问数据库,现在我们写代码使用的是ORM框架,ORM框架再使用JDBC去访问数据库。ORM框架可以看作是JDBC的代理。

我们可以看到,从硬件到中间件,再到程序框架,都有代理的影子。


和编程相关的代理


上面所说的代理,都是广义的代理,主要侧重于角色和功能。

一旦在编程中谈到代理,基本就是狭义代理了。除了广义代理的要求外,还要保持类型的兼容和“接口”的一致

说白了就是需要被代理对象的地方,给它一个代理也可以。可以在被代理对象上调用的方法,在代理上也可以调用。

这个要求和代理模式中的要求是一样的。

如果被代理的对象是一个类。我们用Target表示。

class Target {

    public String getDateTime() {
        return "2019-10-09"
    }

}


为了保持类型兼容和接口一致,我们需要生成一个子类。

除此之外还要有被代理的对象,所以还需一个成员变量。

为了添加一些功能,通常需要重写一些方法。

class Proxy extends Target {

    private Target target;

    @Override
    public String getDateTime() {
        return target.getDateTime() + ", 星期三";
    }

}

这样就生成了一个代理,在需要Target的地方换成Proxy也没有问题,而且还会在日期后面加上星期。

如果被代理的对象是一个接口。我们用ITarget表示。

interface ITarget {

    String sayHello(String name);

}

 

class Target implements ITarget {

    @Override
    public String sayHello(String name) {
        return "hello " + name;
    }

}


此时我们只需实现接口即可(当然也可以生成子类),其它的保持不变。

class Proxy implements ITarget {

    private ITarget target;

    @Override
    public String sayHello(String name) {
        return target.sayHello(name) + ", long time no see.";
    }
}

这也是一个代理,同样可以使用Proxy代替Target,而且在原来问好的基础上增加了更多的话语。

由于类型兼容且接口一致,所以用户代码有时也不知道到底是对象本身还是它的代理,不过这通常并不重要。

代理的好处我们已经看到了,但是也有不好的地方,就是要写代理的代码,造成代码量增加。

这个问题已经通过动态代理解决了。在Java里比较有名的动态代理,就是JDK动态代理CGLIB代理。这大家都知道了。

全文总结

代理的本质就是通过加一层来解决问题。类型兼容和接口一致只是限制条件而已。

代理有着广泛的应用,想想Spring的成功,代理贡献了多少,绝对功不可没。

仔细体会下“加层”的含义,在代码中和生活中,你会发现它真的很牛掰格拉斯。

 

>>> 玩转SpringBoot系列文章 <<<

 

【玩转SpringBoot】配置文件yml的正确打开姿势

【玩转SpringBoot】用好条件相关注解,开启自动配置之门

【玩转SpringBoot】给自动配置来个整体大揭秘

 

>>> 品Spring系列文章 <<<

 

品Spring:帝国的基石

品Spring:bean定义上梁山

品Spring:实现bean定义时采用的“先进生产力”

品Spring:注解终于“成功上位”

品Spring:能工巧匠们对注解的“加持”

品Spring:SpringBoot和Spring到底有没有本质的不同?

品Spring:负责bean定义注册的两个“排头兵”

品Spring:SpringBoot轻松取胜bean定义注册的“第一阶段”

品Spring:SpringBoot发起bean定义注册的“二次攻坚战”

品Spring:注解之王@Configuration和它的一众“小弟们”

品Spring:bean工厂后处理器的调用规则

品Spring:详细解说bean后处理器

品Spring:对@PostConstruct和@PreDestroy注解的处理方法

品Spring:对@Resource注解的处理方法

品Spring:对@Autowired和@Value注解的处理方法

品Spring:真没想到,三十步才能完成一个bean实例的创建

品Spring:关于@Scheduled定时任务的思考与探索,结果尴尬了

 

>>> 热门文章集锦 <<<

 

毕业10年,我有话说

【面试】我是如何面试别人List相关知识的,深度有点长文

我是如何在毕业不久只用1年就升为开发组长的

爸爸又给Spring MVC生了个弟弟叫Spring WebFlux

【面试】我是如何在面试别人Spring事务时“套路”对方的

【面试】Spring事务面试考点吐血整理(建议珍藏)

【面试】我是如何在面试别人Redis相关知识时“软怼”他的

【面试】吃透了这些Redis知识点,面试官一定觉得你很NB(干货 | 建议珍藏)

【面试】如果你这样回答“什么是线程安全”,面试官都会对你刮目相看(建议珍藏)

【面试】迄今为止把同步/异步/阻塞/非阻塞/BIO/NIO/AIO讲的这么清楚的好文章(快快珍藏)

【面试】一篇文章帮你彻底搞清楚“I/O多路复用”和“异步I/O”的前世今生(深度好文,建议珍藏)

【面试】如果把线程当作一个人来对待,所有问题都瞬间明白了

Java多线程通关———基础知识挑战

品Spring:帝国的基石

 

 

作者是工作超过10年的码农,现在任架构师。喜欢研究技术,崇尚简单快乐。追求以通俗易懂的语言解说技术,希望所有的读者都能看懂并记住。下面是公众号的二维码,欢迎关注!