Swift中的工厂方法和抽象工厂

栏目: Swift · 发布时间: 5年前

内容简介:毫无疑问,工厂这个词在讨论编程时是程序员最常用的词之一。与此同时,它们究竟意味着什么不同。它可能是生成对象类(多态或非多态),返回对象方法(静态或非静态),甚至是常规构造函数。但是,并非生成对象的所有内容都可以称为工厂。此外,这个词可以描述四人帮的工厂中的两种不同的创作设计模式 - 工厂方法和抽象工厂。我想深入了解它们的细节,特别注意它们的经典实现和理解。

毫无疑问,工厂这个词在讨论编程时是 程序员 最常用的词之一。与此同时,它们究竟意味着什么不同。它可能是生成对象类(多态或非多态),返回对象方法(静态或非静态),甚至是常规构造函数。

但是,并非生成对象的所有内容都可以称为工厂。此外,这个词可以描述四人帮的工厂中的两种不同的创作设计模式 - 工厂方法和抽象工厂。

我想深入了解它们的细节,特别注意它们的经典实现和理解。

抽象工厂

Swift中的工厂方法和抽象工厂

在 Joshua Kerievsky的《设计模式重构》一书中,他给出了两个例子来说明这个 设计模式 什么时候有用。

  1. 封装由通用接口绑定的具体类型。在这种情况下,可以理解为一种类型,即工厂。 Factory的公共API由方法集(静态或非静态)组成,它返回公共接口类型的实例并具有有意义的名称,因此我们知道必须为特定目的调用哪一个。
  2. 第二个类似于第一个,一般而言,我们使用的所有场景或多或少都相似。这是因为在程序的不同部分中创建一种类型或一组类型的实例。同样,工厂封装了创建代码,但它的动机不同。如果该代码很复杂并且不受初始化程序调用的限制,则这是特别相关的。

在iOS开发中,运行UIViewController子类非常方便。实际上,它是iOS软件开发中流行的类型之一,在使用之前几乎总是被子类化。具体类对于客户端代码通常并不重要。

我将尽力使代码示例尽可能接近Gang of Four的书中的经典模式实现,尽管现实世界代码可以通过某种方式进行简化。只有深入了解基础中的原理才能让人更自由地使用它。

详细代码

假设我们的应用程序销售车辆,视图/展示取决于其类型。我们将为不同的车辆类型使用不同的UIViewController子类。除此之外,所有车辆的状态都不同 - 新的和二手的。

import UIKit
enum VehicleCondition {
    case new
    case used
}
final class BicycleViewController: UIViewController {
    private let condition: VehicleCondition
    init(condition: VehicleCondition) {
        self.condition = condition
        super.init(nibName: nil, bundle: nil)
    }
    required init?(coder aDecoder: NSCoder) {
        fatalError("BicycleViewController: init(coder:) has not been implemented.")
    }
}
final class ScooterViewController: UIViewController {
    private let condition: VehicleCondition
    init(condition: VehicleCondition) {
        self.condition = condition
        super.init(nibName: nil, bundle: nil)
    }
    required init?(coder aDecoder: NSCoder) {
        fatalError("ScooterViewController: init(coder:) has not been implemented.")
    }
}
复制代码

因此,我们有一组相同组的类型,其实例根据条件在类似的地方创建。例如,用户点击列表中的项目 - 根据点击的车辆的类型,它的创建方式不同。视图控制器的初始值设定项具有必须每次传递的参数值。这些论点是否揭示了创建工厂的优势,唯一知道只是所需控制器创建细节的类型?

显然,这个例子很简单。在现实世界中,介绍这个工厂将是过度工程。然而,可以想象在初始化器中具有多个参数的更多车辆类型,并且抽象工厂优势变得更加明显。

protocol VehicleViewControllerFactory {
    func makeBicycleViewController() -> UIViewController
    func makeScooterViewController() -> UIViewController
}
复制代码

介wift API编程指南建议工厂方法以“make”为前缀命名。

Gang of Four的例子是用C ++编写的,基于继承和虚函数。在Swift中,我们更接近面向协议的编程技术。

工厂接口仅包含两种方法,为自行车和踏板摩托车车创建视图控制器。这些方法返回公共父类的实例。因此,意识的区域扩散受到真正需要的区域的限制。

让我们添加两个实现抽象工厂接口的具体工厂:

struct NewVehicleViewControllerFactory: VehicleViewControllerFactory {
    func makeBicycleViewController() -> UIViewController {
        return BicycleViewController(condition: .new)
    }
    func makeScooterViewController() -> UIViewController {
        return ScooterViewController(condition: .new)
    }
}
struct UsedVehicleViewControllerFactory: VehicleViewControllerFactory {
    func makeBicycleViewController() -> UIViewController {
        return BicycleViewController(condition: .used)
    }
    func makeScooterViewController() -> UIViewController {
        return ScooterViewController(condition: .used)
    }
}
复制代码

因此,在我们的案例中,具体工厂负责不同条件下的新车和二手车。

从此以后,必要的视图控制器就像这样创建:

let factory: VehicleViewControllerFactory = NewVehicleViewControllerFactory()
let vc = factory.makeBicycleViewController()
复制代码

用工厂封装类

Swift中的工厂方法和抽象工厂

现在,让我们简单地考虑一下Kerievsky书中给出的例子。

第一个是关于具体类型的封装。记住用于在车辆上显示数据的相同视图控制器子类:

final class BicycleViewController: UIViewController { }
final class ScooterViewController: UIViewController { }
复制代码

假设我们正在处理一个单独的模块,例如库。在这种情况下,上面的视图控制器类保持内部(默认情况下),并且工厂充当公共API。 Factory的方法返回众所周知的视图控制器的父类实例。因此,关于具体类型的知识受到以下模块的限制:

public struct VehicleViewControllerFactory {
    func makeBicycleViewController() -> UIViewController {
        return BicycleViewController()
    }
    func makeScooterViewController() -> UIViewController {
        return ScooterViewController()
    }
}
复制代码

将创建转移到工厂

Swift中的工厂方法和抽象工厂

第二种情况描述了对象的复杂初始化,为了简化代码和封装,Kerievsky提供了用工厂类来约束过程的知识。

假设我们也出售汽车。毫无疑问,它是一种更复杂的车辆,具有更多参数和选项。让我们用相应的燃油类型,传动类型和车轮直径参数实现相应的视图控制器:

enum Condition {
    case new
    case used
}
enum EngineType {
    case diesel
    case gas
}
struct Engine {
    let type: EngineType
}
enum TransmissionType {
    case automatic
    case manual
}
final class CarViewController: UIViewController {
    private let condition: Condition
    private let engine: Engine
    private let transmission: TransmissionType
    private let wheelDiameter: Int
    init(engine: Engine,
         transmission: TransmissionType,
         wheelDiameter: Int = 16,
         condition: Condition = .new) {
        self.engine = engine
        self.transmission = transmission
        self.wheelDiameter = wheelDiameter
        self.condition = condition
        super.init(nibName: nil, bundle: nil)
    }
    required init?(coder aDecoder: NSCoder) {
        fatalError("CarViewController: init(coder:) has not been implemented.")
    }
}
复制代码

视图控制器初始化

let engineType = EngineType.diesel
let engine = Engine(type: engineType)
let transmission = TransmissionType.automatic
let wheelDiameter = 18
let vc = CarViewController(engine: engine,
                           transmission: transmission,
                           wheelDiameter: wheelDiameter)
复制代码

或者我们可以实现一个专门的工厂来处理初始化:

struct UsedCarViewControllerFactory {
let engineType: EngineType
    let transmissionType: TransmissionType
    let wheelDiameter: Int
func makeCarViewController() -> UIViewController {
        let engine = Engine(type: engineType)
        return CarViewController(engine: engine,
                                 transmission: transmissionType,
                                 wheelDiameter: wheelDiameter,
                                 condition: .used)
    }
复制代码

像这样使用它:

let factory = UsedCarViewControllerFactory(engineType: .gas, transmissionType: .manual, wheelDiameter: 17)
let vc = factory.makeCarViewController()
复制代码

工厂方法

Swift中的工厂方法和抽象工厂

另一个单根模式也包含了有关具体类型的知识。它通过多态实现这一点,而不是将实现细节隐藏在专门的类中。 Kerievsky在他的书中提供了 Java 中的示例,并提供了使用抽象类,但很快就会知道它是什么。我们站在自己的立场上,我们有协议。

Gang of Four的书中说这种模式也被称为虚拟构造函数,并且有充分的理由。在C ++中,如果被派生类覆盖,我们将函数称为虚拟函数。但是,人们不能将构造函数声明为虚拟,并且可能,模式发明是由模拟所需行为的尝试引起的。

多态创建

Swift中的工厂方法和抽象工厂

作为模式使用的典型示例,让我们考虑一种情况,即当在方法内部创建的对象实现时,同一层次结构中的不同类型具有类似的方法实现。让我们尝试通过将对象创建移到外部并通过将其在层次结构中向上移动一步来使类似方法变得通用来改善这种情况。

让我们回到我们的车辆视图控制器:

final class BicycleViewController: UIViewController { }
final class ScooterViewController: UIViewController { }
复制代码

现在,让我们考虑一下这个视图控制器的展示并声明一个协调器:

protocol Coordinator {
    var presentingViewController: UIViewController? { get set }
    func start()
}
复制代码

考虑这些协调器实现:

final class BicycleCoordinator: Coordinator {
    weak var presentingViewController: UIViewController?
    func start() {
        let vc = BicycleViewController()
        presentingViewController?.present(vc, animated: true)
    }
}
final class ScooterCoordinator: Coordinator {
    weak var presentingViewController: UIViewController?
    func start() {
        let vc = ScooterViewController()
        presentingViewController?.present(vc, animated: true)
    }
}
复制代码

方法start()具有类似的实现,除了在内部创建的视图控制器。让我们把创建移到外面:

protocol Coordinator {
    var presentingViewController: UIViewController? { get set }
    func start()
    func makeViewController() -> UIViewController
}
复制代码

并使用start()方法的默认实现扩展协议:

extension Coordinator {
    func start() {
        let vc = makeViewController()
        presentingViewController?.present(vc, animated: true)
    }
}

复制代码

实现代码是:

final class BicycleCoordinator: Coordinator {
    weak var presentingViewController: UIViewController?
    func makeViewController() -> UIViewController {
        return BicycleViewController()
    }
}
final class ScooterCoordinator: Coordinator {
    weak var presentingViewController: UIViewController?
    func makeViewController() -> UIViewController {
        return ScooterViewController()
    }
}
复制代码

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

微信公众号深度解析

微信公众号深度解析

魏艳 / 化学工业出版社 / 2017-5 / 49.80元

本书是一本微信公众号营销的教科书,全方位揭秘了微信订阅号、微信服务号、微信企业号三大类型账号的运营管理策略和技巧,有助于企业构建一套全新的微信公众号营销体系,打造一个移动端的商业帝国,是企业和微商必读的微信公众号营销和运营宝典。 《微信公众号深度解析:订阅号+服务号+企业号三号运营全攻略》突出了“新”、“全”、“实战”三大特点,阐述了微信公众号在新形势下的现状、发展趋势和三大类型;微信公众号......一起来看看 《微信公众号深度解析》 这本书的介绍吧!

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具