【问题标题】:Singleton using getInstance vs public declaration使用 getInstance 与公共声明的单例
【发布时间】:2019-11-17 11:31:01
【问题描述】:

当您可以将变量公开并访问实例时,创建getInstance() 有什么意义?

我知道抽象工厂设计模式对工厂来说是有意义的,因为我们在运行时决定我们需要的实现类型。

使用 getInstance():

public class ClientFactory {
    private static ClientFactory instance = null;

    private ClientFactory() { }

    public static synchronized ClientFactory getInstance() {
        if ( instance == null ) {
            instance = new ClientFactory();
        }
        return instance;
    }
}

公开变量而不是创建实例方法:

 public class ClientFactory {
    public static final ClientFactory instance = new ClientFactory ();
    private ClientFactory() { }
 }

我们可以使用ClientFactory.instance.foo();轻松获取访问权限。

【问题讨论】:

  • 如果其他类创建了新实例怎么办? ClientFactory.instance = new ClientFactory(); 此外,如果某个类将某个来自 ClientFactory 的派生类分配给实例怎么办? ClientFactory.instance = new DerivedClientFactory();
  • 不能,构造函数是私有的。
  • 啊,我的错……没注意到
  • 如果你去公共领域,至少要写成final(这样没人能惹它)。
  • @Thilo 是的,我错过了。这是final

标签: java oop design-patterns singleton


【解决方案1】:

使用像 getInstance() 这样的 FactoryMethod 可以为您提供更多未来更改的选择。

例如

  • 延迟加载
  • 用其代理之一替换实际类
  • 用其中一个子类替换类(如果有一天ClientFactory 演变成具有不同子实现的抽象类)等。

【讨论】:

  • 是的,这正是抽象的优势所在,但是当我们知道一个服务并且它的行为方式完全一致时,为什么还要创建它。
  • 未来改变的选择就是能够适应你所知道的事情不再真实的时候。需求变化,软件发展。 FWIW 两种模式都是老派,许多人用依赖注入代替它们。那么使用你的服务的代码就不需要关心它来自哪里了。
  • @Thilo 是的,但这是一个微小的变化。我的意思是当我得到需求时,我可以让它更松耦合和模块化。
  • 很公平。但答案仍然存在:使用函数提供了稍后更改实现的选项,而无需更新所有调用者。字段无法做到这一点。是否需要由您决定。
  • 酷,感谢@Thilo 此外,急切初始化和惰性的区别也在那里,是的,这是在较低级别
【解决方案2】:

后一个例子是急切的 - ClientFactory 总是被创建。如果使用ClientFactory 的可能性很小,并且实例化它的成本很高,这可能是一个缺点。

第一个是懒惰的。这意味着,当不使用 ClientFactory 时,它根本不会被创建。此外,ClientFactory 是不可变的,而公共字段可以被覆盖。它的实例化也是安全的,因为它受到synchronized 的保护。

考虑到这种安全性,当客户端调用以下行时(在第一种情况下):

ClientFactory.instance = null;

【讨论】:

    【解决方案3】:

    正如其他人正确指出的那样,这两种方法之间的主要区别在于,第一种方法将使用惰性实例化,而在第二种方法中,对象是急切地创建的。第二个方法中的ClientFactory 字段应标记为final,以避免让此类的客户端重新分配该字段。

    第一种方法也使用synchronized,这可能会影响性能,因为在第一次成功分配字段后 - 以后不需要同步。因此,对于这种方法,您还可以将 initialization-on-demand 与持有人实例一起使用(因为您正在为单例使用静态字段):

    public class ClientFactory {
    
        private ClientFactory() { }
    
        private static class ClientFactoryHolder {
            private static final ClientFactory INSTANCE = new ClientFactory();
        }
    
        public static ClientFactory getInstance() {
            return ClientFactoryHolder.INSTANCE;
        }
    }
    

    它利用 JVM 类初始化。 ClientFactoryHolder.INSTANCE 字段将在有人调用 ClientFactory.getInstance 方法时被初始化,因为类将在首次引用时被初始化。另请注意,现在getInstance 方法不必同步,因为 JVM 会为我们处理它。

    【讨论】:

      猜你喜欢
      • 2021-08-11
      • 1970-01-01
      • 2013-05-21
      • 2010-12-06
      • 2015-03-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多