Kotlin设计模式之抽象工厂

复制代码
提供一个接口,用于创建相关或依赖对象系列,而无需指定它们的具体类。
抽象工厂------GoF

1、抽象工厂示例

为了更好地理解这种设计模式,我们直接跳入一个小例子。假设我们正在玩一款具有地图/景观的游戏(例如 帝国时代或类似的策略游戏)。游戏必须支持不同的地区,例如福雷斯特、沙漠等。根据地区的不同,可能需要不同的对象。森林里有树,沙漠里有仙人掌。事实证明,这种思维当时可以应用许多不同的对象(例如地形、食物等)。如果我们说有"植被"或有"地形",就有可能将游戏与区域完全脱钩。通过这种方式,高级游戏仅了解其他高级类别的一些信息。

但当然Game类必须使用具体对象。这就是工厂发挥作用的地方。它提供了创建具体对象的中央访问点。根据区域的不同,它会创建不同的对象。下面的UML图说明了这一点。所有蓝色的班级都是高级班级。所有绿色的类都是福雷斯特相关的类。所有黄色的类别都是与沙漠相关的类别。

1.1、SOLID原则

在进入代码之前,我们想补充一下OO设计的SOLID原则。这种设计模式是这些原则的主要贡献者。它通过使用接口将高级类与低级类解耦。这会产生可重用的代码。添加新区域不会影响高级类别(它是开放-封闭的)。所有类都非常有凝聚力(它遵循单一职责)。高级类不依赖于实现细节(它遵循依赖倒置)。

事实上,抽象工厂模式很常见。因为它很容易理解,但仍会导致代码分离得很好。

1.2、代码

kotlin 复制代码
// Different landscape classes
open class Vegatation
class Cactus: Vegetation()
class Tree: Vegetation()

open class Terrain
class Sand: Terrain()
class Grass: Trerrain()
kotlin 复制代码
// Factory Interface
interface Factory {
    fun createTerrain(): Terrain
    fun createVegetation(): Vegetation
}
kotlin 复制代码
// Desert Factory
class DesertFactory: Factory {
    override fun createTerrain(): Terrain {
        return Sand()
    }
    
    override fun createVegetation(): Vegetation {
        return Cactus()
    }
}
kotlin 复制代码
// Forrest Factory
class ForrestFactory: Factory {
    override fun createTerrain(): Terrain {
        return Grass()
    }
    
    override fun createVegetation(): Vegetation {
        return Tree()
    }
}
kotlin 复制代码
// Game
class Game(val factory: Factory) {
    private lateinit var terrain: Terrain
    private lateinit var tree: Vegetation
    
    init {
        terrain = factory.createTerrain()
        tree = factory.createVegetation()
    }
}

正如您所看到,实现非常简单。Kotlin提供了不同的方法来实现多态性。第一个是使用基类。此方法用于地形和植被类。第二个选项是使用用于工厂的接口。这取决于您需要使用的上下文,但我们想在代码中显示这两个版本。

2、TDD------测试驱动开发

抽象通常经常与测试驱动开发结合在一起出现。正如所说,TDD将带来SOLID代码,因此在进行TDD时发现这种模式也就不足为奇了。

作为一个例子,考虑一个调用URL的函数。如果调用成功(例如状态码200),它将返回字符串"OK"。否则返回"FAILURE"。

一种实现可能如下。callUrl函数就是需要测试的函数。在内部它使用名为Network的类,该类执行实际的网络访问(此处未实现)。这可能是协程或类似的东西。

kotlin 复制代码
// Not using TDD
class Network(private val url: String = "") {
    private var errorCode = 200
    
    fun isSuccessful(): Boolean {
        return errorCode == 200
    }
    
    fun execute() {
    
    }
}

fun callUrl(url: String): String {
    val network = Network(url)
    network.execute()
    if (network.isSuccessful()) return "OK"
    return "FAILURE"
}

然而,这段代码即使不是不可能测试,也是非常困难的。测试中网络访问不受控制。这可以通过使用抽象工厂模式引入模拟网络对象来改变。下面的例子证明了这一点。

kotlin 复制代码
interface Network {
    fun isSuccessful(): Boolean
    fun execute()
}

interface NetworkFactory {
    fun createNetwork(url: String): Network
}
kotlin 复制代码
// Concrete Implementation of Network
class NetworkImpl(private val url: String = ""): Network {
    private var errorCode = 200
    
    override fun isSuccessful(): Boolean {
        return errorCode == 200
    }
    
    override fun execute() {
    
    }
}

class NetworkFactoryImpl: NetworkFactory {
    override fun createNetwork(url: String): Network {
        return NetworkImpl(url)
    }
}
kotlin 复制代码
// Mock Network Implementation
class NetworkMock: Network {
    override fun isSuccessful(): Boolean {
        return true
    }
    
    override fun execute() {
    
    }
}

class NetworkMockFactory: NetworkFactory {
    override fun createNetwork(url: String): Network {
        return NetworkMock()
    }
}

测试中的功能仅略有变化。默认情况下它使用网络工厂的具体实现。然而在测试过程中它接受返回Mock对象的工厂。由于我们控制着这个工厂以及模拟对象,因此我们可以手动控制网络行为。此外,对于使用此功能的客户端,没有任何变化。函数的签名是相同的(感谢默认参数)。在内部,即使只改变了一行,它也极大地提高了代码质量,因为现在,函数依赖于接口,而不是具体的对象。

kotlin 复制代码
// Using the factory
fun callUrl(url: String, factory: NetworkFactory = NetworkFactoryImpl()): String {
    val network = factory.createNetwork(url)
    network.execute()
    if (network.isSuccessful()) return "OK"
    return "FAILURE"
}

3、替代方案/相关模式

有一些模式可以与抽象工厂一起使用或者可以用作替代。

3.1、建造者模式

当具体对象如此不同以至于他们不共享相同的接口时,通常使用构建器模式。然而结构是相似的。

3.2、单例模式

由于工厂通常是单个实例,因此控制它们的创建是有意义的。单例模式确保只能创建一个工厂对象,并且易于访问。但缺点是他会稍微降低灵活性。

3.3、装饰者模式

使用此模式的一个优点,它提供了一个完美地访问点,通过使用装饰器模式向对象添加附加行为。例如,在前面有关Network类的示例中,我们可以添加一个装饰器来记录每个网络访问。这可以完成,无需触及所有网络类。

kotlin 复制代码
// Using a decorator
class LogNetworkDecorator(private var network: Network): Network {
    override fun isSuccessful(): Boolean {
        return network.isSuccessful()
    }
    
    override fun execute() {
        // log
        return network.execute()
    }
}

class NetworkFactoryImpl: NetworkFactory {
    override fun createNetwork(url: String): Network {
        var obj = NetworkImpl(url)
        return LogNetworkDecorator(obj)
    }
}
相关推荐
wen's24 分钟前
React Native安卓刘海屏适配终极方案:仅需修改 AndroidManifest.xml!
android·xml·react native
编程乐学1 小时前
网络资源模板--基于Android Studio 实现的聊天App
android·android studio·大作业·移动端开发·安卓移动开发·聊天app
ffcf3 小时前
设计模式—专栏简介
设计模式
没有了遇见4 小时前
Android 通过 SO 库安全存储敏感数据,解决接口劫持问题
android
hsx6664 小时前
使用一个 RecyclerView 构建复杂多类型布局
android
hsx6664 小时前
利用 onMeasure、onLayout、onDraw 创建自定义 View
android
守城小轩4 小时前
Chromium 136 编译指南 - Android 篇:开发工具安装(三)
android·数据库·redis
whysqwhw4 小时前
OkHttp平台抽象机制分析
android
hsx6665 小时前
Android 内存泄漏避坑
android
whysqwhw5 小时前
OkHttp之okhttp-bom模块的分析
android