为了账号安全,请及时绑定邮箱和手机立即绑定

设计模式之创建型模式

标签:
Go 设计

引言
GoF的《设计模式》一书总结了面向对象软件中一些宝贵的设计经验,系统地对它们命名、解释和评价,并以编目分类的形式将它们展现出来,这就是广为流传的23个设计模式的由来。

模式是一项管理复杂度的技术,几乎所有模式都遵循两个原则:

针对接口编程,而不是实现。
多用组合,少用继承。
很多模式看上去很类似,这是因为实现各种模式的方式一般就是继承和组合。对外暴露一个通用接口,既易于使用又隐藏实现细节,内部用各种子类来实现不同功能,支持扩展变化,并尽量用对象组合来实现解耦。所以你可以认为23个模式就是根据不同的使用场景变着法儿地声明接口然后继承实现最后再花式组合罢了。

模式依据其设计目的可以分为三大类——创建型(Creational)、结构型(Structural)和行为型(Behavioral)。本文主要论述几个创建型模式之间的区别与联系。

《设计模式》一书的副标题是“可复用面向对象软件的基础”,所以显然这23个设计模式是用于面向对象软件设计的,而众所周知,最适合面向对象这种范式的领域其实是 GUI 编程领域(这也是《设计模式》中大部分应用实例都是一些 GUI 框架的原因),所以本文也主要以 iOS 开发为例进行说明。示例语言选用静态语言 Swift(其实个人认为设计模式主要还是针对静态语言,很多模式在动态语言中都用处不大)。

创建型模式简介
创建型模式将实例化对象的部分从系统中独立出来,它们将系统具体使用哪些类的信息封装起来,并隐藏了这些类是如何被创建和组合的,对外只提供一个通用接口。

创建型模式有五种——Abstract Factory(抽象工厂)、Builder(生成器)、Factory Method(工厂方法)、Prototype(原型)、Singleton(单例)。我个人认为抽象工厂模式和生成者模式的抽象层级最高,因为它们都可以分别用工厂方法和原型实现。而工厂方法和原型是同一个层级的,它们在大多数时候是互斥的,一般不能结合使用。至于单例,就是保证某个类只实例化一次而已,想用在哪儿都行(只要符合实际需求)。

抽象工厂侧重于创建一系列同一风格的产品,每个产品都有一个抽象接口,使用者并不知道它使用的是具体哪种风格的产品。而生成器侧重于一步步构建一个复杂产品,这个复杂产品不需要有一个公共接口,使用者知道它具体得到了一个什么产品。

抽象方法定义一个用于创建对象的接口,子类重写创建方法,被创建的产品会有一个抽象接口,所以使用者并不知道具体得到的是什么产品。原型将某个实例对象作为“原型”,通过复制这个原型来创建新的对象,由于可以动态指定原型,所以可以在运行期改变创建的产品。

一个简单案例
假设我们现在要构建两个界面,界面的构成元素都是一个 Label 和 Button。一个界面在打开应用的时候显示,Label 和 Button 会显示“Hello……”,另一个在应用关闭前显示,Label 和 Button 会显示“Goodbye……”。而且不止是显示的文字,连同背景色、位置、大小等等属性都会不同。于是我们考虑自定义几个 Label 和 Button:

//MARK: - Hello 系列产品
class HelloButton: UIButton {
    init() {
        let frame = CGRect(x: 50, y: 200, width: 300, height: 50)
        super.init(frame: frame)
        backgroundColor = UIColor.greenColor()
        setTitle("Hello, I am a button.", forState: .Normal)
        setTitleColor(UIColor.redColor(), forState: .Normal)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

}

class HelloLabel: UILabel {
    init() {
        let frame = CGRect(x: 50, y: 300, width: 300, height: 50)
        super.init(frame: frame)
        backgroundColor = UIColor.yellowColor()
        text = "Hello, I am a label."
        textAlignment = .Center
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

//MARK: - Goodbye 系列产品
class GoodbyeButton: UIButton {
    init() {
        let frame = CGRect(x: 50, y: 400, width: 300, height: 20)
        super.init(frame: frame)
        backgroundColor = UIColor.redColor()
        setTitle("Goodbye, don't forget I'm a button.", forState: .Normal)
        setTitleColor(UIColor.greenColor(), forState: .Normal)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

}

class GoodbyeLabel: UILabel {
    init() {
        let frame = CGRect(x: 100, y: 500, width: 200, height: 100)
        super.init(frame: frame)
        backgroundColor = UIColor.blackColor()
        text = "Goodbye, don't forget I'm a label."
        textColor = UIColor.whiteColor()
        font = UIFont.systemFontOfSize(10)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}
然后我们定义一个用来组合界面视图的类:

class PageView: UIView {
    init() {
        super.init(frame: UIScreen.mainScreen().bounds)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

稍后我会介绍用不同的模式来创建产品。

Factory Method(工厂方法)

现在我们给 PageView 加上用来创建 Label 和 Button 的工厂方法,并在构造器中调用工厂方法。工厂方法可以是抽象方法也可以有一个默认实现,这里我给出一个默认实现:

class PageView: UIView {
    init() {
        super.init(frame: UIScreen.mainScreen().bounds)

        let label = createLabel()
        let button = createButton()

        addSubview(label)
        addSubview(button)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    func createLabel() -> UILabel {
        return UILabel()
    }

    func createButton() -> UIButton {
        return UIButton()
    }
}

这样我们要创建 HelloPageView 和 GoodbyepageView 的时候只要重写工厂方法就好了:

class HelloPageView: PageView {
    override func createLabel() -> UILabel {
        return HelloLabel()
    }
    override func createButton() -> UIButton {
        return HelloButton()
    }
}

class GoodbyePageView: PageView {
    override func createLabel() -> UILabel {
        return GoodbyeLabel()
    }
    override func createButton() -> UIButton {
        return GoodbyeButton()
    }
}

常规的工厂方法有个显而易见的缺点就是当需要进行不同产品的组合的时候,容易导致类爆炸。譬如现在我们只是需要“HelloLabel + HelloButton”和“GoodByeLabel + GoodbyeButton”,但如果我们还需要“ HelloLabel + GoodbyeButton”和“ GoodByeLabel + HelloButton”这样的组合,那就又得新建两个 PageView 的子类。

解决这个问题的方法是可以使用参数化的工厂方法,可以给工厂方法传递一个参数(标识符),然后根据标识符来实例化特定的产品,这样我们就不需要各种 PageView 子类了。但是一旦扩充了新产品(增加了新的XXXLabel或者XXXButton),就得去修改相应的工厂方法以支持新产品。这时候如果是支持范型的语言,就可以使用范型参数来解决这个问题(前提是工厂方法中没有针对某个特定子类产品的操作),我们把 PageView 改成一个范型类:

class PageView<L: UILabel, B: UIButton>: UIView {
    init() {
        super.init(frame: UIScreen.mainScreen().bounds)

        let label = createLabel()
        let button = createButton()

        addSubview(label)
        addSubview(button)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    func createLabel() -> L {
        return L()
    }

    func createButton() -> B {
        return B()
    }
}

这样就可以在 Controller 中指定要返回那种类型的 PageView,可以任意组合 Label 和Button:

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        let pageView = PageView<HelloLabel, HelloButton>()
        view.addSubview(pageView)
    }
}
Prototype(原型)

原型模式顾名思义就是将某个实例对象当做原型,通过复制它来创建其他同类型的对象。要使用原型模式需要给产品类设置一个用来克隆自身实例的函数,虽然很多语言或者标准库都有 copy 函数,对克隆对象提供了一些原生支持,但你还得考虑深拷贝和浅拷贝的问题,前者同时拷贝对象内部的状态,后者则通过指针共享状态。

像 Self、JavaScript 这样基于原型的语言可以说处处都用到了原型模式,而像SmallTalk、OC、Ruby 等动态语言中,类本身可以当作对象传递并用其创建实例对象,甚至在 Swift 中也可以直接用所谓的元类型(SomeClass.Type)来初始化一个对象,所以我觉得这个原型模式在很多时候并不实用。它最大的优点是灵活性,可以动态指定要创建的对象,而这点,可以通过传递“元类型”或者利用范型轻易做到。

Abstract Factory(抽象工厂)

抽象工厂通常是利用工厂方法来实现的,也可以利用范型或者原型。它的核心思路就是单独抽象出一个工厂类,通过对象组合,系统委托这个工厂类来创建一系列产品。没错,这个模式的重点就在于强调了“一系列”这三个字。如果你的最终目的是要把这一系列产品组合成一个产品,那就应该用 Builder 模式。

所以我上面举的那个实例其实用 Builder 模式比较合适,当然,我这里也可以强行用抽象工厂做一下,只要把最后组装产品那一步留到外部好了。

//MARK: - 抽象工厂
protocol UIFactory {
    func createLabel() -> UILabel
    func createButton() -> UIButton
}

//MARK: - 具体工厂
class HelloUIFactory: UIFactory {
    func createLabel() -> UILabel {
        return HelloLabel()
    }

    func createButton() -> UIButton {
        return HelloButton()
    }
}

class GoodbyeUIFactory: UIFactory {
    func createLabel() -> UILabel {
        return GoodbyeLabel()
    }

    func createButton() -> UIButton {
        return GoodbyeButton()
    }
}

然后修改 PageView,构造器以一个 UIFactory 对象为参数:

class pageView: UIView {
    init(factory: UIFactory) {
        super.init(frame: UIScreen.mainScreen().bounds)

        let label = factory.createLabel()
        let button = factory.createButton()

        addSubview(label)
        addSubview(button)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

如果要生成一个 HelloPaveView 并显示,只需要在 Controller 的 ViewDidLoad 方法中这样写就好了:

let pageView = PageView(factory: HelloUIFactory())
view.addSubview(pageView)

当然跟工厂方法一样,为了避免类继承层次过深,也可以使用范型版本的工厂,只不过这样在使用的时候就需要明确指出需要创建的产品类型了:

//用范型控制产品类型,不需要定义一堆 UIFactory 子类。
class GenericUIFactory<L: UILabel, B: UIButton>: UIFactory { 
    func createLabel() -> UILabel {
        return L()
    }

    func createButton() -> UIButton {
        return B()
    }
}

使用:

let pageView = PageView(factory: GenericUIFactory<HelloLabel, HelloButton>())

更多待我后续补充。谢谢阅读

点击查看更多内容
7人点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
移动开发工程师
手记
粉丝
0
获赞与收藏
57

关注作者,订阅最新文章

阅读免费教程

感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消