建设者还是二传手?
不用說,每個(gè)對象都需要先創(chuàng)建才能使用。 無論我們是在談?wù)撚?#xff0c;框架,庫還是任何其他類型的類,都沒有關(guān)系。 當(dāng)您的代碼是面向?qū)ο蟮臅r(shí),這些類僅是對象的定義。 在創(chuàng)建對象之前,不能使用它們。
在談?wù)搶ο蟮某跏蓟瘯r(shí),我們經(jīng)常需要考慮依賴關(guān)系。 您將如何注入它們? 您會(huì)使用構(gòu)造函數(shù)還是二傳手?
讓我來幫助您做出正確的決定。
很久以前..
……需要處理一些事件。 為此,我們必須首先從存儲庫中檢索必要的數(shù)據(jù),然后將其傳遞給觸發(fā)器,該觸發(fā)器負(fù)責(zé)根據(jù)給定的數(shù)據(jù)觸發(fā)適當(dāng)?shù)牟僮鳌?
在實(shí)現(xiàn)過程中,我們創(chuàng)建了以下類:
public class SomeHandler {public SomeHandler(Repository repository, Trigger trigger) {// some code}public void handle(SomeEvent event) {// some code} }事情總是在變化。 我們的客戶告訴我們,他們有時(shí)會(huì)需要存儲從存儲庫中檢索到的一些信息,然后才能采取適當(dāng)?shù)拇胧?他們需要此數(shù)據(jù)用于統(tǒng)計(jì)目的和進(jìn)一步分析。
更改后,這是我們班級的樣子:
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} }又過了一個(gè)月,客戶提出了另一個(gè)要求。 他們希望有可能在觸發(fā)事件后立即啟用通知。 對于某些緊急事件,這對于他們來說是必要的。 他們希望具有更高的透明度。
好的,現(xiàn)在我們可以啟用兩件事:
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} }代碼看起來不錯(cuò),不是嗎? 好的,這是一個(gè)反問。 讓我們做些事情。
構(gòu)造器與否?
在上面的示例中,我們有四個(gè)構(gòu)造函數(shù)的類。 為什么那么多? 由于客戶需求的變化。 這很好。 一個(gè)應(yīng)用程序應(yīng)該滿足客戶的需求。
問題出在哪里? 問題在于類的設(shè)計(jì)。
為什么我們有這么多構(gòu)造函數(shù)? 由于某些依賴項(xiàng)是可選的,因此它們的存在取決于外部條件。
我們需要這么多構(gòu)造函數(shù)嗎?
在回答這個(gè)問題之前,最好先問一個(gè)不同的問題: 構(gòu)造函數(shù)的目的是什么?
我們應(yīng)該創(chuàng)建一個(gè)處于有效狀態(tài)的對象。 如果需要做更多的事情來使對象可用,我們就不應(yīng)創(chuàng)建實(shí)例。 這就是為什么所有必需的依賴項(xiàng)都應(yīng)放在構(gòu)造函數(shù)中的原因 。
另一方面, 我們應(yīng)僅將所需的依賴項(xiàng)放在構(gòu)造函數(shù)中 。 構(gòu)造函數(shù)不是放置任何可選內(nèi)容的地方。 如果某些東西是可選的,則意味著我們不需要它來創(chuàng)建有效的對象。
如果我們想使用其他很好的依賴項(xiàng),則應(yīng)該以其他方式注入它們。 這就是二傳手的角色。 我們沒有被迫調(diào)用setter方法。 我們可能有需要,但這不是必需的。 當(dāng)依賴項(xiàng)為選項(xiàng)時(shí),應(yīng)使用setter 。
那么,我們需要那么多構(gòu)造函數(shù)嗎? 讓代碼作為答案:
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} }更少的代碼,更具描述性。 從第一刻起,您就知道需要什么以及可以使用什么。
塞特犬?
我不喜歡二傳手。 為什么? 因?yàn)檫@些方法以某種方式破壞了封裝 。
但是,我們可以用什么代替二傳手? 在給定的示例中可以代替使用什么?
好吧,我們不會(huì)避免使用這些方法。 或更確切地說,我們需要它們的功能。 需要讓客戶啟用該功能。 在給定的示例中,因?yàn)樾枰兞?#xff0c;所以需要保留它們。 但是,我們總是可以使代碼更好。 與域更多相關(guān)。 怎么樣? 我們只需要顯示與域的這種關(guān)系:
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,因?yàn)樗鼈兊闹袛喾庋b,但這不僅與方法的功能本身有關(guān)。 使用諸如setX之類的方法的另一個(gè)問題是,即使它們的名稱也是面向?qū)崿F(xiàn)的。 有時(shí),setter功能是必需的。 但是,請記住以一種顯示域含義的方式來命名方法。
太多選擇
有時(shí),太多的選擇也會(huì)帶來問題。 這可能表明您違反了“ 單一責(zé)任原則” 。
如果選擇太多,可能意味著責(zé)任過多,值得重新考慮當(dāng)前的解決方案。
每當(dāng)在類的代碼中添加另一個(gè)可選部分時(shí),都要非常小心。 也許這堂課做得太多了?
字尾
希望您覺得這篇文章有用。
現(xiàn)在,您應(yīng)該知道應(yīng)該只在構(gòu)造函數(shù)中放置必需的依賴項(xiàng)。 任何可選的依賴項(xiàng)都需要其他命名良好的方法。
下一步是什么?
我們?nèi)?chuàng)建一些對象:)
翻譯自: https://www.javacodegeeks.com/2016/02/constructor-or-setter.html
總結(jié)
- 上一篇: TCMalloc 简介
- 下一篇: JUnit 5 –架构