构造函数中更好的初始化设计

3
我有一个类,大致是这个样子的,它作为客户端的工厂,基于从凭证服务中检索的凭证。 它只构建一次客户端,并在每次调用时返回。
public class ClientFactory {
   private CredentialService credentialService;
   private ClientA clientA; 

   public ClientFactory(CredentialService credentialService){
      this.credentialService = credentialService;
      //initialization in constructor
      this.clientA = buildClientA(credentialService.getCredentials());
   }

   public ClientA getClientA(){
       return clientA;
   }

   /** Build new ClientA using crendentials*/
   private ClientA buildClientA(String credentials){
     return new ClientA(credentials);
   } 
}

我看到的问题是构造函数中的第2行,它基本上开始使用依赖项“credentialService”立即初始化其他依赖项。如果其他开发人员移动构造函数中代码的顺序,它将开始失败。另一个选项是将方法getClientA()更改为这样。
   public ClientA getClientA(){
      if(clientA == null) {
        this.clientA = buildClientA(credentialService.getCredentials());
      }
       return clientA;
   }

但是它存在线程安全问题。有没有更好的设计方式来避免我上面提到的问题?谢谢。

1
但它存在线程安全问题。有什么问题?这不一定有问题。你能把 getClientA() 方法变为 synchronized 吗? - Peter Lawrey
@PeterLawrey:如果两个线程同时调用getClientA(),它们都可以执行空值检查并创建ClientA的不同实例。然后就会出现竞争条件来确定将返回/存储哪个实例。 - unholysampler
@PeterLawrey - synchronized 也可以使用。我只是想知道在这种情况下是否可以应用某些单例模式。 - RandomQuestion
为什么工厂要持有它构造的对象的引用? - Jeff
这个问题不应该放在codereview.stackexchange.com上吗? - DaoWen
3个回答

3

好的,

this.clientA = buildClientA(credentialService.getCredentials()); 依赖于构造函数传递的参数 credentialService,而不是成员 this.credentialService。因此,初始化顺序并不重要。

顺便提一下,为了安全起见和避免混淆,我不会在构造函数的参数和成员中使用相同的名称。


我太蠢了!没注意到这个问题。这个方法也应该可以工作,除了我的问题中只提到了一个依赖项,而实际上我有多个依赖项,所以需要在构建方法中传递它们。但我想这不应该是个问题。 - RandomQuestion

1
你所称的ClientFactory看起来其实只是一个ClientA。那么,为什么不创建一个工厂依赖于凭证,并共享对ClientA的引用,以避免创建额外的ClientA对象呢?
public class ClientFactory {
   private final CredentialService credentialService;

   public ClientFactory(CredentialService credentialService){
      this.credentialService = credentialService;
   }

   public ClientA newClientA(String credentials){
     return new ClientA(credentials);
   } 
}

如果您想拥有潜在的客户池,可以共享一个Supplier<ClientA>
Supplier<ClientA> simpleSupplier = Suppliers.ofInstance(ClientFactory.newClientA()); // guava
Supplier<ClientA> allNewSupplier = () -> ClientFactory.newClientA(); // java 8 lambda
ClientA cachedClient = simpleSupplier.get();
ClientA newClient = allNewSupplier.get();

了解Supplier很好。我会看看能否利用它。 - RandomQuestion

1

只需不将CredentialService的引用保存为实例变量,并继续将credentialService.getCredentials()传递给bulldClientA。您已经没有使用实例变量credentialService


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接