只一篇就够了·设计模式(1) - 工厂模式

工厂模式属于创建类模式,一般情况下根据使用场景和实现方式分为4种:简单工厂模式(Simple Factory Pattern)、静态工厂模式(Static Factory Pattern)、工厂方法模式(Factory Method Pattern)、抽象工厂模式(Abstract Factory Pattern)。

在学习设计模式的时候建议不要去生搬硬套类图,而是去学习这种模式的思想以及关注它是怎么去解决这一类问题的,所以后面的东西都是以这两个目的去分享自己的心得。

下面的例子不恰当,能够明白工厂模式就行。朋友在成都开了一个茶坊,想做一个茶叶管理的系统。现在简单的分析一下需求,给这个系统添加一个简单的下单功能,顾客来买茶的时候能够通过客户端下单。

先做一个简单的版本,通过TeaStore根据不同类型的茶下单:

fun main(args: Array<String>)
{
    val redStore:TeaStore = TeaStore()
    redStore.order(TeaStore.TeaType.RED).getTea() 
    redStore.order(TeaStore.TeaType.GREEN).getTea()
}

新建一个TeaStore,然后顾客点了一杯红茶,一杯绿茶。从下面的代码可以看到,现在系统只有红茶和绿茶。

class TeaStore
{
   enum class TeaType 
   {
        RED,
        GREEN,
        BLACK,
        YELLOW,
    }
    /**
     * 下单
     */
    fun order(type: TeaType): Tea
    {
       val tea = when (type)
        {
            TeaType.RED   -> RedTea()
            TeaType.GREEN -> GreenTea()
        } 
        tea.addWater()
        tea.cook() 
        return tea
    }
}

现在有顾客希望能够在店子里面喝到黄茶,所以系统得支持黄茶类型,这个时候我们可以这样修改TeaStroe

 /**
  * 下单
  */
 fun order(type: TeaType): Tea
 {
   val tea = when (type)
    {
        TeaType.RED   -> RedTea()
        TeaType.GREEN -> GreenTea()
        TeaType.YELLOW -> YellowTea() // 新增一种
    } 
    tea.addWater()
    tea.cook() 
    return tea
}

现在我们下单的时候就可以添加黄茶类型了。现在的问题是每添加一种茶都得在这里修改switch里面的条件,显然不符合开闭原则

简单工厂模式

类图不是目的,仅仅帮助理解
[图片上传失败...(image-e21af9-1527174175649)]

简单工厂可以解决每次去修改直接消费类的代码。接下来用简单工厂模式实现一个版本,用工厂来生产Tea类的实例:

class SimpleFactory
{
    /**
     * 创建茶
     */
    fun createTea(type: TeaStore.TeaType): Tea
    {
        return when (type)
        {
            TeaStore.TeaType.RED   -> RedTea()
            TeaStore.TeaType.GREEN -> GreenTea()
            else                   -> NoneTea()
        }
    }
}

然后修改一下下单的代码使用工厂创建类:

/**
 * 下单
 */
fun order(type: TeaType): Tea
{
    val tea = SimpleFactory.createTea(type)
    tea.addWater()
    tea.cook() 
    return tea
}

现在用简单工厂代替swith创建的Tea类。咦,等等,现在我要添加一种茶的时候怎么办?打开工厂类SimpleFactory,然后继续添加swith分支。肯定会奇怪,这不是跟之前一样嘛,只是换了个地方修改,这里只有TeaStroe在生产消费Tea这个类的实例,假如有100个地方呢?

设计模式在做的只有一件事情:封装变化,把经常在变化的代码封装到一个黑盒子里面,使整体看起来是不变的。

简单工厂模式解决的问题就是封装创建类的变化,把Tea类别的修改控制在工厂类里面,任何使用工厂类产生Tea类实例的地方不需要再去修改。

最后说明一下简单工厂模式和静态工厂模式的区别,如果你把createTea()实现成静态方法就是静态工厂模式,如果实现成类方法就是简单工厂模式。

由于用了我们的系统,茶馆效率提高了赚了很多钱,现在在软件园开了一个分店,为了适应不同人群每个店子卖不同的茶,软件园卖茶添加了其它兴奋剂,代码红茶(喝了写代码没bug)和代码绿茶,之前的店子在东边直接叫东店好了,卖普通红茶和普通绿茶。之前的方式只能满足对茶的修改,对茶店的扩展就满足不到了。

工厂方法模式

工厂方法模式(英语:Factory method pattern),就像其他创建型模式一样,它也是处理在不指定对象具体类型的情况下创建对象的问题。工厂方法模式的实质是“定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类。工厂方法让类的实例化推迟到子类中进行。”

类图不是目的,仅仅帮助理解

[图片上传失败...(image-485df-1527174175650)]
从类图可以看到,我们把创建茶的方法放到了茶店,有一个抽象的茶店TeaStore,并且把创建Tea实例的方法放到了TeaStroe的子类。

abstract class TeaStore
{
    enum class TeaType
    {
        RED,
        GREEN,
        BLACK,
        YELLOW,
    }
    /**
     * 下单
     */
    fun order(type: TeaType): Tea
    {
        val tea: Tea = createTea(type)
        print("\n--------------------")
        tea.addWater()
        tea.cook()
        return tea
    }
        // 创建类的方法放到子类去决定
    abstract protected fun createTea(type: TeaType): Tea
}

基于抽象茶店实现两个不同地方的茶店:

class SoftTeaStore : TeaStore()
{
    override fun createTea(type: TeaType): Tea
    {
        return when (type)
        {
            TeaType.RED   -> CodeRedTea()
            TeaType.GREEN -> CodeGreenTea()
            else          -> NoneTea()
        }
    }
}
class EastTeaStore : TeaStore()
{
    override fun createTea(type: TeaType): Tea
    {
        return when (type)
        {
            TeaType.RED   -> RedTea()
            TeaType.BLACK -> BlackTea()
            TeaType.GREEN -> GreenTea()
            else          -> NoneTea()
        }
    }
}

可以看到软件园的茶店买的红茶是代码红茶,绿茶是代码绿茶,东店卖的是普通的茶。现在用这两个店分别点一杯茶:

fun main(args: Array<String>)
{
    val redStore:TeaStore = EastTeaStore()
    // 在软件园点了一杯红茶
    redStore.order(TeaStore.TeaType.RED).getTea()

    val greenStore: TeaStore = SoftTeaStore()
    // 在东店点了一杯绿茶
    greenStore.order(TeaStore.TeaType.GREEN).getTea()
}

在软件园的红茶,顾客拿到的添加了兴奋剂的红茶,在东店拿到的红茶是普通的绿茶。现在如果添加一种茶不管是在软件园还是东店跟之前一样只需要修改工厂方法,和之前不同的是现在不但可以根据地域不同扩展不同的茶还可以新增新的店子比如北店、黑店什么的,我们的客户端Main就是消费tea的地方不用修改。

抽象工厂模式

抽象工厂模式(英语:Abstract factory pattern),抽象工厂模式提供了一种方式,可以将一组具有同一主题的单独的工厂封装起来。

类图不是目的,仅仅帮助理解
[图片上传失败...(image-5a426f-1527174175650)]
现在假如我们需要给茶添加不同工艺,比如软件园的茶店就不能是普通的水 必须是矿泉水,温度也不能是80-100,只能是20度干泡。所以现在泡茶用的水等等需要分区域单独提供,这样的话我们的工厂就需要生产一系列的材料,和工厂方法模式一样,只是多了一些创建水的工厂方法:

class SoftTeaStore : TeaStore()
{
    // 创建茶
    override fun createTea(type: TeaType): Tea
    {
        return when (type)
        {
            TeaType.RED   -> CodeRedTea()
            TeaType.GREEN -> CodeGreenTea()
            else          -> NoneTea()
        }
    }
    // 创建泡茶的水
    override fun createWater(type: WaterType) : Water
    {
        return when (type)
        {
            WaterType.WHITE   -> WhiteWater()
            WaterType.NORMAL -> NormalWater()
            else          -> NoneWater()
        }
    }
}

这样的话就把一系列工艺限制在了一个工厂里面,就不会出现东店使用矿泉水的情况,还能规范流程和生产方试。同样的新增一种茶或者茶店,很方便的根据需求定制不同的茶店。

总结

总结一下,因为有时候类的创建和初始化是比较重的,如果在每个使用的地方去实例化就会造成很多重复代码,工厂模式可以统一创建类以及初始化。工厂模式分为4种:简单工厂模式、静态工厂模式、工厂方法模式、抽象工厂模式。其中简单工厂模式、静态工厂模式的区别只是在于把创建方法实现成什么方式,工厂方法模式是把创建延迟到子类进行,抽象工厂模式就是一系列工厂方法的组合。
它们分别解决不同的问题,简单工厂和静态工厂主要解决的是类创建不统一,重复代码,工厂方法主要解决的是对不同类别可以创建不同的同类实例,抽象工厂主要解决的问题是对一系列工厂方法的封装,流程上的规范扩展。

😊查看更多😊

不登高山,不知天之高也;不临深溪,不知地之厚也
感谢指点、交流、喜欢

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容

  • 设计模式汇总 一、基础知识 1. 设计模式概述 定义:设计模式(Design Pattern)是一套被反复使用、多...
    MinoyJet阅读 3,901评论 1 15
  • 简单工厂模式虽然简单,但存在一个很严重的问题。当系统中需要引入新产品时,由于静态工厂方法通过所传入参数的不同来创建...
    justCode_阅读 1,176评论 1 9
  • 一个UML类图 类之间的关系 类的继承结构表现在UML中为:泛化(generalize)与实现(realize) ...
    僚机KK阅读 638评论 0 0
  • 设计原则: 要依赖抽象,不要依赖具体类 目录 本文的结构如下: 什么是抽象工厂模式 为什么要用该模式 模式的结构 ...
    w1992wishes阅读 1,102评论 0 6
  • 今天学习下最常见的工厂模式,工厂模式细分下来有三大类: 他们的目标都是一样的:封装对象的创建。但是实现手段和使用场...
    西木柚子阅读 2,140评论 7 28