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

前言

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

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

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

code review 的开始

小二所在的公司最近出了不少线上bug,痛定思痛,因而老大们纷纷决定落实code review机制...
很走运,C哥负责review小二消息中心的代码设计模式

code review

好一段switch...case...

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

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

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

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

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

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类。"cdn

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

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

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

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

问题的本质

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

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

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

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

简单工厂模式

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

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

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

/****************File:MessageFactory.php*******************/
<?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*******************/
<?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*******************/
<?php
class SmsFactory extends MessageFactory {
    public function get_instance() {
        return new SmsMessage();
    }
}

/*************调用端:Client.php***************/
<?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」便可关注。

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