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

本文涉及的产品
短信服务,100条 3个月
日志服务 SLS,月写入数据量 50GB 1个月
短信服务,200条 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", "我爱你");
    }
}

可以发现

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

当目标类发生变化

代理类可以不变

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

这就是通过反射来实现的

总结

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

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

通过代理

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

相关文章
|
7月前
|
测试技术 程序员 项目管理
甲方怒喷半小时:一次项目上线失败的深刻教训
小米分享了一次项目上线失败的经历,起因是运营提出一个看似简单的白名单功能。问题包括:没有需求原型导致理解偏差,新成员对项目不熟悉,测试流程不全面,以及人员变动大。解决方案涉及需求确认会、原型图设计、交接制度、团队培训和全流程测试等。这次失败提供了关于需求分析、项目管理及团队协作的教训。
62 2
|
存储 消息中间件 缓存
大环境不好?来看看前同事30K月薪掌握了什么技术!
大环境不好?来看看前同事30K月薪掌握了什么技术!
70 0
|
小程序 安全 Java
你还只会用 AtomicXXX ?!恭喜你,可以下岗了!
你还只会用 AtomicXXX ?!恭喜你,可以下岗了!
你还只会用 AtomicXXX ?!恭喜你,可以下岗了!
|
程序员
能让程序员瞬间崩溃的五个瞬间,共鸣的同学请举手!
在我们的眼里,程序员好像是无所不能的,那么复杂的App和那些游戏都是他们做出来的,这让我们很难相信还有什么是他做不出来的。不过,就是我们每天眼里看着很厉害的程序员,每天都要面临的就是头疼,头疼,头好疼,特别是我接下来要说的几件事情,几乎是所有程序员都会把头抓秃的事     那么这五件事情究竟是什么事呢? 写着代码停电,代码没有保存 如果有一天突然代码写到一半,眼看就快要完工了,突然一下就断电,代码没保存。
1340 0
|
弹性计算 运维 物联网
|
芯片
瞧!公务员的工作还可以这样干
盼啊盼,第六届世界互联网大会在乌镇如期而至。 在今天的大会上,小云带来了帮助公务员提升工作效率的“神器”,平头哥压箱底的“宝贝”...... 一起来深入了解下。
9263 0
|
小程序 Android开发 容器
为何小程序上线了,他们的内心却留下遗憾?
你的小程序还是那么卡卡卡卡么?
7728 0
为何小程序上线了,他们的内心却留下遗憾?
|
安全 生物认证 定位技术
老板,我要一台抗摔抗腐蚀的PC
极端天气和环境,都对PC的需求提出了极致考验。正所谓,术业有专攻,一台能抗摔又抗腐蚀的PC哪里找,结果老板推荐了这台戴尔Latitude Rugged坚固型笔记本。
工作半年遇到最奇葩的问题
工作半年遇到最奇葩的问题 背景 公司最近买了一套项目,在启动的时候出现了一系列奇怪的问题,对方的技术栈要求是Tomcat7启动,但是由于我们公司出于安全的考虑所以是要求用Tomcat9进行启动的。 问题描述 下面情况都是相同war包相同Tomcat情况下 系统 Tomcat版本 能否启动 Windows Tomcat7 能 Windows Tomcat9 能 macOS Tomcat7 能 macOS Tomcat9 不能 Linux Tomcat7 能 Linux Tomcat9 不能 由于对于项目的不熟悉,导致找了很久才找出来原因。
947 0
|
Java 程序员 应用服务中间件
阿里员工吐槽:工作压力真的大,年假都没心情用,后悔来阿里
阿里巴巴是当下国内最顶尖的互联网公司之一,经过19年的发展,如今的阿里已经从单纯的电子商务公司过渡到了综合型的超级巨头。如今阿里旗下的阿里云已经成为了国内第一,世界第三的云计算公司。
4217 0

相关实验场景

更多