设计模式系列 - 连载3
当年胡歌放话说:
“彭于晏都没结婚,他不急,我也不急。”
结果今天突然官宣当爹
喜提宝贝女儿
今天,大概小彭同学
是不是可以急一急了?
遥想5年后
小胡妹妹都可以上街
替爸比打酱油了
正好我们可以讲一讲小代理的故事
结构型模式
前面两章
主要是介绍创建型模式
今天开始介绍结构型
主要分为7种
分别是:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式
他们的共同特点是,通过组合,让简单的类组成更大、更复杂的结构
首先介绍我最喜欢用的:代理模式
代理模式
代理的核心是
对象直接不直接调用
而是有一个代理者作为中介
形式上满足调用的规则
很多人读书时
不敢和喜欢的同学表白
而是让其他同学传话
这个传声筒,就是代理
常见的代理
生活中的代理很多
房产中介、快递小哥、代办年检……
这些共同特点是,某人需要一项服务
但他自己不方便、不会、不擅长
所以需要一个代理来做
还有另一种
比如:传达室大爷、总裁的秘书……
某人需要接触到对方
但对方不让你接触
只能间接进行联系
写一个最简单的代理类
写一个发短信的例子
public class SmsMessageProxy{ /** * 电信的短信服务 */ private ChinaTelecomSmsService service; /** * 服务访问令牌 */ private String access_token="JSHDQ-EKQKH-KKAOW-9WWJE"; public void send(String mobile, String content){ service.send(access_token, new Date(),mobile, content); } } public class BizController{ private SmsMessageProxy smsMessageProxy; public DTO finish(Param param){ // ... smsMessageProxy.send(param.getMobile(), "您的业务已办结"); return DTO.success(); } }
这就是一个简单的例子
有3个类
- BizController
- SmsMessageProxy
- ChinaTelecomSmsService
1 作为业务的执行者,无法和 3 直接通讯
而是间接使用了 2 这个代理类
通过间接调用,形式上完成了 1~3 的调用
我们可以发现
代理类除了转发请求之外
还额外做了令牌传参,日期传参工作
其实是简化了调用的参数
给他们提供了默认值
这样做对使用方是有利的
代理的应用场景
试想一下
我有没有这方面的需要
比如监控,比如统计,还有事务、日志、权限等等
这些其实都可以用代理proxy来实现
主要解决一些非功能性的需求开发
以日志为例
核心功能类只需瞒住功能性的需求
然后用代理进行包装
将日志写在代理类中
这样的话,直接调用核心类是没有日志的
当需要日志时,采用代理转发的方式实现。
静态代理
上面的示范是最简单的代理
一个目标类 target
一个代理类 proxy
他们彼此对应
因此有人经常将两者共同抽象出一套接口
这种代理方式可以解决常规的一些问题
比如:
/** * 短信服务接口 */ public interface SmsService{ void send(String mobile, String content); } /** * 电信短信服务 */ public class TelecomSmsService implements SmsService{ public void send(String mobile, String content){ // TODO 发送电信短信 } } /** * 代理类 */ public class SmsProxy implements SmsService{ private SmsService service; public void send(String mobile, String content){ // TODO 设置请求 header 等鉴权信息 service.send(mobile, content); // TODO 记录发送日志 } } /** * 测试类 */ public class ProxyTest{ @Test void test(){ SmsProxy proxy=new SmsProxy(); proxy.send("13812345678", "我爱你"); } }
这就是简单的实现
在Spring的AOP里
就大量的使用了代理模式
大家如果经常看日志
就会对proxy这个词不陌生
静态代理存在的问题
前面这个例子能解决简单问题
但随着类的内容越来越多
每次 target 类发生变化,或者增加子类
proxy 都有可能需要同步调整
这加大了维护的代价
因此我们希望有一种尽量少改代码的方法
动态代理
动态代理的核心是反射 reflect
他能解决,一个类,对另外一堆相似的类的统一代理问题
还是前面的例子,我们有一个电信短信类
但后面可能会扩充 移动 和 联通
如果想少改代码
应该怎么做呢?
来看一个示范
/** * 短信服务接口 */ public interface SmsService{ void send(String mobile, String content); } /** * 电信短信服务 */ public class TelecomSmsService implements SmsService{ public void send(String mobile, String content){ // TODO 发送电信短信 } } // 以上两个类和前面一样,下面是新增的类 /** * 联通短信服务 */ public class UnicomSmsService implements SmsService{ public void send(String mobile, String content){ // TODO 发送联通短信 } } /** * 微信服务,接口和前面的不一样,但方法名类似 */ public class WechatService implements NetService{ public void send(String appid, String openid, String content){ // TODO 发送微信公众号消息 } }
现在问题变复杂了
不但有多个平行子类
还有个完全搭不上关系的微信消息类
此时代理怎么做呢?
Java里有一个代理公共接口 InvocationHandler
他的用法我们举一个例子
public class MessageProxy implements InvocationHandler { private Object bean; public MessageProxy(Object bean) { this.bean=bean; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object re=null; String methodname=method.getName(); if (methodname.equals("send")){ // TODO 设置请求 header 等鉴权信息 re = method.invoke(bean, args); // TODO 记录发送日志 } return re; } } /** * 测试类 */ public class ProxyTest{ @Test void test(){ MessageProxy proxy=new MessageProxy(new TelecomSmsService()); SmsService service0=(SmsService)Proxy.newProxyInstance(proxy.getClass().getClassLoader(), new Class[] {SmsService.class}, proxy); service0.send("13812345678", "我爱你"); MessageProxy proxy1=new MessageProxy(new UnicomSmsService()); SmsService service1=(SmsService)Proxy.newProxyInstance(proxy.getClass().getClassLoader(), new Class[] {SmsService.class}, proxy); service0.send("13812345678", "我爱你"); MessageProxy proxy2=new MessageProxy(new WechatService()); NetService service2=(NetService)Proxy.newProxyInstance(proxy.getClass().getClassLoader(), new Class[] {SmsService.class}, proxy); service0.send("wx81283123", "oejak8123123", "我爱你"); } }
可以发现
动态代理类能够适用变化的问题
当目标类发生变化
代理类可以不变
甚至可以一个代理管多个目标类
这就是通过反射来实现的
总结
代理模式是一种常用的设计模式
解决类之间的线性调用问题
通过代理
可以使非功能需求得到很好的解决