# 从定制机器人女友学设计模式 -- 工厂模式

367 阅读6分钟

为什么直接new一个对象不好?(答案在文末)

背景交代

我们是一个机器人女友的生产商,我们生产的女友都会洗衣,打扫,聊天。以上这些都是出厂配置,我们给您留了一个可以定制的功能,就是做饭。您下单是只要告诉我们你喜欢会做什么饭的女友,我们为您定制完成后邮寄到您家。

起步阶段

// 伪代码如下,实操不要有中文哦,不然可能会被打哦
public class GirlRobotStore{

    GirlRobot orderGirlRobot(String type) {
        GirlRobot girlRobot;
        if (type.equal("糕点")) {
            girlRobot = new 糕点师();
        } else if (type.equal("汤")) {
            girlRobot = new 熬汤小能手();
        } else if (type.equal("菜")) {
            girlRobot = new 炒菜小能手();
        } else if (type.equal("肉")) {
            girlRobot = new 肉类终结者();
        }
        
        洗衣();
        打扫();
        聊天();
    }
    
    // 其他运输和售后的方法
    // ......
}

增加需要

这几天我们收到用户的反馈,说我们的产品做出饭菜不符合他们的口味,这可难不倒我们,我们抓紧收集他们不同口味的菜品信息,加入我们的 if..else..大军。

// 伪代码如下,实操不要有中文哦,不然可能会被扔臭鸡蛋哦
public class GirlRobotStore{

    GirlRobot orderGirlRobot(String type) {
        GirlRobot girlRobot;
        if (type.equal("南方糕点")) {
            girlRobot = new 南方糕点师();
        } else if (type.equal("北方糕点")) {
            girlRobot = new 北方糕点师();
        } else if (type.equal("老火汤")) {
            girlRobot = new 老火汤小能手();
        } else if (type.equal("清淡汤")) {
            girlRobot = new 清淡汤小能手();
        } else if (type.equal("蒸菜")) {
            girlRobot = new 蒸菜小能手();
        } else if (type.equal("辣菜")) {
            girlRobot = new 炒辣菜小能手();
        } else if (type.equal("肉")) {
            girlRobot = new 肉类终结者();
        }
        //...
        
        洗衣();
        打扫();
        聊天();
    }
    
    // 其他运输和售后的方法
    // ......
}

过了几天,又有用户说,太辣了,太淡了,肉太大块了......就这样一共加了几十种新需求。我们门店也心累,样样都要操心。。。。。。。

优化设计 -- 简单工厂

后来有人向总部提了建议,说可以把做饭这一部分抽个出来给个工厂去负责,这样子有新的需要就直接跟工厂联系,他们去拓展,我们门店从这个工厂获得会做饭的机器最终成品然后加上洗衣打扫聊天模块即可。这个工厂就叫 SimpleFactory。

// 伪代码如下,实操不要有中文哦,不然可能会被删代码哦
public class GirlRobotStore{

   GirlRobot orderGirlRobot(String type) {
        GirlRobot girlRobot = SimpleFactory.会做饭(type);
        
        洗衣();
        打扫();
        聊天();
   }
    
    // 其他运输和售后的方法
    // ......
}
// 伪代码如下,实操不要有中文哦,不然可能会被埋坑哦
public class SimpleFactory {
    GirlRobot 会做饭(String type)  {
        if (type.equal("南方糕点")) {
            girlRobot = new 南方糕点师();
        } else if (type.equal("北方糕点")) {
            girlRobot = new 北方糕点师();
        } else if (type.equal("老火汤")) {
            girlRobot = new 老火汤小能手();
        } else if (type.equal("清淡汤")) {
            girlRobot = new 清淡汤小能手();
        } else if (type.equal("蒸菜")) {
            girlRobot = new 蒸菜小能手();
        } else if (type.equal("辣菜")) {
            girlRobot = new 炒辣菜小能手();
        } else if (type.equal("肉")) {
            girlRobot = new 肉类终结者();
        }
    }
}

有人提出反对意见,说这是换汤不换药,增加了制作成本不说,实际上也没有减少工序,该加功能的时候还不是要加,只是从门店负责改成工厂负责而已。 领导说了,我们虽然现在只有一家门店,但是我们之后会开分店,独立出这个工厂出来,以后多个门店都可以直接在这个工厂获得半成品,缩小了开门店的成本。

果然还是领导有大局观。

需求升级 -- 工厂方法

随着用户越来越多,大家提出的要求就更多。简单的辣和清淡的分级已经无法满足顾客的需求。有的顾客要做广东特色菜,有的顾客要做东北菜,有的要重启口味的...... 为了满足需求,我们淘汰了简单工厂,用各个地区的分店来代替。我们店由于开的最早,所以就定为总店。我们控制最终的产品效果和质量,而做饭的口味和特色就由分散到各地的门店负责。UML图大概是这样的:

总店的需要负责的内容就变成了这样:

// 伪代码如下,实操不要有中文哦,不然可能会天天有bug哦
public abstract class GirlRobotStore{

   GirlRobot orderGirlRobot(String type) {
        GirlRobot girlRobot = 会做饭(type);
        
        洗衣();
        打扫();
        聊天();
   }
   
   public abstract GirlRobot 会做饭(String type);
    
    // 其他运输和售后的方法
    // ......
}
// 伪代码如下,实操不要有中文哦,不然可能会天天加班哦
public class 广州分店{
    GirlRobot 会做饭(String type)  {
        if (type.equal("南方糕点")) {
            girlRobot = new 早茶茶点师();
        } else if (type.equal("北方糕点")) {
            girlRobot = new 北方糕点师();
        } else if (type.equal("老火汤")) {
            girlRobot = new 老火靓汤小能手();
        } else if (type.equal("清淡汤")) {
            girlRobot = new 清淡汤小能手();
        } else if (type.equal("蒸菜")) {
            girlRobot = new 蒸菜小能手();
        } else if (type.equal("辣菜")) {
            girlRobot = new 辣只是为了好看的炒菜小能手();
        } else if (type.equal("叉烧")) {
            girlRobot = new 叉烧制作者();
        }
    }
}

现在这样可就太好了,以前我们店需要知道做饭的具体内容,依赖很多具体的做饭方式,门店越来越臃肿,现在我们只要知道我们我们的分店会继承我们的规范,实现自己的特色,我们总会得到一个会做饭的机器人,具体怎么做交给分店,这样子可省心了不少。他们管这叫依赖倒置。就是指我们这些高层的组件不用依赖会做什么饭的低层组件,而是依赖会做饭这样一个接口和得到一个会做饭的机器人。

事无巨细 -- 抽象工厂

就这样,我们的规模又翻了一番,居然有人开始挑剔我们的配料问题(这些顾客真是让人不省心)。原因是因为我们的分店有时候由于采购问题,大葱买不到只能用小葱代替,没有老姜只能用生姜,顾客又没法接受辣椒不是小米辣......无奈之下,我们只能抽调一个部门来负责配料的配给了。这是我们类图:

  • 配料总厂是一个接口类型,只定义规范,没有具体的实现。
  • 各个菜系的配料厂固定供应对应的配料,严格把关,防止配料交给多个门店时出现配料不齐或不符合标准的问题。
  • 例如东北菜系供应大葱生姜;江南菜系供应小葱老姜;闽南菜系供应大头葱老姜;湘菜系用指天椒等等。

总结

  • 工厂方法模式 定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。
  • 抽象工厂模式 提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。
  • 依赖倒置 依赖抽象,而不是依赖具体类。

彩蛋 -- 为什么不能直接new一个对象

  1. 造成高度耦合,不方便拓展,不符合对修改关闭对拓展开发的原则
  2. 不符合依赖倒置的原则
  3. 不是不能new一个对象,毕竟不new就无法获得实例。使用模式不是为了不使用new的方式获得对象,而是为了在一个统一的地方获取对象,方便“圈”起来统一管理,工厂模式就是这样的一种模式。

最后欢迎大家来我们店定够一个机器人女友呀 (·-·)