1. 重构实例-消息发送-原始代码及准备

由于这是一个长文,分成了好几章来介绍如何重构。

  1. 原始代码及准备
  2. 职责独立
  3. 去除static关键字
  4. 接口添加转换方法
  5. 方法合并
  6. 使用策略进行重构-Map方式
  7. 使用策略进行重构-枚举方式

1.1 原始代码

这是来自于我所经历的项目中一个消息发送的程序,调用者告诉发送程序:类型、接收者及消息的内容,发送程序会根据发送的类型调用不同的发送程序,将待发送的消息发送出去。 目前已知的发送渠道包括:短信、email、微信、钉钉、企业微信, 由于短信、email、微信、钉钉、企业微信这些发送渠道的调用方式非本程序所关心的重点,帮将对这些具体的发送程序进行简化,使用system.out替代。

先还是来看下类uml类图

MessageSender(消息发送)

MessageSender是消息发送类的处理逻辑,按消息的类型调用各种消息的发送方法。

public class MessageSender {public void sendMessage(String msgType, MessageData notify) {MessageTypeEnum typeEnum = MessageTypeEnum.getEnumType(msgType);switch (typeEnum) {// 短信发送case SMS:NotifySendUtils.sendSms(notify.getFunCode(), notify.getNotifyNumber(), notify.getContent());break;// email的发送case EMAIL:NotifySendUtils.sendMail(notify.getFunCode(), notify.getNotifyNumber(), notify.getTitle(), notify.getContent());break;// 微信的发送case WECHAT:NotifySendUtils.sendWxMsg(notify.getFunCode(), notify.getNotifyNumber(), notify.getContent());break;// 钉钉的消息发送case DINGDING:NotifySendUtils.sendDingDingMsg(notify.getFunCode(), notify.getNotifyNumber(), notify.getContent());break;// 企业钉钉的消息发送case WORKWECHAT:NotifySendUtils.sendWorkWebchatMsg(notify.getFunCode(), notify.getNotifyNumber(), notify.getContent());break;// 其他不做处理case OTHER:break;default:break;}}
}

NotifySendUtils(各种消息的发送操作)

public class NotifySendUtils {/*** 给一个邮箱,发送邮件** @param funCode 功能类型编码* @param sendToMail 地址* @param subject 主题* @param content 内容* @return 返回发送结果*/public static boolean sendMail(String funCode, String sendToMail, String subject, String content) {System.out.println("邮件的发送开始.....");System.out.println("邮件发送: " + ",sendToMail:" + sendToMail + ",subject:" + subject + ",content:" + content);System.out.println("短信的发送结束.....");return true;}/*** 给单个手机号,发送短信** @param funCode 功能类型编码* @param sendToMobileNos 接收的号码* @param smsContent 短信内容*/public static void sendSms(String funCode, String sendToMobileNos, String smsContent) {System.out.println("短信的发送开始.....");System.out.println("短信发送: " + ",sendToMobileNos:" + sendToMobileNos + ",smsContent:" + smsContent);System.out.println("短信的发送结束.....");}/*** 给单个微信openID,发送模板消息(类似银行交易提醒通知)** @param funCode 功能类型编码* @param toUserOpenId 发送的目标用户(微信openId)* @param context 内容*/public static void sendWxMsg(String funCode, String toUserOpenId, String context) {System.out.println("微信消息发送开始.....");System.out.println("微信消息:funCode" + funCode + ",toUserOpenId:" + toUserOpenId + ",context:" + context);System.out.println("微信消息发送结束.....");}/*** 发送钉钉消息** @param funCode 功能类型编码* @param toUserOpenId 接收的id* @param context 发送的内容*/public static void sendDingDingMsg(String funCode, String toUserOpenId, String context) {System.out.println("钉钉消息发送开始.....");System.out.println("钉钉消息:funCode" + funCode + ",toUserOpenId:" + toUserOpenId + ",tplData:" + context);System.out.println("钉钉消息发送结束.....");}/*** 企业微信消息的发送** @param funCode 功能类型编码* @param toUserOpenId 接收消息的id* @param context 发送的内容*/public static void sendWorkWebchatMsg(String funCode, String toUserOpenId, String context) {System.out.println("企业微信消息发送开始.....");System.out.println("企业微信消息:funCode" + funCode + ",toUserOpenId:" + toUserOpenId + ",tplData:" + context);System.out.println("企业微信消息发送结束.....");}
}

MessageData(发送消息的内容信息)

MessageData表示一条要发送的消息的内容

public class MessageData {/** 功能类型编码 */private String funCode;/** 通知对象(邮箱,手机号,钉钉号) */private String notifyNumber;/** 主题 */private String title;/** 短信内容 */private String content;/** 发送状态(1:未发送 | 2:发送完成 | 3:发送失败) */private Integer status;public String getNotifyNumber() {return notifyNumber;}public void setNotifyNumber(String notifyNumber) {this.notifyNumber = notifyNumber;}public String getTitle() {return title;}public void setTitle(String title) {this.title = title;}public String getContent() {return content;}public void setContent(String content) {this.content = content;}public Integer getStatus() {return status;}public void setStatus(Integer status) {this.status = status;}public String getFunCode() {return funCode;}public void setFunCode(String funCode) {this.funCode = funCode;}@Overridepublic String toString() {final StringBuilder sb = new StringBuilder("MessageData{");sb.append("funCode='").append(funCode).append('\'');sb.append(", notifyNumber='").append(notifyNumber).append('\'');sb.append(", title='").append(title).append('\'');sb.append(", content='").append(content).append('\'');sb.append(", status=").append(status);sb.append('}');return sb.toString();}
}

MessageTypeEnum 消息类型信息

MessageTypeEnum表示消息的类型信息

public enum MessageTypeEnum {/** 短信方式 */SMS("sms"),/** 邮件发送 */EMAIL("email"),/** 微信 */WECHAT("wechat"),/** 钉钉 */DINGDING("dingding"),/** 企业微信 */WORKWECHAT("workwechat"),/** 其他方式 */OTHER("other");/** 消息类型 */private String msgType;MessageTypeEnum(String msgType) {this.msgType = msgType;}/*** 获取消息的枚举类型** @param type 字符串表示* @return 枚举的类型*/public static MessageTypeEnum getEnumType(String type) {for (MessageTypeEnum typeEnum : values()) {if (typeEnum.getMsgType().equals(type)) {return typeEnum;}}return MessageTypeEnum.OTHER;}public String getMsgType() {return msgType;}@Overridepublic String toString() {final StringBuilder sb = new StringBuilder("MessageTypeEnum{");sb.append("msgType='").append(msgType).append('\'');sb.append('}');return sb.toString();}
}

调用序列图

对此程序的评价

我相信这样的代码在项目的开发中屡见不鲜,可能会说我这代码写得不好,不够面向 对象,判断逻辑都写死在了判断中,职责分离做的不好。
单就这样一个小程序而言这是可以接受的,但如果这是在一个复杂的应用系统中的一部分呢?那还能接受吗?单就这个程序而言,本身是可以运行的。是的没有错,其实只要是个学编程的,能写机器可以运行的代码这不是一件很难的事情,机器是死的,它不关注设计的够不够好,只要能够运行即可,但现实情况真的是这样吗?只需要能够机器运行即可吗?复杂的系统通常需要一个团队来维护,而这个时候,还只是机器能运行就可以了吗?当然不是,这个时候的
重点就从机器转移到人,需要人来维护,需要人来加入新的功能。有的时候可能这个人写的代码需要另外的人来继续扩展功能,人是关注代码的可读性的,是否能够容易修改的。

1.2 准备工作-单元测试

这是一个必要的工作。在没有单元测试的情况下,请不要重构。

如果没有单元测试,必须先写单元测试。并且必须将相关的功能都测试到,否则无法保证重构后的代码质量。

这里存在两层的单元测试,底层发送消息的测试,上层发送逻辑的测试。
第一层可以称物理层的测试。主要做用就是测试与真实的物理消息发送的交互。

首先进行消息通道相关的发送测试

public class TestNotifySendUtils {@Test@DisplayName("测试发送短信通知")public void testSmsSender() {NotifySendUtils.sendSms("datatest", "13412345678", "测试短信息的内容信息");// 无异常即为成功assertTrue(true);}@Test@DisplayName("邮件发送邮件通知")public void testEmailSender() {NotifySendUtils.sendMail("dataTest", "134@163.com", "测试邮件通知", "测试邮件的内容信息");// 无异常即为成功assertTrue(true);}@Test@DisplayName("微信发送通知")public void testWechatSender() {NotifySendUtils.sendWxMsg("dataTest", "131234567", "微信消息内容");// 无异常即为成功assertTrue(true);}@Test@DisplayName("钉钉发送通知")public void testDingDingSender() {NotifySendUtils.sendDingDingMsg("dataTest", "134123456", "钉钉发送通知");// 无异常即为成功assertTrue(true);}@Test@DisplayName("微信企业发送通知")public void testWorkWechatSender() {NotifySendUtils.sendWorkWebchatMsg("dataTest", "1231231", "企业钉钉通知");// 无异常即为成功assertTrue(true);}
}
第二层发送逻辑的测试
在这里已经屏蔽了物理层,只做发送的逻辑测试,
此处使用jmockit
public class TestMockMessageSender {private MessageSender instance = new MessageSender();@Test@DisplayName("测试发送短信mock")public void testSmsSenderMock() {new Expectations(NotifySendUtils.class) {{// 将方法给mock掉,无需与真实的操作进行交互NotifySendUtils.sendSms(anyString, anyString, anyString);}};MessageData data = new MessageData();data.setFunCode("dataTest");data.setNotifyNumber("13412345678");data.setTitle("测试短信息通知");data.setStatus(0);data.setContent("测试短信息的内容信息..");instance.sendMessage(MessageTypeEnum.SMS.getMsgType(), data);new Verifications() {{NotifySendUtils.sendSms(anyString, anyString, anyString);minTimes = 1;}};}@Test@DisplayName("邮件发送邮件通知Mock")public void testEmailSender() {new Expectations(NotifySendUtils.class) {{// 将方法给mock掉,无需与真实的操作进行交互NotifySendUtils.sendMail(anyString, anyString, anyString, anyString);}};MessageData data = new MessageData();data.setFunCode("dataTest");data.setNotifyNumber("13412345678@163.com");data.setTitle("测试邮件息通知");data.setStatus(0);data.setContent("测试邮件的内容信息..");instance.sendMessage(MessageTypeEnum.EMAIL.getMsgType(), data);// 无异常即为成功assertTrue(true);new Verifications() {{// 验证邮件是否被成功调用NotifySendUtils.sendMail(anyString, anyString, anyString, anyString);minTimes = 1;}};}@Test@DisplayName("微信发送通知mock")public void testWechatSender() {new Expectations(NotifySendUtils.class) {{// 将方法给mock掉,无需与真实的操作进行交互NotifySendUtils.sendWxMsg(anyString, anyString, anyString);}};MessageData data = new MessageData();data.setFunCode("dataTest");data.setNotifyNumber("1234567");data.setTitle("测试微信息通知");data.setStatus(0);data.setContent("测试微信的内容信息..");instance.sendMessage(MessageTypeEnum.WECHAT.getMsgType(), data);// 无异常即为成功assertTrue(true);new Verifications() {{// 验证邮件是否被成功调用NotifySendUtils.sendWxMsg(anyString, anyString, anyString);minTimes = 1;}};}@Test@DisplayName("钉钉发送通知mock")public void testDingDingSender() {new Expectations(NotifySendUtils.class) {{// 将方法给mock掉,无需与真实的操作进行交互NotifySendUtils.sendDingDingMsg(anyString, anyString, anyString);}};MessageData data = new MessageData();data.setFunCode("dataTest");data.setNotifyNumber("1234567");data.setTitle("测试钉钉通知");data.setStatus(0);data.setContent("测试钉钉的内容信息..");instance.sendMessage(MessageTypeEnum.DINGDING.getMsgType(), data);// 无异常即为成功assertTrue(true);new Verifications() {{// 验证邮件是否被成功调用NotifySendUtils.sendDingDingMsg(anyString, anyString, anyString);minTimes = 1;}};}@Test@DisplayName("微信企业发送通知mock")public void testWorkWechatSender() {new Expectations(NotifySendUtils.class) {{// 将方法给mock掉,无需与真实的操作进行交互NotifySendUtils.sendWorkWebchatMsg(anyString, anyString, anyString);}};MessageData data = new MessageData();data.setFunCode("dataTest");data.setNotifyNumber("1234567");data.setTitle("测试企业微信通知");data.setStatus(0);data.setContent("测试企业微信的内容信息..");instance.sendMessage(MessageTypeEnum.WORKWECHAT.getMsgType(), data);// 无异常即为成功assertTrue(true);new Verifications() {{// 验证邮件是否被成功调用NotifySendUtils.sendWorkWebchatMsg(anyString, anyString, anyString);minTimes = 1;}};}
}

重构实例-消息发送-原始代码及准备-1相关推荐

  1. springcloudstream+rabbitmq+eureka进行消息发送和接收实例代码

    文章目录 eureka作注册中心的配置: 消息提供方: 消费者代码 注册中心.消息接受者.消息提供者分别启动: eureka作注册中心的配置: 依赖包: <dependencies>< ...

  2. Unity内置的三套消息发送机制的应用实例

    转自http://blog.sina.com.cn/s/blog_1491e52310102wuf6.html 代码简介 : [1] 实例中包含2个类文件, SendMessage.cs 和 Rece ...

  3. RocketMQ一行代码造成消息发送失败

    这是我的第 198 期分享 作者 | 丁威 来源 | 中间件兴趣圈(ID:dingwpmz_zjj) 分享 | Java中文社群(ID:javacn666) 1.问题现象 首先接到项目反馈使用 Roc ...

  4. RocketMQ 一行代码造成大量消息发送失败

    作者 | 丁威 来源 | 中间件兴趣圈 问题现象 首先接到项目反馈使用 RocketMQ 会出现如下错误: 错误信息关键点:MQBrokerException:CODE:2DESC:[TIMEOUT_ ...

  5. VS.net 2005 MFC QQ 2006 TM 2006 消息发送 简单核心代码

    VS.net 2005 MFC QQ 2006 & TM 2006 消息发送 简单核心代码  1 void  SendQQMsgDlg::SendQQmsg()  2 {  3    CStr ...

  6. php+实现群发微信模板消息_使用php实现微信小程序发送模板消息(附代码)

    本篇文章给大家带来的内容是关于使用php实现微信小程序发送模板消息(附代码),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. 本章将会简单说一下微信小程序的模板消息发送,相对来说比较简 ...

  7. 企业微信机器人脚本python_python实现企业微信定时发送文本消息的示例代码

    企业微信定时发送文本消息 使用工具:企业微信机器人+python可执行文件+计算机管理中的任务计划程序 第一步:创建群机器人 选择群聊,单击鼠标右键,添加群机器人. 建立群机器人后,右键查看机器人,如 ...

  8. 微信公众 php代码,微信公众号开发之文本消息自动回复php代码_php实例

    本文实例为大家分享了php微信文本消息自动回复 别代码,供大家参考,具体内容如下 1.PHP示例代码下载 下载地址1:http://xiazai.php.net/201608/yuanma/phpwx ...

  9. 2022.8.31 进程中无名管道的特点,无名管道的创建,为何无名管道只能能够实现具有亲缘关系的进程间的通信,以及实现利用无名管道父进程给子进程发送消息的完整代码。

    无名管道通信 无名管道特点: (1):只能用于具有亲缘关系的进程之间的通信.(父子进程或兄弟进程) (2):是一个半双工的通信模式,具有固定的读端和写端.(fd[0]固定为读端,fd[1]固定为写端) ...

  10. 【微信小程序】客服系统,客服聊天发送商品详情,快捷发送链接和图文消息,附代码和流程

    客服聊天发送商品详情,快捷发送链接和图文消息,附代码和流程 遇到一个新需求,需要做一个客服聊天的功能能够发送链接和图文消息,先在小程序后台做一个配置,首先在后台添加客服 然后客服按钮编写,功能实现 小 ...

最新文章

  1. ndk 路径修改 超简单的方法
  2. oracle sql 调用自定义函数_PSCAD入门教程(第7节):怎样调用fortran编写的自定义函数?(附模型)...
  3. MicroStation VBA 操作提示
  4. 每周一算法之六——KMP字符串匹配算法
  5. Canvas入门06-线段与像素边界
  6. 因离职,3人拟终止人才项目!
  7. html 多页面合并,让多个HTML页面 使用 同一段HTML代码
  8. CCF201409-1 相邻数对(100分)【序列处理】
  9. Flutter 微信语音消息播放动画
  10. setTimeout运行机制
  11. 捋一捋字符串和字节序列的关系
  12. three.js 拉伸成型 ExtrudeGeometry
  13. IM3、IIP3、OIP3、G、P1dB指标之间的关系
  14. 开源视频播放器IjkPlayer使用记录之(二)--自定义的mediaController实现。
  15. 171023 逆向-BDCTF(Re)
  16. IST改进算法之Two-Step Iterative Shrinkage/Thresholding(TwIST)
  17. 蓝牙功率放大器系统性能
  18. java 计算包含中文字符串的真实长度
  19. 解决办法:Ubuntu 16.04 【缺少依赖】导致出现该错误——ERROR: the following packages/stacks could not have their
  20. 计算机已达到最大连接数

热门文章

  1. Android性能优化系列之App启动优化
  2. python的easyocr图像文字识别
  3. 移动平均线rolling()与加权移动平均线ewm()
  4. 2021齐齐哈尔实验中学高考成绩查询,齐齐哈尔名列前茅的4所高中,成绩一目了然,谁是市内最强中学?...
  5. 小白快速学习 Kotlin 语法基础
  6. 用matlab调节窗宽窗位的代码,基于HTML5的PACS HTML5图像处理(7)实现客户端JS调整窗宽窗位...
  7. 财务自由,整层楼沸腾!万亿蚂蚁IPO来了,诞生几千个亿万富翁?杭州、上海房价又要涨了…...
  8. 虚拟服务器 emule,web服务器配置emule功能详解
  9. 计算机显示时区怎么更改,电脑时区自动改怎么办
  10. 苹果计算机怎样恢复桌面,mac桌面整理_使Mac桌面恢复整洁的四种技巧