闲话设计模式之工厂模式

这篇文章打算以更休闲的对话模式来进行,我觉得这样会有趣点。为了完成对话,我决定将自己有丝分裂,就“风海”和“铜锣”吧。

风海: hello,铜锣菌,听说你最近在埋头写你的文件浏览器项目?

铜锣: 是的,没错,还在埋头苦干中呢。等发布了我一定会公开告诉大家。那么有何指教啊。

风海: 嘿嘿,我最近在学习设计模式相关的知识,看到了工厂模式,所以想问问,你的项目里有没有用到工厂模式啊。

铜锣: 嗯,其实对一个资深的程序员,在开发软件的时候是不会刻意的去想我要用什么模式设计什么,而是在遇到具体问题的时候没有找到“自然”的解决方案,开始从设计模式中去寻找启发……嗯,扯远了,说到工厂模式,我最近确实有个场景是适合用的。

风海: 哦哦?说来听听!因为我看到很多教材写工厂模式,用的例子都是“创造形状”,哎,对很多大学生来说工厂模式就等价于创造形状,没有更丰富的案例支撑,理解起来就不深刻了呀。

铜锣: 确实,不过“创造形状”确实很适合做例子就对了,毕竟大家都听得懂嘛……
嗯嗯,回到你的问题吧。你刚问我在开发中在哪里用到了工厂模式嘛,这个还蛮多的。我就举个现在正在写的代码好了。你知道的,我正在写一个文件管理器嘛,那么既然是文件管理器,自然有文件打开逻辑了。

风海: 对的,比如我打开图片文件,那么就进入图片浏览,如果我打开视频文件,那么就会播放视频。

铜锣: 没错了。为了设计上的统一,我对文件管理器中的所有文件的点击行为,都会采取一致的视觉处理策略,导航到新的界面,这个界面被我称作FileViewerView。也就是文件查看界面。

风海: 所以,文件查看器是统一的查看器,但是具体查看的行为,是由具体的文件类型决定,对吧?

铜锣: 哎哟,不错哦,没错就是这样。所以对于图片文件,那就有一个“图片文件查看器”,对于视频文件,那就是“视频文件查看器”,甚至根据用户自己的决策,还可以把任何文件查看行为转成“二进制文件查看器”呢。

风海: 就是说,铁打的文件查看器,流水的查看类型……那么具体写代码要怎么做到这点?

铜锣: 嗯嗯,Show me the code. 具体到代码上,我们就需要一个“文件查看器工厂”来决定具体产生什么查看器咯。来,初始代码奉上。

因为用SwiftUI来介绍工厂模式不够“典型”,所以我在这里把代码改造成了UIKit来替代。

import UIKit

class FileViewerController: UIViewController {
}

class FileViewerFactory {
    static let shared = FileViewerFactory()
    private init() {}
    
    func createView(filePath: String) -> FileViewerController? {
        nil
    }
}

风海: 哇,看到代码心里就踏实了……等等,你这个FileViewerFactory看起来是个单例,说到单例模式……

铜锣: 咳咳,确实是,不过我们现在聊的是工厂模式啊,专注,专注。
在我这个例子里,FileViewerFactory工厂通过传入的文件路径来识别具体的文件类型,然后根据文件的类型返回具体的文件查看器界面。看,这里用工厂模式是不是很自然。

风海: 嗯嗯,确实,那么代码写到底,如果我传入的文件是图片文件,在你的代码实现里该怎么写呢。

铜锣: 好,那我就接着往下走啦,不过接下去写的不是完整代码哦,意思到了即可。

import UIKit

class FileViewerController: UIViewController {
}

class FileViewerFactory {
    static let shared = FileViewerFactory()
    private init() {}
    
    func createView(filePath: String) -> FileViewerController? {
        if isFilePicture(filePath) {
            return PictureFileViewerController(filePath)
        }
        return nil
    }
}

风海: YES!能get到意思了。当我们点击文件时需要唤起文件查看器界面,但是我们并不知道也不关心具体会唤起哪个界面,只知道那是个“界面”。(具体到本例中是一个UIViewController),而具体产生哪个界面的任务,就是交给FileViewerFactory来决定的,我理解的对吧。

铜锣: Bingo!你答对了!其实工厂模式理解起来挺简单的,只要活血活用,可以解决很多问题呢。

风海: 嗯嗯,在你这段代码里,虽然点击文件的逻辑处理者不关心具体唤起什么查看器,但是最起码还是要告知文件的具体地址的,这样才能让工厂知道自己应该针对性的创建具体的查看器。

铜锣: 没错了,逻辑的处理者虽然不需要清楚具体唤起的是什么查看器,但是必须提供“必要信息”给工厂,这些必要信息自然是以输入参数的方式传给工厂了,这样才能让工厂合适的创建具体的查看器。举例来说,如果我开发的产品不但要求打开文件自动选择查看器,还允许用户自己选一些查看器,这样我们就要对参数进行必要改造了。

风海: 哦哦,这题我或许会。假如说我要求用二进制查看器,那么可以给createView追加一个参数openWithBinary咯?

铜锣: 你的意思是代码变成这样:

import UIKit

class FileViewerController: UIViewController {
}

class FileViewerFactory {
    static let shared = FileViewerFactory()
    private init() {}
    
    func createView(filePath: String, openWithBinary: Bool = false) -> FileViewerController? {
        if openWithBinary {
            return BinaryFileViewerController(filePath)
        }
        if isFilePicture(filePath) {
            return PictureFileViewerController(filePath)
        }
        return nil
    }
}

嗯嗯,漂亮,这确实解决了问题。当然了,这么做扩展性还是不足的,比如用户可能有多种选择,除了二进制查看器,还有文本查看器等……另外还有可能无法支持的情况,都要考虑进去。当然了,这种要求就和工厂模式本身无关了,而是一个代码设计问题。

风海: 啊哈。我们从头到尾不讨论工厂模式的定义,一上来就讨论了项目呢……不过感觉好像也没不妥,具体的例子看到后起码意会了概念。

铜锣: 确实啊,概念意会了,再去读形式就好办多了。好了,让我们来读一读工厂模式的定义。(来自维基百科)

工厂方法模式(英语:Factory method pattern)是一种实现了“工厂”概念的面向对象设计模式。就像其他创建型模式一样,它也是处理在不指定对象具体类型的情况下创建对象的问题。工厂方法模式的实质是“定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类。工厂方法让类的实例化推迟到子类中进行。”

风海: 嗯……确实先了解了案例再去看概念,好像会比较好理解一点呢。

铜锣: 啊哈。是吧。
维基百科还用了个具体的java代码对此进行了讲解。

//幾個Button類
class Button{/* ...*/}
class WinButton extends Button{/* ...*/}
class MacButton extends Button{/* ...*/}

//他們的工廠類
interface ButtonFactory{
    abstract Button createButton();
}
class WinButtonFactory implements ButtonFactory{
    Button createButton(){
        return new WinButton();
    }
}
class MacButtonFactory implements ButtonFactory{
    Button createButton(){
    }
}

风海: 嗯嗯,确实简明扼要,一句话,懂的都懂,但是实际项目用的好不好是另一回事了。

铜锣: 所以啊,要多温故而知新,在自己的项目里多反刍反刍,对自己会很有帮助呢。你看跟你聊完,我感觉自己认知又升了一个档次。

风海: 升了吗,升多高啊?

铜锣: 嗯……高这种事很抽象的,三四层楼那么高吧。

风海: ………………好了,今天我们先聊到这啦,不然代码任务完不成啦。

铜锣: 啊对对,好,拜拜。

引用:

维基百科:https://zh.wikipedia.org/wiki/工厂方法


想要一起讨论的朋友可以在我的公众号风海铜锣的加群菜单栏中申请加群完成加群申请,一起共同进步。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值