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

字数 1925阅读 318

工厂模式属于创建类模式,一般情况下根据使用场景和实现方式分为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种:简单工厂模式、静态工厂模式、工厂方法模式、抽象工厂模式。其中简单工厂模式、静态工厂模式的区别只是在于把创建方法实现成什么方式,工厂方法模式是把创建延迟到子类进行,抽象工厂模式就是一系列工厂方法的组合。
它们分别解决不同的问题,简单工厂和静态工厂主要解决的是类创建不统一,重复代码,工厂方法主要解决的是对不同类别可以创建不同的同类实例,抽象工厂主要解决的问题是对一系列工厂方法的封装,流程上的规范扩展。

😊查看更多😊

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

推荐阅读更多精彩内容