构造器或者 setter ?

我的网站

Java 对象在使用前需要创建出来是不言而喻的,无论是 domain、框架、库或者其它任何形式的类都一样。当你的编码是面向对象的,这些类不过就是对象的定义而已,总之就是不能在没有创建前就使用对象。

当我们谈到对象的初始化时,我通常都会考虑到它们的依赖。是如何注入它们的?你是会用构造器还是 setter ?

让我来帮助你们做出一个正确的选择吧。

之前我们有个需求去处理一些事件。为了处理这些事件我们需要从仓库里访问必要的数据然后传递给触发器,而触发器会根据所给的数据触发对应的动作。

在实现的时候我们创建了如下的类:

public class SomeHandler {
   public SomeHandler(Repository repository, Trigger trigger) {
       // some code
   }

   public void handle(SomeEvent event) {
       // some code
   }
}

需求总是会不断更改的。我们的客户说偶尔呢他们会在对应的动作执行之前将访问到的一些仓库中的信息存储下来,他们会拿这些数据做些统计以及更长久的分析。

那我们更改过后的类就成这样了:

public class SomeHandler {
   public SomeHandler(Repository repository, Trigger trigger) {
       // some code
   }

   public SomeHandler(Repository repository, Trigger trigger, SnapshotTaker snapshotTaker) {
       // some code
   }

   public void handle(SomeEvent event) {
       // some code
   }
}

一个月过后我们的客户又提出了另一个需求,在触发一个事件后需要给个通知。这个在处理一些紧急事件是必要的,需要一个更高的透明度。

好,我们满足了以上两个需求后代码如下:

public class SomeHandler {
   public SomeHandler(Repository repository, Trigger trigger) {
       // some code
   }

   public SomeHandler(Repository repository, Trigger trigger, SnapshotTaker snapshotTaker) {
       // some code
   }

   public SomeHandler(Repository repository, Trigger trigger, Notifier notifier) {
       // some code
   }

   public SomeHandler(Repository repository, Trigger trigger, SnapshotTaker snapshotTaker, Notifier notifier) {
       // some code
   }

   public void handle(SomeEvent event) {
       // some code
   }
}

这代码看起来没什么问题,难道不是么?好吧,这是个反问。

用构造器还是不用?

在上述的例子中我们的类有 4 个构造器。为什么会有这么多呢?这是用户需求更改的需要。而且似乎很不错。我们的应用应该要满足用户的需求。

但是问题出在哪里呢?问题就出在类的设计上。

为什么我们会需要这么多的构造器?因为有些参数是可选的,它们完全由外部条件所决定。那我们真的需要这么多的构造器么?在回答这个问题之前,我们最好问一下这个不同的:构造器的涵义是啥?

我们要创建一个拥有合法状态的对象,在创建一个可用对象的参数里不应该再包括其它非必需参数。这才是那些放入构造器的参数的意义。换句话说就是,我们在构造器里边儿只应该放必要的参数,构造器里的参数不应该是可选择的。如果某个参数是可选的,那么在创建一个合法的对象时它不是必须要的。

如果我们需要一些额外的参数来处理业务那么我们应该通过不同的方式将其注入,这就是 setter 的作用之所在。我们并没有强制的去调用 setter 方法,它所提供的功能并不是必须的。因此当存在那种非必需的参数时我们应该用 setters 来注入。

那么,我们还需要这么多的构造器么?请代码来说事儿:

public class SomeHandler {
   public SomeHandler(Repository repository, Trigger trigger) {
       // some code
   }

   public void setSnapshotTaker(SnapshotTaker snapshotTaker) {
       // some code
   }

   public void setNotifier(Notifier notifier) {
       // some code
   }

   public void handle(SomeEvent event) {
       // some code
   }
}

代码量少了且更清晰了。这里第一眼就知道什么是必须的以及什么是可能用到的。

但是等等! Setter ?!

我并不喜欢 setters。为什么呢?因为某种程度上这些方法违反了 封装性,那么我们可以用什么来代替 setters 呢?在这个样例中可以用什么来代替呢?

不过我们无法避免食用这些方法,更准确地说,我们需要它们所带来的功能。相当于用户有需求来开启这些功能,在样例中修改器是需要保留的。当然,我们始终能够让代码更优,更加的域相关。如何做呢?我们只需要把这些关系和域关联起来即可:

public class SomeHandler {
   public SomeHandler(Repository repository, Trigger trigger) {
       // some code
   }

   public void enable(SnapshotTaker snapshotTaker) {
       // some code
   }

   public void enable(Notifier notifier) {
       // some code
   }

   public void handle(SomeEvent event) {
       // some code
   }
}

我写到我对 setters 不感冒,因为它们破坏了封装性,而不仅仅是功能本身。使用类似 setX 的方法还存在另外一个问题,甚至方法的名称都是面向实现的。有时 setter 功能是需要的,但是记住对于这类方法的命名需要和域关联起来。

太多的选择

有时太多的可选项也会导致问题,这可能暗示着你正在破坏单一职责原则。如果有太多的选择意味着有太多的职责,这个时候就需要再次考虑你当前的方案了。

当增加一个可能值到类中的时候一定要小心,有可能这个类担负了太多的事儿?

总结

希望你觉得这片文章有用。

你现在应该明白在构造器中只应该放那些必要的参数,而非必需的通过那些有明确意义的方法来实现。


翻译原文

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章