热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

设计模式系列·工厂方法模式之CodeReview

前言以小说的笔法写的设计模式系列文章,你绝对看得懂![首发于公众号:聊聊代码]设计模式系列王小二需求历险记(一)设计模式系列王小二需求历险记(二)设

前言

以小说的笔法写的设计模式系列文章,你绝对看得懂![首发于公众号:"聊聊代码"]

设计模式系列·王小二需求历险记(一)
设计模式系列·王小二需求历险记(二)
设计模式系列·封装、继承、多态
设计模式系列·初探设计模式之王小二的疑问
设计模式系列·Facade模式之MVC的烦恼
设计模式系列·Adapter 模式之如何优雅的使用别人的轮子
设计模式系列·类爆炸之Bridge模式
设计模式系列·工厂方法模式之 Code Review
设计模式系列·抽象工厂模式

------华丽的分割线------

code review 的开始

小二所在的公司最近出了很多线上bug,痛定思痛,于是老大们纷纷决定落实code review机制...
很走运,C哥负责review小二消息中心的代码

code review

好一段switch...case...

"小二,我们开始吧,让我看看前几天你写的代码"。C哥微笑道。
"好的,C哥!"

小二熟练的打开电脑,找到消息中心的代码。

"C哥,这是你之前告诉我用的桥接模式写的!"
"嗯,写的不错,这样抽象与实现就能各自独立的变化了。"

"等等,小二,让我看看你这段代码是怎么回事?"
"稍等,C哥,我放大一些。"

小二将鼠标聚焦在这段代码上...

switch ($mes_type){case 'Sms':$obj = new SmsMessage();break;case 'Email':$obj = new EmailMessage();break;default:throw new Exception('NO Message Type Found');
}
$obj->send();

"小二,你讲讲这段代码的逻辑。"
"好的,C哥,这段代码是调用端的代码,根据不同的消息类型($mes_type),实例化不同的消息类。比如消息类型传入Sms的时候,这时候就会实例化SmsMessage类。"

"哦,我知道了。好一段 switch...case...,但这段代码有问题!"
"嗯?什么问题啊?"小二好奇的问道

"比如我现在新增一种消息类 AppPushMessage,你想想,你调用端的代码是不是要改啊?"
"的确是,switch...case...这块要变动。"

"对嘛,我只是新增了消息类型,不应该对调用端的代码产生影响!"
"是,C哥。有没有什么办法可以解决这个问题呢?"

C哥微微笑道:"办法是有的,还不止一种呢!"
小二双手抱拳:"请C哥不吝赐教!"

问题的本质

"小二,你觉得刚才问题的本质是什么?"
"嗯...想不出来..."

"好吧,不难为你了。问题的本质是:调用端负责了太多的事情!"
"哦哦,违背了'单一职责'原则?"

"是啊,调用端只是负责实质的调用,发送出实质的消息而已。这才是他的职责。"
"对!我明白了,我代码里调用端既负责调用相关的消息类完成发送消息,又负责根据不同的参数实例化不同的消息类。他的责任到底是负责调用发送呢?还是负责实例化不同的消息类呢?责任不明确,所以会产生耦合性的问题!"

"嗯嗯,小二悟性长进不少啊!"
"哈哈,多蒙C哥指教!"

简单工厂模式

"C哥,我们知道了问题的本质。怎么解决呢?"
"好,下面我们就用简单工厂模式来解决。"

"简单工厂模式?我好像听说过。"
"简单工厂,用的人挺多的,但不属于23种GOF设计模式之一。"

"哦哦,这样啊。"
"你看,上面的代码利用简单工厂可以改写一下。"

/****************File:MessageFactory.php*******************/

class MessageFactory{public static function get_instance($mes_type){switch ($mes_type){case 'Sms':$obj = new SmsMessage();break;case 'Email':$obj = new EmailMessage();break;default:throw new Exception('NO Message Type Found');}return $obj;}
}

/********************File:Client.php**********************/
class Client{public function main(){$obj=MessageFactory::get_instance($mes_type);$obj->send();}
}

"你看看,调用端只负责调用消息类进行发送。而具体实例化哪个消息类,这就不是调用端关心的了。"
"对,是啊!调用端只需要向简单工厂发送请求,简单工厂就返回相应实例化好的对象。"

"这样,调用端负责实际的消息发送,简单工厂负责制造(实例化)相应的消息对象。他们的职责分明!"
"对,像您刚才说的,我再增加一种消息AppPush,我也不用改调用端的代码了!"

工厂方法模式

"小二,还记不记得我刚才说的,解决上面问题的办法不止一种。"
"嗯嗯,记得记得!还有什么好办法吗?"

"有的。上面的简单工厂,你觉得有什么缺点吗?"
"嗯...找不出来。要实在找缺点的话,还真有一个。比如我刚才新增了AppPush消息类型,就要修改上面的MessageFactory工厂类。"

"是,这样就违背了 开放-封闭 原则。我们应该对扩展开发,而对修改关闭。因为修改,可能会带来意想不到的bug。"
"对,确实是。但简单工厂确实解决了单一职责的问题,也不失为一种好的模式。那怎么才能既解决单一职责的问题,又不违背开闭原则呢?"

"有一种设计模式:工厂方法模式。可以解决你说的问题。"
"太好了!C哥你能简单介绍一下吗?"

"首先看一下工厂方法模式的类图吧!"
"好的,C哥!"

"看这个类图,你能明白工厂方法模式大致的意图吗?"
"我看看。C哥,工厂方法模式,是不是将对象的创建,延迟到了子类中去执行?也就是每个子类工厂去负责创建相关的对象?"

"对,这样的话,我就不用在工厂类中写一大堆 switch...case... 了。当出现一种新消息类的时候,我只需要扩展出一个相应的工厂类来就行了。"
"嗯嗯,明白了,这就符合开闭原则了!"

"但是,C哥,我还有一个疑问。虽然工厂方法模式符合了开闭原则,但是,我要在调用端决定使用哪个工厂啊?"
"对,这的确是个问题。但是我们有很多种解决的办法:你可以写一个配置文件,每次去读这个配置文件来决定使用哪个工厂。"

"写一个配置文件,可以是可以,但总觉得不优雅。"
"哈哈,有没有听说过反射?反射也可以解决这个问题。"

"哦哦,这样啊!厉害!"
"小二,用工厂方法模式,你画一下上面代码的UML类图。"

不一会,小二就画出了工厂方法模式的类图。

"不错嘛,小二,我这里先用反射,给你看看代码的实现。具体反射的机制、原理,你自己去查一下吧!"
"好的,C哥!"

/*************抽象类:MessageFactory.php*******************/
abstract class MessageFactory{abstract public function get_instance();
}/*************EmailFactory.php*******************/
php

class EmailFactory extends MessageFactory {public function get_instance(){return new EmailMessage();}
}/*************SmsFactory.php*******************/
class SmsFactory extends MessageFactory {public function get_instance(){return new SmsMessage();}
}/*************调用端:Client.php***************/
class Client{public function main($mes_type){//利用反射,消除调用端的逻辑判断$reflection=new ReflectionClass($mes_type.'Factory');$factory=$reflection->newInstance();$mes_obj=$factory->get_instance();$mes_obj->send();}
}

"哇塞!C哥太棒了。这解决办法非常好!"
"不能说非常好,但解决了问题。"

review结束

不知不觉中,2个小时过去了,code review也接近尾声了。
小二望向窗外,看着天边的云彩慢悠悠的飘着,想想自己刚学到的设计模式,嘴角不自觉的露出了微笑,coder的快乐,或许就是这么简单...

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

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




推荐阅读
  • 本文介绍了深入浅出Linux设备驱动编程的重要性,以及两种加载和删除Linux内核模块的方法。通过一个内核模块的例子,展示了模块的编译和加载过程,并讨论了模块对内核大小的控制。深入理解Linux设备驱动编程对于开发者来说非常重要。 ... [详细]
  • 使用Ubuntu中的Python获取浏览器历史记录原文: ... [详细]
  • 如何实现织梦DedeCms全站伪静态
    本文介绍了如何通过修改织梦DedeCms源代码来实现全站伪静态,以提高管理和SEO效果。全站伪静态可以避免重复URL的问题,同时通过使用mod_rewrite伪静态模块和.htaccess正则表达式,可以更好地适应搜索引擎的需求。文章还提到了一些相关的技术和工具,如Ubuntu、qt编程、tomcat端口、爬虫、php request根目录等。 ... [详细]
  • GetWindowLong函数
    今天在看一个代码里头写了GetWindowLong(hwnd,0),我当时就有点费解,靠,上网搜索函数原型说明,死活找不到第 ... [详细]
  • Spring源码解密之默认标签的解析方式分析
    本文分析了Spring源码解密中默认标签的解析方式。通过对命名空间的判断,区分默认命名空间和自定义命名空间,并采用不同的解析方式。其中,bean标签的解析最为复杂和重要。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文介绍了Redis的基础数据结构string的应用场景,并以面试的形式进行问答讲解,帮助读者更好地理解和应用Redis。同时,描述了一位面试者的心理状态和面试官的行为。 ... [详细]
  • eclipse学习(第三章:ssh中的Hibernate)——11.Hibernate的缓存(2级缓存,get和load)
    本文介绍了eclipse学习中的第三章内容,主要讲解了ssh中的Hibernate的缓存,包括2级缓存和get方法、load方法的区别。文章还涉及了项目实践和相关知识点的讲解。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • Voicewo在线语音识别转换jQuery插件的特点和示例
    本文介绍了一款名为Voicewo的在线语音识别转换jQuery插件,该插件具有快速、架构、风格、扩展和兼容等特点,适合在互联网应用中使用。同时还提供了一个快速示例供开发人员参考。 ... [详细]
  • 本文介绍了在Mac上搭建php环境后无法使用localhost连接mysql的问题,并通过将localhost替换为127.0.0.1或本机IP解决了该问题。文章解释了localhost和127.0.0.1的区别,指出了使用socket方式连接导致连接失败的原因。此外,还提供了相关链接供读者深入了解。 ... [详细]
  • 本文介绍了在Windows环境下如何配置php+apache环境,包括下载php7和apache2.4、安装vc2015运行时环境、启动php7和apache2.4等步骤。希望对需要搭建php7环境的读者有一定的参考价值。摘要长度为169字。 ... [详细]
  • Java在运行已编译完成的类时,是通过java虚拟机来装载和执行的,java虚拟机通过操作系统命令JAVA_HOMEbinjava–option来启 ... [详细]
  • 本文主要复习了数据库的一些知识点,包括环境变量设置、表之间的引用关系等。同时介绍了一些常用的数据库命令及其使用方法,如创建数据库、查看已存在的数据库、切换数据库、创建表等操作。通过本文的学习,可以加深对数据库的理解和应用能力。 ... [详细]
  • 从批量eml文件中提取附件的Python代码实现方法
    本文介绍了使用Python代码从批量eml文件中提取附件的实现方法,包括获取eml附件信息、递归文件夹下所有文件、创建目的文件夹等步骤。通过该方法可以方便地提取eml文件中的附件,并保存到指定的文件夹中。 ... [详细]
author-avatar
火俊逸香嘉孝
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有