恭喜胡歌喜提小棉袄一件,彭于晏表示压力很大

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
国际/港澳台短信套餐包,全球plus 100条 6个月
短信服务,100条 3个月
简介: 恭喜胡歌喜提小棉袄一件,彭于晏表示压力很大

设计模式系列 - 连载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个类

  1. BizController
  2. SmsMessageProxy
  3. 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", "我爱你");
    }
}

可以发现

动态代理类能够适用变化的问题

当目标类发生变化

代理类可以不变

甚至可以一个代理管多个目标类

这就是通过反射来实现的

总结

代理模式是一种常用的设计模式

解决类之间的线性调用问题

通过代理

可以使非功能需求得到很好的解决

目录
打赏
0
0
0
0
56
分享
相关文章
关于我用半个月过了软件设计师这件事
这篇文章分享了作者在半个月内通过软件设计师考试的经验,包括快速刷视频了解知识体系、针对性地刷历年真题并根据错题加强知识点巩固、进行模拟考试和总结,以及使用笔记软件记录重要知识点的方法。
关于我用半个月过了软件设计师这件事
大环境不好?来看看前同事30K月薪掌握了什么技术!
大环境不好?来看看前同事30K月薪掌握了什么技术!
79 0
终于来新同事了,没想到竟是我噩梦的开始
终于来新同事了,没想到竟是我噩梦的开始
76 0
平时做开发需要掌握哪些数据库方面的知识(个人经验之谈)
平时做开发需要掌握哪些数据库方面的知识(个人经验之谈)
256 0
学弟学妹们,学会霍夫曼编码后,再也不用担心网络带宽了!(2)
学弟学妹们,学会霍夫曼编码后,再也不用担心网络带宽了!
137 0
学弟学妹们,学会霍夫曼编码后,再也不用担心网络带宽了!(2)
学弟学妹们,学会霍夫曼编码后,再也不用担心网络带宽了!(1)
学弟学妹们,学会霍夫曼编码后,再也不用担心网络带宽了!
130 0
学弟学妹们,学会霍夫曼编码后,再也不用担心网络带宽了!(1)
这下女友总算满意了!
上次跟女友介绍了正则表达式的基本语法,以及在 Python 中如何使用。结果她还不满意,说传说中的正则表达式就这么简单?当然不是,今天就来跟大家一起介绍下正则表达式更多的使用技巧。
172 0
惊心动魄!程序员们说这些时刻再也不想经历了
下面的这个场景你熟悉吗: 在一个月黑风高的晚上,大风无情的刮落着树上的枝叶。一个少年突然从睡梦中惊醒,发现已是一身冷汗,他看了看时间,才凌晨三点多,然后又重新闭上眼睛平复心跳,面无表情地躺在床上一动不动,他要努力着让自己睡着,因为他已经好几天没有睡觉了,他的身体需要好好的休息。
7988 0

相关实验场景

更多
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等