美文网首页
工厂模式

工厂模式

作者: l1n3x | 来源:发表于2024-05-20 18:18 被阅读0次

    设计模式教程介绍的第一个模式大部分是工厂模式,这就犹如英语单词本中的 abandon,看几次就放弃几次,看到最后看到设计模式这个字眼就想放弃了。其实初学者设计模式无法理解很正常,因为网上充斥着大量奇怪的设计模式教程。里面举的例子看起来逻辑感觉没什么问题,但是越读越奇怪。就拿工厂模式来举例,在 google 上搜索 工厂模式,排在第一位的是工厂模式|菜鸟教程。然后对于简单工厂的举例是:

    public class FactoryPatternDemo {
       public static void main(String[] args) {
          ShapeFactory shapeFactory = new ShapeFactory();
          Shape shape1 = shapeFactory.getShape("CIRCLE");
          shape1.draw();
       }
    }
    

    然后解释大概就是:

    但是怎么看都一本正经的说着充满专业术语但没用的话,我们来缕一缕里面提到的三个优点:

    • 一个调用者想创建一个对象,只要知道其名称就可以了。
      从字里行间完全看不出为什么这是一个优点,只要知道其名称就可以调创建对象为什么是一个优点?
    • 扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。
      为什么扩展性高? 要增加一个产品难道不是只需要增加一个类即可吗?
    • 屏蔽产品的具体实现,调用者只关心产品的接口。
      直接 new 对象没屏蔽具体实现吗? 既然都是实现同一接口,调用者当然只用关心产品的接口啊!

    我甚至可以仿照这样的方式,写一个不使用简单工厂三个优点:

    1. 一个调用者想创建一个对象,只要知道其包名和类名即可,甚至无需创建工厂类。
    2. 扩展性高,如果想增加一个产品,只需要增加一个类即可。
    3. 调用者只需要关注需要的类,无需关注需要的工厂。

    虽然这样有点为了反而反的意思。另外,不是说上面的三个优点是错误的,它在某些情况下是真实存在的。但是用这种方式写出来,新手看了一脸懵逼。老手虽然能看懂,但是人家是老手啊,不看也能懂。

    借用这个话题,我来谈一谈自己对工厂模式的理解。不说能有多深刻,多贴近真实情况。至少我能把原因和结果说清楚。

    回到刚才提到的三个优点,第一点是:

    一个调用者想创建一个对象,只要知道其名称就可以了。

    这个句话换个说法就是,可以在运行时确定对象的具体类型,而非在编译时。当然这不是工厂模式带来的优点,而是工厂模式是这样做更优雅了。举个例子,要渲染某个软件的 UI 可以这样写:

    def renderUI():
        theme = new Theme() 
        theme.render() # 渲染主题
      # 其他的渲染工作
    

    代码一定会面例变换。例如,现在大部分的软件都推出了夜间模式。我们的软件也要推出夜间模式。要根据用户设置动态的更换主题,不使用工厂模式可以这样写:

    def renderUI():
        if config.dark: # config 可能来至包的导入或其他途径
            theme = new DarkTheme() # 主题
        else:
            theme = new Theme()
        theme.render()
        # 其他的渲染工作
    

    当然这样写问题很大,具体如下:

    1. 扩展性: 如果需要增加其他主题类型,如男生主题,女生主题,则需要到 renderUI 修改创建逻辑代码。
    2. 抽象层级不一致: renderUI 实际上不应该包含如果根据设置项来创建哪个主题对象这一逻辑,这明显违反了单一职责原则

    如何解决,其实从上面提到的抽象层级不一致能看出解决手段。即让 renderUI 只做渲染的功能,根据设置项来创建哪个主题对象的工作就交给简单工厂来做吧。我们来稍微改一下代码:

    def theme_factory(dark):
        if dark: # config 可能来至包的导入或其他途径
            theme = new DarkTheme() # 主题
        else:
            theme = new Theme()
    
    def renderUI(): # 不包含根据设置创建对象的逻辑,
        theme = theme_factory(config.dark)
        # 其他的渲染工作
    

    看到这里你也许会问,这怎么就是工厂模式了?你只不过把一些代码移动到另一个方法了而已啊。没错,这确实是如假包换的简单工厂模式。设计模式也就是能保证写出的代码满足好代码的一些规范,如一致抽象层级等规范的一系列方法。如果你的代码本身就满足这些条件,那你大概也在无形中使用了设计模式。

    当然这里还是存在没讲清楚的问题: 新增主题时,仍然要修改 theme_factory 方法 那和修改 renderUI 有区别吗?只不过修改的地方从一个方法移动到另一个方法而已。当然,区别是存在的。从我个人的角度区总结可能有以下方面:

    1. 不是所有的工厂都只被调用一次。在这个例子中,theme_factory 只会被 renderUI 调用。但是如果一个应用里把工厂做的事情都分散在调用方,且调用方又很多的时候。进行修改就要修改无数个调用方。如果用简单工厂,那么只需要在工厂中修改即可。这其实就是封装变动,增加重用性
    2. 修改后出错的概率。就算 theme_factory 只被调用了一次,发生变动时修改的次数是相同的。但是仍然要假设修改简单方法比修改复杂方法更简便,也更不容易出错。就算不得不进行修改,那也宁愿修改简单的方法,而不去修改复杂的方法。

    那么什么是抽象工厂呢,抽象工厂其实就是产生工厂的工厂。例如我们的UI准备增大对VIP用的吸引力。要针对现存的的主题都制作一个VIP版。那么此时我们可以增加一个方法:

    def theme_factory_factory(is_vip):
        if is_vip:
            return vip_theme_factory
        else:
            return normal_theme_factory
    
    def renderUI():
        theme_factory = theme_factory_factory(config.is_vip)
        theme = theme_factory(config.dark)
    

    那么你可能还会说,那万一有一天又有工厂的工厂的工厂。。

    使用方法或则类决于你工厂的复杂情况。如果你的工厂有复杂的内部状态和复杂的参数,建议使用类来表示工厂。

    相关文章

      网友评论

          本文标题:工厂模式

          本文链接:https://www.haomeiwen.com/subject/hpntohtx.html