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

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 恭喜胡歌喜提小棉袄一件,彭于晏表示压力很大

设计模式系列 - 连载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", "我爱你");
    }
}

可以发现

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

当目标类发生变化

代理类可以不变

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

这就是通过反射来实现的

总结

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

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

通过代理

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

相关文章
|
数据采集 SQL 运维
巧用指标平台DataIndex,五步法轻松实现指标管理
在业务发展初期,企业需要做好规范的指标管理,以保证随着业务的不断发展,数据化决策能够成为业务强有力的支撑。本文将为大家详解如何通过袋鼠云指标管理平台DataIndex 进行规范化的指标开发管理,轻松开发指标,避免各类指标问题。
1535 0
|
Rust 编译器 开发者
Rust宏之derive的设计及实战
【10月更文挑战第18天】在 Rust 中,`derive` 宏是一种自动生成代码的工具,可为结构体和枚举类型自动实现特定 trait,减少重复代码。它通过语法糖简化代码,支持 Debug、Clone、PartialEq 等 trait 的自动实现,并允许开发者自定义 `derive` 宏以扩展功能。
333 1
|
SQL 安全 前端开发
SourceCodester v1.0 SQL 注入(CVE-2023-2130)
SourceCodester v1.0 SQL 注入(CVE-2023-2130)
SourceCodester v1.0 SQL 注入(CVE-2023-2130)
|
安全 Linux Shell
用户和组高级操作
本文介绍了Linux系统中用户和组管理的基本操作,包括使用`usermod`命令修改用户属性、使用`passwd`和`usermod`命令禁用和恢复用户账户、使用`userdel`命令删除用户账户、使用`groupadd`、`groupdel`和`groupmod`命令管理组群,以及使用`gpasswd`命令为组群添加用户。此外,还介绍了`su`和`sudo`命令的使用方法,帮助用户在不同身份之间切换。
208 4
|
12月前
|
开发框架 人工智能 安全
Promptic:轻量级 LLM 应用开发框架,提供完善的底层功能,使开发者更专注于构建上层功能
Promptic 是一个轻量级的 LLM 应用开发框架,支持通过一行代码切换不同的 LLM 服务提供商。它提供了类型安全的输出、流式支持、内置对话记忆、错误处理和重试等功能,帮助开发者专注于构建功能,而不是底层的复杂性。
266 6
Promptic:轻量级 LLM 应用开发框架,提供完善的底层功能,使开发者更专注于构建上层功能
|
存储 Web App开发 移动开发
js【详解】本地存储 Cookie、sessionStorage、localStorage
js【详解】本地存储 Cookie、sessionStorage、localStorage
565 0
|
JavaScript Java 测试技术
基于微信小程序的小说阅读系统+vue.js附带文章和源代码设计说明文档ppt
基于微信小程序的小说阅读系统+vue.js附带文章和源代码设计说明文档ppt
385 1
|
存储 运维 资源调度
云资源的使用规范是什么?
【5月更文挑战第9天】云资源的使用规范是什么?
476 1
|
安全 算法 编译器
【C++ 基础知识】进一步了解 C++ 中 操纵符std::endl 的原理
【C++ 基础知识】进一步了解 C++ 中 操纵符std::endl 的原理
768 0
|
缓存 NoSQL Java
springboot中集成redis,二次封装成工具类
springboot中集成redis,二次封装成工具类