建设者还是二传手?
不用說,每個對象都需要先創建才能使用。 無論我們是在談論域,框架,庫還是任何其他類型的類,都沒有關系。 當您的代碼是面向對象的時,這些類僅是對象的定義。 在創建對象之前,不能使用它們。
在談論對象的初始化時,我們經常需要考慮依賴關系。 您將如何注入它們? 您會使用構造函數還是二傳手?
讓我來幫助您做出正確的決定。
很久以前..
……需要處理一些事件。 為此,我們必須首先從存儲庫中檢索必要的數據,然后將其傳遞給觸發器,該觸發器負責根據給定的數據觸發適當的操作。
在實現過程中,我們創建了以下類:
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} }代碼看起來不錯,不是嗎? 好的,這是一個反問。 讓我們做些事情。
構造器與否?
在上面的示例中,我們有四個構造函數的類。 為什么那么多? 由于客戶需求的變化。 這很好。 一個應用程序應該滿足客戶的需求。
問題出在哪里? 問題在于類的設計。
為什么我們有這么多構造函數? 由于某些依賴項是可選的,因此它們的存在取決于外部條件。
我們需要這么多構造函數嗎?
在回答這個問題之前,最好先問一個不同的問題: 構造函數的目的是什么?
我們應該創建一個處于有效狀態的對象。 如果需要做更多的事情來使對象可用,我們就不應創建實例。 這就是為什么所有必需的依賴項都應放在構造函數中的原因 。
另一方面, 我們應僅將所需的依賴項放在構造函數中 。 構造函數不是放置任何可選內容的地方。 如果某些東西是可選的,則意味著我們不需要它來創建有效的對象。
如果我們想使用其他很好的依賴項,則應該以其他方式注入它們。 這就是二傳手的角色。 我們沒有被迫調用setter方法。 我們可能有需要,但這不是必需的。 當依賴項為選項時,應使用setter 。
那么,我們需要那么多構造函數嗎? 讓代碼作為答案:
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} }更少的代碼,更具描述性。 從第一刻起,您就知道需要什么以及可以使用什么。
塞特犬?
我不喜歡二傳手。 為什么? 因為這些方法以某種方式破壞了封裝 。
但是,我們可以用什么代替二傳手? 在給定的示例中可以代替使用什么?
好吧,我們不會避免使用這些方法。 或更確切地說,我們需要它們的功能。 需要讓客戶啟用該功能。 在給定的示例中,因為需要變量,所以需要保留它們。 但是,我們總是可以使代碼更好。 與域更多相關。 怎么樣? 我們只需要顯示與域的這種關系:
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} }我寫道,我不喜歡setter,因為它們的中斷封裝,但這不僅與方法的功能本身有關。 使用諸如setX之類的方法的另一個問題是,即使它們的名稱也是面向實現的。 有時,setter功能是必需的。 但是,請記住以一種顯示域含義的方式來命名方法。
太多選擇
有時,太多的選擇也會帶來問題。 這可能表明您違反了“ 單一責任原則” 。
如果選擇太多,可能意味著責任過多,值得重新考慮當前的解決方案。
每當在類的代碼中添加另一個可選部分時,都要非常小心。 也許這堂課做得太多了?
字尾
希望您覺得這篇文章有用。
現在,您應該知道應該只在構造函數中放置必需的依賴項。 任何可選的依賴項都需要其他命名良好的方法。
下一步是什么?
我們去創建一些對象:)
翻譯自: https://www.javacodegeeks.com/2016/02/constructor-or-setter.html
總結
                            
                        - 上一篇: TCMalloc 简介
 - 下一篇: JUnit 5 –架构