【问题标题】:Consider providing static factory methods insteads of constructors考虑提供静态工厂方法而不是构造函数
【发布时间】:2012-10-24 09:58:08
【问题描述】:

类允许客户端获取实例的正常方式是提供公共构造函数。另一种方法是提供一个公共静态工厂方法,它只是一个返回类实例的静态方法。使用静态工厂方法的优缺点是什么?

【问题讨论】:

    标签: java


    【解决方案1】:

    Effective Java 一书中的这一章很好地解释了这一点:Consider Static Factory instead of Constructors。它以您能理解的最佳方式解释了它们的所有优缺点。

    只是引用书中的优点和缺点:

    优势

    • 静态工厂方法的一个优点是,与构造函数不同,它们有名称。
    • 静态工厂方法的第二个优点是,与构造函数不同,它们不需要在每次调用时都创建新对象。
    • 静态工厂方法的第三个优点是,与构造函数不同,它们可以返回其返回类型的任何子类型的对象。
    • 静态工厂方法的第四个优点是它们减少了创建参数化类型实例的冗长性(在 Java 7 中可以忽略这一点)

    缺点

    • 只提供静态工厂方法的主要缺点是不能对没有公共或受保护构造函数的类进行子类化

    • 静态工厂方法的第二个缺点是它们不容易与其他静态方法区分开来。

    您可以在我提供的链接中更详细地研究它们。

    【讨论】:

    • 静态工厂方法的第二个优点是,与构造函数不同,它们不需要在每次调用时都创建一个新对象。这并不总是一个优点实际上,if 可能会阻止编译器正确优化(因为代码路径未知),如果 cpu 无法正确预测分支,情况可能会更糟。
    • 如果你反对子类化并且有工厂方法的命名约定,比如newClassName(),那么缺点似乎不是很大。
    【解决方案2】:

    唯一的缺点是要编写更多代码,但它仍然存在,因此拥有工厂至少需要一些好处。

    工厂不需要总是返回一个新对象,这是一个优势。

    Factory 可以实例化它想要的任何子类,那是另一个。

    在我的项目中,我经常添加工厂只是为了让我的客户端代码看起来更好。如果您对工厂方法使用静态导入,则调用看起来比 new 表达式更好,尤其是在类名称不是特别简洁的情况下,这种情况经常发生。

    【讨论】:

    • 你仍然需要一个受保护的 ctor 来允许继承。我也有点不同意更好的代码和返回已经存在的对象的能力——我更喜欢知道一个对象是新创建的还是实际缓存的。如果我想排除 new XXX() ,只需向客户端提供不带任何实例的接口。
    • @bestsss 提供构造函数排除缓存选项:如果目的是“让您知道实例是否被缓存”,这是一个非常生硬的工具。无论缓存策略如何,代码的许多方面都保持完全相同,并且工厂允许实现这种解耦。如果您向客户端提供接口,那么这些接口中至少有一个方法是工厂方法。在这方面我不明白你的意思。
    • 我的意思是在查看代码时知道实例是否是缓存的。额外的知识有助于追踪错误——这是主要原因。至于接口 - 是的,你有一个入口工厂点,然后它单独在接口上工作,从客户端的角度来看,没有静态方法可以用作工厂。不过,像 InitialContext 这样的东西甚至不使用静态方法来创建第一个实例。
    • @bestsss 那么,代码看起来如何让您知道正在返回缓存的实例?顺便说一句,我看不出new InitialContextContextFactory.getInstance 有什么优势,除了我知道一个劣势:后者的返回类型可以是一个接口。
    【解决方案3】:

    优势: - 静态工厂方法的一个优点是,与构造函数不同,它们有名称。 - 静态工厂方法的第二个优点是,与构造函数不同,它们不需要在每次调用时都创建一个新对象。 - 他们可以返回其返回类型的任何子类型的对象。 缺点: - 静态工厂方法的主要缺点是没有公共或受保护构造函数的类不能被子类化。 - 它们不容易与其他静态方法区分开来。

    【讨论】:

      【解决方案4】:

      我会对问题的原作者说静态工厂方法是一种工具。像所有工具一样,它们具有最适合的用途、尚可通过的其他用途以及难以适应的其他用途。举一个现实世界的例子,锤子非常擅长打钉子,足以用去除钉子的一端顶开一个密封的板条箱(撬棍仍然会好得多),但对于刨平粗糙的表面无用。

      工厂方法是指一组创建设计模式中的一个,即创建对象的范例。在诸如“Builder”和“Prototype”之类的创建设计模式中,不仅不鼓励使用 new 操作符来创建对象,而且认为它对整体设计目标有害。人们谈论的创造设计模式是……

      1. 工厂方法
      2. 抽象工厂方法
      3. 单例模式
      4. 建设者
      5. 原型

      一般来说,工厂方法用于根据用户或设计者提供给方法的数据,从一组相关的子类中创建一个对象。更具体地说,静态工厂方法使您可以控制对象的创建,即使每次返回的对象都相同。例如,当创建对象的过程在时间和资源方面非常昂贵时,这可能非常重要。在这种情况下,使用 new 运算符创建对象可能会导致严重的性能损失。

      一种解决方案可能是维护一个可重用的对象池。通过使用静态工厂方法,应用程序设计者可以提供逻辑来返回一个可用的免费现有对象。这将节省构建新对象的潜在高成本。这正是提供“连接池”的连接管理器对网络数据库连接所做的事情。不是在每次客户端发出请求时都建立一个新的数据库连接,而是从现有对象池中分配连接对象(如果可用的话)。这是使用新运算符实际上会损害应用程序性能并破坏软件工程师的设计目标的情况示例。

      考虑使用工厂方法而不是 new 运算符来创建对象的好时机是:

      • 将要创建的对象属于可以根据提供的数据创建的几个可能的对象子类之一。
      • 有一个很好的理由来控制对象创建过程,而不是在构造函数中,例如。 Singleton 设计模式需要静态工厂方法。

      考虑使用工厂方法的最佳时机是:

      • 简单、轻量级的对象

      这一切都是为了列举软件设计问题,然后决定哪些工具最适合解决它。静态工厂方法对某些事情有好处,而对其他事情则不太好……就像任何工具一样。

      【讨论】:

        【解决方案5】:

        其中一个优点是您可以为工厂方法提供易于理解的名称。它将帮助您轻松了解您的函数中的哪些函数在工作,并在将来轻松维护您的代码。看看这个例子,希望对你有帮助。

            public class Contact {
        
            private Contact(String displayName, String phoneNumber, int contactType){
            //do something
            }
        
            //then we will have few functon static to get constructor private
            public static createContactUnknow(String phoneNumber){
                return new Contact("","00000000",0);
            }
        
            public static createContactValid(String displayName, String phoneNumber, int contactType){
                return new Contact(displayName, phoneNumber, contactType);
            }
        }
        
            //then
            Contact myGirlFriend = Contact.createContactValid("xxxx","000000",1);
            Contact unknowFriend = Contact.createContactUnknow("45454545");
        

        【讨论】:

          猜你喜欢
          • 2016-11-28
          • 1970-01-01
          • 2018-08-04
          • 1970-01-01
          • 2014-02-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-07-24
          相关资源
          最近更新 更多