迷之微笑

经过 C 哥的精心指导,消息中心终于上线!代码运行了半个月,稳健无 bug 。

王小二托着下腮,看着代码,一抹迷之微笑随之闪现^_^。作为一名有追求的码农,此时的快乐或许只有自己能懂。

消息中心的重构

一天清晨,小二凝神聚力,手指在键盘间有节奏的敲击着,一行行代码跃然屏上。不知不觉,老大在小二背后站了半天了...

"小二,之前消息中心是你做的吧?"

"嗯嗯,是的。"

"好的,咱们现在正在搞服务拆分。而消息中心又是一个通用的服务,所以我想把消息中心拆出来,作为底层服务。"

"好啊,早应该这样了!"

"嗯,具体发送消息的逻辑,这块交给 java 组同学去写。你只需要按照约定的数据格式,将数据 push 到队列里去, java 那边去消费就可以了。"

"嗯...可以,队列用什么实现呢?"

"关于队列,这次需要你支持两种方式:一种是 redis 、一种是 mq"

"也就是说我既支持往 redis 队列里面 push 数据,也支持往 mq 里 push 数据?"

"是的,就是这样,这块你好好设计下吧!"

"好的,放心吧老大!"

设计类图

小二这两天正在研究设计模式,既然接到了重构的新需求,那就好好大展一番身手吧!

不一会,小二就理出了大体的思路:

发送消息,分为 3 步:

1 、不同的消息(短信、微信)组装各自的数据格式和内容;

2 、消息可以使用不同的方式(redis 、 mq)推送到队列里;

3 、使用一个 send()方法,先从步骤 1 获取数据,再利用步骤 2 的方法 push 到相应的队列里。

思路清楚了,小二马上画出了类图:

小二反复看了几遍自己设计的类图:

嗯,基本实现了需求。

1 、消息分为短信消息和微信消息(SmsMessage 和 WechatMessage)

2 、相同的消息既可以通过 redis 发送,又可以通过 Mq 发送。

没毛病, great!

类爆炸

和往常一样,比较大的设计,还是得请 C 哥把把关。

小二找到 C 哥,详细介绍了自己的需求和设计。

"嗯...小二啊,问题是解决了,但设计看起来有点问题啊!"

"啊?有问题?请 C 哥指教"

"这个会引起类爆炸!"

"啥?类还会爆炸?你别逗我了"

"哈哈,不信?来,我让你看看类怎么爆炸的。假设需求要你新增 Email 消息类型,你再设计下类图"

"好的, C 哥你等下,马上设计出来"

不一会,王小二就设计出了新的类图:

"小二,红色部分是你新增的 3 个类。"

"嗯嗯,是的!"

"好,在此基础上,你再增加 Mysql 队列的发送方式"

"好的!"

小二拿着新的类图找到了 C 哥:

“小二,刚才只是让你新增一种消息类型和发送方式,你看看一共增加了几个类?”

“ 1.2.3..6 ,一共新增了 6 个类!”

"好,你现在一共有 13 个类,假设再让你新增一种消息类型和发送方式,你又会新增多少个类?"

"嗯...会新增 8 个类,到时候就 13+8=21 个类了..."

“类太多了,爆炸了吧?哈哈,这就是类爆炸”

“确实是,类确实太多了!但是,怎么解决呢?...”

Bridge 模式登场

"小二啊,你还记不记得前面我给你讲的四人团的三条建议?"

“嗯,记得:

1 、针对接口编程;

2 、优先使用对象组合,而不是类继承;

3 、找到并封装变化的点。

“对,就是这 3 点。你看看,你的设计就违背了上面的原则。” C 哥说道。

"嗯?还真违反了???"王小二看了一会...

"哦...是的, C 哥,确实是。违反了第 2 点,你看我类图中使用的都是继承,这个继承间耦合性太高了,太庞大了!"

“是的,现在我们就用 Bridge 模式把他拆出来。”

"我先给你讲讲 Bridge 模式的基本定义吧!"

“好的, C 哥!”

Bridge 模式,也即桥梁模式,四人团的说法是:“将抽象部分与它的实现部分分离,使它们都可以独立地变化。”

“啊? C 哥,表示完全听不懂...”

"哈哈,正常,你一下能听懂才怪呢,这句话很容易使初学者产生误解,我们边实践,边解释这个定义。"

“小二,你刚才不是说四人团建议:‘找到并封装变化的点’吗?你现在在你的设计中找到这些变化的点,并封装起来。”

“好的,C 哥,我想想...”

小二想了一会:“变化的点有 2 个。一个是消息类型会变化,一个是发送方式会变化。”

想好后,小二马上画了出来。

"嗯,不错,小二你解释下吧"

小二解释道:"

变化的点有 2 个:一个是消息类型[Sms 、 Wechat...],一个是发送方式[redis 、 mq...]。

所以我把他们各自都封装了起来,成为 2 个独立的抽象类:Message 和 SendType 。

Message 类负责组装好自己消息类型的数据(combine_data()),并发送(send())出去。

SendType 类负责将数据 push(push_to_queue())进相应的队列。"

"不错嘛,小二,我在你类图的基础上扩展下,你就知道怎么解决类爆炸的问题了。"

"哇塞,好的, C 哥!"

不一会, C 哥就在小二的基础上,画出了完整的类图:

"看不太懂, C 哥你解释下吧!"

C 哥解释道:"

小二你看,消息有 2 种类型:短信和微信。

但不论是短信和微信,他们都应该知道自己的消息格式和内容。

并且,他们得把自己发送出去,也就是 push 到相应的队列里面去。

而如何 push 到队列里面去呢?这又有 2 种实现方式,一种是 redis 队列,一种是 mq 队列。

也就是,实现发送这个动作,得知道如何发送。

你看这里,我没有用你最初设计的类继承的方法:

这里的抽象部分:即是 Message 的抽象;

这里的实现部分:即是 SendType 的实现。

在抽象部分与实现部分之间搭个桥,使抽象部分可以引用实现部分的对象,就是桥接模式。

这样使用对象组合的方式,特别的灵活。"

"哇塞, C 哥,这个桥接威力好大啊!"

"是啊,桥接模式比较难,但也更有用。你看,这样不管你是增加一种新的消息类型还是一种新的发送方式,他们之间没有耦合,可以独立的变化。"

"是啊,这样类爆炸的问题也就没有了,冗余减少了,代码更好维护!"

"是这样的!"

代码实现

见证了 bridge 模式的威力之后,小二迫不及待的写出了相应的伪代码:

"C 哥,你帮我看下我写的代码思路对吗?"

"好的,我看看..."

//消息抽象类

abstract class Message{

//定义发送方式对象与消息数据

public $send_type_obj;

public $data;

//构造函数

public function __construct($send_type_obj,$data)

{

$this->send_type_obj=$send_type_obj;

$this->data=$data;

}

//抽象类:不同的消息来重写此方法,以得到不同的消息数据

abstract public function combine_data();

//桥接到外部对象(引用外部对象, push 到相应的队列)

public function push_to_queue($data){

if($this->send_type_obj instanceof SendType){

$this->send_type_obj->push_to_queue($data);

}

}

//完成发送

public function send(){

$combined_data=$this->combine_data();

$this->push_to_queue($combined_data);

}

}

//短信消息类

class SmsMessage extends Message {

//发送短信消息数据

public function combine_data(){

return 'sms combined data:'.$this->data;

}

}

//微信消息类

class WechatMessage extends Message {

//发送微信消息数据

public function combine_data(){

return 'wechat combined data:'.$this->data;

}

}

//发送方式抽象类

abstract class SendType{

abstract public function push_to_queue($data);

}

//Redis 发送方式类

class RedisSendType extends SendType {

//将消息 push 到 redis 队列里,完成发送

public function push_to_queue($data)

{

echo $data." has sent by redis queue\n";

}

}

//Mq 发送方式类

class MqSendType extends SendType {

//将消息 push 到 mq 队列里,完成发送

public function push_to_queue($data)

{

echo $data." has sent by mq queue\n";

}

}

/************Test Case*************/

//实例化不同的发送方式类

$redis_send_obj=new RedisSendType();

$mq_send_obj= new MqSendType();

//通过 redis 发送短信

$sms_redis_obj=new SmsMessage($redis_send_obj,'123');

$sms_redis_obj->send();

//通过 redis 发送微信

$wechat_redis_obj=new WechatMessage($redis_send_obj,'456');

$wechat_redis_obj->send();

//通过 mq 发送短信

$sms_mq_obj=new SmsMessage($mq_send_obj,'789');

$sms_mq_obj->send();

//通过 mq 发送微信

$wechat_mq_obj=new WechatMessage($mq_send_obj,'100');

$wechat_mq_obj->send();

"嗯,看起来没毛病,我看看你的运行结果。"

"好的, C 哥,这是运行结果"

"哈哈,确实没问题,不错嘛小二!"

"C 哥指点的好,谢谢 C 哥,又学习了一种强大的设计模式!"

结语

设计模式如此强大,从 bridge 就可见其不一般。

那到底什么是设计模式呢?有没有一个通俗的定义呢?

其实,通俗点说:

设计模式,是针对特定问题的,反复出现的解决方案,这种方案被抽象化、模板化。并且随着时间的流逝,被历史证明这是优秀的解决方案。

所以,跟着王小二一起好好的学习设计模式吧,相信你终将迈入"左手代码右手诗"的天地!^_^

转载声明:本文转载自「聊聊代码」,搜索「talkpoem」即可关注。

关注「聊聊代码」,让我们一起聊聊“左手代码右手诗”的事儿。

java 类爆炸_设计模式系列·类爆炸之 Bridge 模式相关推荐

  1. java enum 父类_枚举基类Enum详解

    本文主要是对枚举类型的基类Enum类做一个介绍: 首先,Enum类位于java.lang包下,根据类的介绍可以发现,Enum类是Java中所有枚举类的父类,将枚举作为一个set或者Map的keys来使 ...

  2. 软件工程类图_软件工程系列-UML3

    本系列文章为笔记,内容根据北京大学<软件工程>MOOC UML的模型表达工具 系统静态部分建模工具 UML为不同抽象层提供了6种可对系统静态部分建模的图形工具 类图 显示了类(及接口).类 ...

  3. java printerjob打印_利用PrinterJob类实现打印控制

    无论采用哪种语言来开发应用系统,打印功能总是少不了的.为了节省Java程序开发人员的工作量,在Java语言中专门提供了一个PrinterJob类来帮助开发人员实现打印控制的功能. 本文将给大家介绍一下 ...

  4. Java 大黑话讲解设计模式 -- UML类图

    前言 关于UML类图的这篇文章,我觉得把它放在设计模式专栏的篇首最为合适,因为在学习设计模式的时候,经常会遇到有关UML类图,没有去专门学过的童鞋肯定会感觉很复杂.学到后面,发现不掌握UML类图,对设 ...

  5. plsql执行command命令控制台出现乱码_设计模式系列 — 命令模式

    点赞再看,养成习惯,公众号搜一搜[一角钱技术]关注更多原创技术文章.本文 GitHub org_hejianhui/JavaStudy 已收录,有我的系列文章. 前言 23种设计模式速记 单例(sin ...

  6. 访问者模式的java语言_Java 设计模式系列(二三)访问者模式(Vistor)

    Java 设计模式系列(二三)访问者模式(Vistor) 访问者模式是对象的行为模式.访问者模式的目的是封装一些施加于某种数据结构元素之上的操作.一旦这些操作需要修改的话,接受这个操作的数据结构则可以 ...

  7. java实现备忘录模式_设计模式之第17章-备忘录模式(Java实现)

    设计模式之第17章-备忘录模式(Java实现) 好男人就是我,我就是曾小贤.最近陈赫和张子萱事件闹得那是一个沸沸扬扬.想想曾经每年都有爱情公寓陪伴的我现如今过年没有了爱情公寓总是感觉缺少点什么.不知道 ...

  8. 举例说明层次分析的三大原则_设计模式系列,六大设计原则

    设计模式和性能优化有没有关系?最近,我看到有人再讲性能优化的时候,讲到了"有些设计模式可以做到一定程度的性能优化". 我读书少,别骗我.我看过无数篇设计模式了,第一次听到有人说,设 ...

  9. Java进阶专题(八) 设计模式之适配器模式、装饰者模式、观察者模式

    本章节将介绍:三个设计模式,适配器模式.装饰者模式和观察者模式.通过学习适配器模式,可以优雅的解决代码功能的兼容问题.另外有重构需求的人群一定需要掌握装饰者模式.本章节参考资料书籍<Spring ...

最新文章

  1. P8 控制系统校正与综合-《Matlab/Simulink与控制系统仿真》程序指令总结
  2. 一起学nRF51xx 12 -  flash
  3. 计算机基础18春在线作业1,东师-计算机应用基础18春在线作业1-1.docx
  4. VTK:提取选择 OriginalId用法实战
  5. Apple Catching POJ - 2385(基础的动态规划算法)
  6. Xcode9的xib只支持iOS7.0及以上版本
  7. iofactory.php,CI框架下引入类库资源PHPPowerPoint报出“ Cannot redeclare class IOFactory”...
  8. CentOS7.2下python安装pip-8.0.2管理
  9. 第 7 章 Neutron - 077 - 配置 linux-bridge mechanism driver
  10. Dreamweaver网页设计与制作100例:用DIV+CSS技术设计的书法主题网站(web前端网页制作课作业)
  11. 采用H264编码视频的参数设定
  12. 2021电赛预测—手把手带你玩转DDS模块AD9833
  13. 产品UI原型设计软件:Balsamiq Mockups 3 for Mac
  14. 爬虫抓图全网最新方法,这一次终于是4k高清美图,只因为我不下载JPG图片!
  15. 计算机ps图片在哪里看,怎么看图片有没有PS 两种查看照片有没被PS过的方法-电脑教程...
  16. 查看服务器的ip地址
  17. 香港虚拟主机和美国虚拟主机的区别体现在哪些方面
  18. 街道大动土,断网一周,学习计划照旧
  19. 蓝桥杯JAVA答题技巧,第九届蓝桥杯大赛个人赛省赛(软件类)C/C++ 大学B组比赛心得(还在更新)...
  20. 计算机老师发展的现状,计算机专业教师队伍的现状分析.doc

热门文章

  1. 在JavaScript中判断键盘的大写锁定键(Caps Lock)是否打开(附扫描枪相关规则)
  2. 【宫水三叶的刷题日记】497. 非重叠矩形中的随机点(中等)
  3. 工程力学专业考研转计算机,2017考研比较好的专业:工程力学专业
  4. 在CSDN的文章中插入图片
  5. Rundeck基础:2:安装设定
  6. 彻底禁用DeliveryOptimization
  7. uni-app app分享到朋友和朋友圈
  8. 评审的艺术——谈谈现实中的代码评审
  9. web集成3D模型,可动态展示设备模型及控制,互动性全息投影
  10. 【数方大数据】装修行业怎么找客户?线上获客的渠道有哪些