给人看的Kotlin设计模式——抽象工厂

给人看的Kotlin设计模式目录

概念

抽象工厂模式相对正式的定义:

The abstract factory pattern provides a way to encapsulate a group of individual factories that have a common theme without specifying their concrete classes. (Wikipedia)

这个定义看上去也不咋正式啊,的确,抽象工厂模式实际上只是工厂方法模式的扩展,将其归入工厂方法模式也并无不妥。但是,既然它被单独列出来了,那自然还是有它不同之处的。以上定义中的关键字是 a group of,也就是“一组”,一组什么呢?一组相关联的工厂方法,一组工厂方法就会构造出一组产品,称之为产品族。工厂方法模式强调的是一个工厂方法与产品,抽象工厂模式强调的是一组工厂方法与产品族。

产品:举个例子,手机是个产品,用编程的语言讲就是手机是个接口,其实现类可能包含小米手机、苹果手机等。我们定义一个工厂方法要求返回一部手机,这是工厂方法模式。

产品族:现在我们觉得手机这个接口抽象层次太高了,要把它拆分成抽象层次更低的接口,于是我们定义了CPU接口、内存接口、摄像头接口等等,这就是个产品族了。我们定义一组工厂方法要求分别返回以上各个接口,这是抽象工厂模式。

核心思想:对象创建与对象使用分离。

那么使用抽象工厂模式的目的是什么呢?其实跟工厂方法模式是一样的,最主要的目的就是分离对象创建和使用,让使用者可以面向接口编程,而无须理会具体对象是如何创建的以及对象的具体类型是什么。

示例

从工厂方法模式到抽象工厂模式只是从产品到产品族的扩展,现实中我们需要定义一个产品族的情况并不多,所以抽象工厂模式实际上的应用也不多。我们来看看MvRx中抽象工厂模式的一个应用。

这里有必要介绍一下背景知识。ViewModel承载着我们所有的业务逻辑,MvRx对ViewModel进行了扩展,要求ViewModel创建时必须提供一个State,这个State实际上就是一个Kotlin data class,它包含了页面显示所有的数据。MvRx的核心思想就是改变这个State进而驱动页面更新,所以MvRx要求每个ViewModel创建时必须提供一个State,这个初始State就代表了页面的初始状态。用“抽象工厂模式”的话说就是ViewModel和State构成了一个产品族。

来看一下MvRx对于抽象工厂模式实际的应用:

//抽象工厂
interface MvRxViewModelFactory, S : MvRxState> {

    //工厂方法1,构造ViewModel
    //这里的参数state就是 initialState 初始化好的state
    @Suppress("Detekt.FunctionOnlyReturningConstant")
    fun create(viewModelContext: ViewModelContext, state: S): VM? = null

    //工厂方法2,初始化State
    @Suppress("Detekt.FunctionOnlyReturningConstant")
    fun initialState(viewModelContext: ViewModelContext): S? = null
}

该抽象工厂就包含了两个工厂方法,只是每个工厂方法都默认返回null。在MvRx中,实现MvRxViewModelFactory是个可选项,一般都是为了依赖注入才会去实现MvRxViewModelFactory,并且MvRx要求必须在使用ViewModel的伴生对象来实现MvRxViewModelFactory:

class MyViewModel(initialState: MyState, dataStore: DataStore) : BaseMvRxViewModel(initialState) {

    //伴生对象实现 MvRxViewModelFactory接口
    companion object : MvRxViewModelFactory {

        override fun create(viewModelContext: ViewModelContext, state: MyState): MyViewModel {
            val dataStore = if (viewModelContext is FragmentViewModelContext) {
              // If the ViewModel has a fragment scope it will be a FragmentViewModelContext, and you can access the fragment.
              viewModelContext.fragment.inject()
            } else {
              // The activity owner will be available for both fragment and activity view models.
              viewModelContext.activity.inject()
            }
            //具体实现并不是很重要,总之就是让我们有机会自己构造 MyViewModel,注入 dataStore
            return MyViewModel(state, dataStore)
        }
        
        override fun initialState(viewModelContext: ViewModelContext): MyState? {
            
            // The owner is available too, if your state needs a value stored in a DI component, for example.
            val foo = viewModelContext.activity.inject()
            //我们自己构造 MyState
            return MyState(foo)
        }

    } 
}

如前所述,实现MvRxViewModelFactory是可选项,并且MvRxViewModelFactory中工厂方法都有默认返回值null,所以即使实现MvRxViewModelFactory,也不是两个工厂方法都必须自己实现,如果我们不实现哪个工厂方法,或者连MvRxViewModelFactory接口都不去实现,那么最后MyState和MyViewModel都会通过反射构造出来。

以上这个例子并不是严格意义上的抽象工厂模式,首先State和ViewModel并不是严格的产品族,而是依赖关系;其次,抽象工厂模式强调的是面向产品族接口编程,这里其实也没有,只是为了构造出ViewModel。这个例子只是长着抽象工厂模式样子的简单工厂模式,大家理解就好,不必太深究其中的细节。如同目录中提到的那样,设计模式是为了解决问题的,而不是发现问题的,关键是理解各个设计模式的思想,然后灵活应用。给模式起名字是为了方便交流,而不是限制我们的思想。

结构

抽象工厂模式的经典结构:

其实跟工厂方法模式是一样的,只是产品从单一的产品Product变成了产品族ProductA和ProductB。

你可能感兴趣的:(给人看的Kotlin设计模式——抽象工厂)