阅读 93

闲话设计模式之抽象工厂模式

本文以对话模式来进行,为了完成对话,我决定将自己有丝分裂,就“风海”和“铜锣”吧。

风海: 铜锣老弟,听说你最近的文件管理器项目进展很顺利啊,来,给我看看做到哪里了?嗯,这个按钮感觉不够炫酷啊,来,加个荧光特效,再描个边,中间再塞个小图标……

铜锣: 去去,风海老兄,就算你要改按钮也要遵守基本法啊,你没看整个产品的格调是“简约”么?

风海: emm……好吧。不过像我这种追求酷炫的,对你这个界面不太感冒啊。来来来,咱们来聊聊,假设我是个产品经理,要求你的界面既支持简约,又支持酷炫,你该怎么做。

铜锣: 得亏我是个人开发者,不用理会你这个需求。不过如果你是公司的产品经理,那我就不得不听命了。确实,对于一些产品来说,换肤是个很适合个性化人群的需求,比如PC端的QQ。

风海: 对啊,换肤多炫酷啊,QQ可以换肤,还有各种PC端的音乐播放器,哦,还有输入法。

铜锣: 嗯,每个产品都有自己的视觉架构,比如说QQ,就有好友栏,对话框,功能按钮栏等。音乐播放器有音波展示区,按钮区。如果要做换肤,就要定义好产品的视觉架构,然后去做抽象。

风海: 听得我似懂非懂,那我问你,假如你这个按钮要做换肤,你具体该怎么做嘛……哦等等,你这个按钮一变酷,标题栏还是老土风格,不行,一起换了。

铜锣: 你看你看,光换一个按钮解决不了问题吧,你又提到了标题栏,所以这是成套的设计。我就从你说的这两个做起,你要按钮和标题栏的视觉效果是动态可变化的对吧。那我设计一个类满足你:

class UI_AbstractFactory {
    func createButton() -> Button? { nil }
    func createTopBar() -> TopBar? { nil }
}
复制代码

风海: 我看到了Factory,那说明你要使用工厂模式嘛,我们之前已经聊过了这个模式了。

铜锣: 你的记性不错,确实工厂模式我们已经聊过了,这个也确实是工厂模式,不过还是可以聊一聊的,因为它是工厂模式的衍生版本:抽象工厂模式。

风海: 怪不得前面还有个Abstract。那么抽象工厂模式和普通的工厂模式有什么不同?

铜锣: 在普通工厂模式里,工厂负责创建接口使用者需要的对象,但是接口使用者不需要关心具体的对象类型,只需要关心已知的返回对象类型和接口即可。

比如当我使用一个视图工厂时,该工厂可以给我提供必要的视图,我只要用来展示就行了,我不需要关心具体视图是查看图片视图,还是播放视频视图。

抽象工厂模式也是工厂模式,但是多出了以下几点不同:

  1. 存在一个抽象工厂类型,该类型是抽象的类型定义,它定义了具体的生产接口,而不负责具体的生产任务。
  2. 具体的生产任务交给具体的工厂类型完成,具体的工厂类型明确定义了生产接口具体该生产什么类型的生产对象。

风海: 哦,我明白了,也就是说,在你举的例子里,UI_AbstractFactory是一个抽象工厂,我就叫他“UI工厂”好了,这个UI工厂是抽象的,那么具体的生产任务会交给具体的工厂来完成,对吧。

铜锣: 对,你这个UI工厂起名起的不错嘛。我在这里创建了一个UI工厂,它负责提供各种各样的UI接口,比如按钮啊,标题栏啊。但是具体该怎么实现这些接口,就交给具体的工厂来完成,比如我现在就定义了一个“极简UI工厂”。

class SimplifyUI_Factory: UI_AbstractFactory {
    func createButton() -> Button? {
        return SimplifyButton()
    }
    
    func createTopBar() -> TopBar? {
        return SimplifyTopBar()
    }
}
复制代码

风海: 我懂了!你把工厂给“抽象”了,目的就是为了灵活配置工厂类型,比如你现在实现了一个“极简UI工厂”,那么为了满足我的需求,你可以再实现一个“炫酷UI工厂”。

铜锣: 呵,可不是为了满足你的需求吗,不然我还写什么抽象工厂模式,毕竟程序员没事不会给自己瞎折腾,设计模式就是为了解决需求的,没有需求就不要凭空写设计模式,为设计模式而设计模式是设计模式的大忌……

风海: 别,你把我绕晕了。你还是赶紧给我写一个酷炫UI工厂吧。

铜锣: 好嘞。

class CoolUI_Factory: UI_AbstractFactory {
    func createButton() -> Button? {
        return CoolButton()
    }
    
    func createTopBar() -> TopBar? {
        return CoolTopBar()
    }
}
复制代码

你想要用酷炫的工厂进行生产,那么代码如下

var factory: UI_AbstractFactory?
...
factory = CoolUI_Factory()
...

let button = factory.createButton()
...
let topbar = factory.createTopBar()
...

复制代码

风海: 嗯,酷炫UI工厂果然生产了酷炫的标题和顶栏。所谓龙生龙凤生凤,不同的工厂负责生产不同的道具。各司其职,很不错呢,最关键的是,如果想要切换可以批量而不是挨个切换。

铜锣: 对,没错,这就是抽象工厂模式的核心价值。

我们且参看抽象工厂模式在维基百科的定义吧。

抽象工厂模式(英语:Abstract factory pattern)是一种软件开发设计模式。抽象工厂模式提供了一种方式,可以将一组具有同一主题的单独的工厂封装起来。在正常使用中,客户端程序需要创建抽象工厂的具体实现,然后使用抽象工厂作为接口来创建这一主题的具体对象。客户端程序不需要知道(或关心)它从这些内部的工厂方法中获得对象的具体类型,因为客户端程序仅使用这些对象的通用接口。抽象工厂模式将一组对象的实现细节与他们的一般使用分离开来。

风海: 嗯,我看到网上还把工厂模式分为三类:简单工厂模式,普通工厂模式和抽象工厂模式。而我们好像就讨论了两类,这是为什么?

铜锣: 是的,我们之前讨论过工厂模式的例子。事实上简单工厂模式和普通工厂模式的差别比较小,可以合在一起讲,它们的区别是简单工厂模式里,工厂直接负责产品的生产,而到普通工厂模式里,工厂是个抽象类,具体的生产行为交给了具体的工厂。到了抽象工厂模式里,工厂生产的对象不止一个,而是多个。

风海: 这么看来,抽象工厂模式真的很适合解决换肤问题呢,还有做PC软件时,MacWin风格的切换这类。

铜锣:对,只要涉及到“批量的接口替换”的需求。都可以考察一下是否需要抽象工厂模式呢。

风海: 很棒,今天是周六了,我们去好好吃一顿吧。周末愉快。

铜锣: 周末愉快,走起。

文章分类
代码人生
文章标签