instanceof_您真的需要instanceof吗?
instanceof
使用instanceof是一種代碼味道。 我認(rèn)為我們可能對(duì)此表示同意。 每當(dāng)我看到這樣的構(gòu)造時(shí),我都會(huì)確定出現(xiàn)了問(wèn)題。 也許有人只是在進(jìn)行更改時(shí)沒(méi)有注意到問(wèn)題? 也許有一個(gè)主意,但是它太復(fù)雜了,以至于需要太多的精力或時(shí)間才能讓開(kāi)發(fā)人員決定不做呢? 也許只是懶惰? 誰(shuí)知道。 事實(shí)仍然是代碼演變成這種狀態(tài),我們必須與之合作。
或者也許我們可以做些什么? 有什么可以打開(kāi)我們的擴(kuò)展代碼嗎?
今天,我想向您展示如何實(shí)現(xiàn)這一目標(biāo)。 但是首先,讓我解釋一下為什么這個(gè)instanceof根本是個(gè)問(wèn)題。
看一下代碼
今天我們將討論以下代碼:
public class ChangeProcessingHandler {public CodeDelta triggerProcessingChangeOf(Code code, Change change) {verifyChangeOf(code, change);if (change instanceof Refactoring) {return processRefactoring(code, (Refactoring) change);} else if (change instanceof Improvement) {return processImprovement(code, (Improvement) change);} else if (change instanceof Growth) {return processGrowth(code, (Growth) change);} else {throw new UnsuportedChangeException();}}// some more code }我們將嘗試對(duì)其進(jìn)行改進(jìn)。
我試圖使這段代碼具有描述性,但是讓我簡(jiǎn)要地總結(jié)一下。 根據(jù)Change接口實(shí)現(xiàn)的特定類型,我們選擇一種準(zhǔn)確的處理方式。 如果找不到匹配的類型,我們將拋出一個(gè)異常。
現(xiàn)在,讓我們看一下這段代碼有什么問(wèn)題。
接口及其實(shí)現(xiàn)?
當(dāng)您查看方法的聲明時(shí),您能怎么說(shuō)呢? 確實(shí)需要兩個(gè)輸入?yún)?shù)。 它給我們什么樣的信息? 我們知道依賴關(guān)系,并基于它們的API,我們知道如何在方法主體中與傳遞的對(duì)象進(jìn)行交互。
在給定的例子中是真的嗎? 不幸的是沒(méi)有。 我們正在傳遞一個(gè)Change實(shí)例,我們希望方法的主體將取決于其接口。 但是在內(nèi)部,我們將實(shí)例轉(zhuǎn)換為特定類型,這導(dǎo)致依賴性增加。
這本身不是一個(gè)好的設(shè)計(jì)決策,但更糟糕的是–我們?cè)谀缓笤黾恿诉@個(gè)數(shù)字。 直到您不閱讀方法的主體,您都不會(huì)知道。
缺乏知識(shí)遠(yuǎn)比依賴數(shù)量嚴(yán)重得多。
新類型不是那么容易添加
假設(shè)您必須添加Change接口的新實(shí)現(xiàn)。 會(huì)發(fā)生什么? 好吧,什么都沒(méi)有。 您將添加類定義并對(duì)其進(jìn)行測(cè)試。 您將運(yùn)行所有測(cè)試。 如果至少有一個(gè)組件或系統(tǒng)測(cè)試能夠通過(guò)新引入的Change接口實(shí)現(xiàn)達(dá)到所提供的代碼并且失敗,您將很幸運(yùn)。
當(dāng)沒(méi)有這樣的測(cè)試時(shí),問(wèn)題就開(kāi)始了,您甚至都不知道應(yīng)該改變某個(gè)地方以適應(yīng)新功能。
一切都會(huì)編譯,您將一直工作到……
為什么?
您在代碼中注意到了這個(gè)不錯(cuò)的UnsupportedChangeException嗎? 老實(shí)說(shuō),它在那里只是因?yàn)樵O(shè)計(jì)錯(cuò)誤。
我們擁有它有兩個(gè)原因:
- 沒(méi)有它,代碼將無(wú)法編譯。 當(dāng)然,如果方法無(wú)效,我們可以跳過(guò)它,但是在我們的示例中,我們必須返回或拋出一些東西。 我們可以用其他方法代替last if-else,但這不是我們想要做的。
- 它使我們無(wú)法添加新類型,而忘記在其中添加對(duì)新引入功能的支持。 假設(shè)至少有一種測(cè)試會(huì)在這種情況下失敗。
為什么我稱其為錯(cuò)誤的設(shè)計(jì)? 很好,使用異常來(lái)表示需要支持新功能,這是對(duì)異常的濫用。 我還相信,如果我們的代碼通過(guò)不編譯來(lái)發(fā)出信號(hào),那會(huì)更好。 這對(duì)我來(lái)說(shuō)很有意義,而且肯定會(huì)提供更快的反饋。
來(lái)訪者進(jìn)行搶救!
訪問(wèn)者允許我們添加其他功能,其實(shí)現(xiàn)取決于對(duì)象的特定類型。 它允許使用接口的方法。 因此,我們可以避免自己檢索有關(guān)特定接口的實(shí)現(xiàn)的信息。
首先,我們需要使檢索有關(guān)對(duì)象類型的信息成為可能。 為此,我們必須在接口中添加一種方法,該方法將允許我們傳遞訪問(wèn)者:
public interface Change {void accept(Visitator visitator); }實(shí)現(xiàn)接口的每個(gè)對(duì)象的實(shí)現(xiàn)非常簡(jiǎn)單:
public class Refactoring implements Change {@Overridepublic void accept(Visitator visitator) {visitator.visit(this);}// some code }通過(guò)查看調(diào)用方法visit()的行,我們可以觀察到什么? 這是檢索有關(guān)類型的信息的地方。 不需要instanceof,也不需要強(qiáng)制轉(zhuǎn)換。 這是我們?cè)诟玫脑O(shè)計(jì)支持下免費(fèi)獲得的東西。
這時(shí),您可能知道Visitor的界面如下所示:
public interface Visitator {void visit(Refactoring refactoring);void visit(Improvement improvement);void visit(Growth growth); }不是那么復(fù)雜,不是嗎?
之后,我們必須從ChangeProcessingHandler類中提取一些代碼到實(shí)現(xiàn)Visitor接口的類中:
public class ChangeProcessor implements Visitator {private final Code code;public ChangeProcessor(Code code) {this.code = code;}@Overridepublic void visit(Refactoring refactoring) {// some code}@Overridepublic void visit(Improvement improvement) {// some code}@Overridepublic void visit(Growth growth) {// some code} }當(dāng)然,我們必須在正確的位置使用它:
public class ChangeProcessingHandlerRefactored {public void triggerProcessingChangeOf(Code code, Change change) {verifyChangeOf(code, change);change.accept(new ChangeProcessor(code));} }是不是更好?
好的,所以我們更改了原始代碼。 現(xiàn)在讓我解釋一下我們獲得了什么。
- 我們剛剛擺脫了一個(gè)例外。 不再需要它,因?yàn)閷?duì)新引入的實(shí)現(xiàn)的必要支持將通過(guò)非編譯代碼發(fā)出信號(hào)。
- 快速反饋是使用接口的結(jié)果,該接口將告訴我們要完全支持所有功能還需要實(shí)現(xiàn)什么。
- 單一責(zé)任原則之所以起作用,是因?yàn)椤霸L客”界面的每個(gè)特定實(shí)現(xiàn)僅負(fù)責(zé)一項(xiàng)功能。
- 設(shè)計(jì)是面向行為的(接口),而不是面向?qū)崿F(xiàn)的(instanceof + cast)。 這樣,我們隱藏了實(shí)現(xiàn)細(xì)節(jié)。
- 設(shè)計(jì)是開(kāi)放的擴(kuò)展。 引入易于實(shí)現(xiàn)的新功能真的很容易,這些功能針對(duì)特定對(duì)象而有所不同。
它不是那么完美
每個(gè)設(shè)計(jì)都是一個(gè)權(quán)衡。 您得到了一些東西,但這是有代價(jià)的。
我在上段中列出了收益,那么成本呢?
- 這么多的對(duì)象
可能有人說(shuō)這是使用任何設(shè)計(jì)模式的明顯結(jié)果,我會(huì)說(shuō)是的。 但是,這并不能改變以下事實(shí):隨著對(duì)象數(shù)量的增加,在對(duì)象之間導(dǎo)航變得更加困難。
將所有內(nèi)容都放在一個(gè)對(duì)象中可能是一個(gè)問(wèn)題,但是名稱不正確或雜亂無(wú)章的類可能會(huì)導(dǎo)致混亂。 - 復(fù)雜
所有這些對(duì)象都需要一個(gè)名稱,如果這些對(duì)象與域相關(guān),那就太好了。 在這種情況下,我們最終會(huì)對(duì)我們的應(yīng)用程序有了更好的了解。 但這并非總是如此。
同樣,我們?cè)诿乱氲念悤r(shí)也必須非常小心。 所有這些都必須以不言自明的方式命名。 這并不像某些人想象的那么容易。 - 我的(受限)上下文在哪里?
訪客可以幫助解決與示例中出現(xiàn)的問(wèn)題類似的問(wèn)題。 但是,如果有很多類似的地方,您必須意識(shí)到每個(gè)訪問(wèn)者都在某種程度上將對(duì)象的行為放入另一個(gè)對(duì)象。 得墨meter耳定律呢? 那告訴泰勒,不要問(wèn)嗎?
在使用訪客解決instanceof問(wèn)題之前,您應(yīng)該問(wèn)自己這個(gè)功能是否不是對(duì)象本身的一部分? 一些開(kāi)發(fā)人員向我解釋這是擁有小對(duì)象的一種方式。 好吧,對(duì)我來(lái)說(shuō),這樣的解釋證明了我們應(yīng)該考慮綁定上下文 。 對(duì)象仍然很小,它們的行為也不會(huì)泄漏給外部類。
就這樣,伙計(jì)們
今天就這些。 我希望您發(fā)現(xiàn)重新設(shè)計(jì)的想法很有用,并且在閱讀本文之后,您的代碼中的異味肯定會(huì)受到威脅。 與往常一樣,我鼓勵(lì)您發(fā)表評(píng)論并分享您的觀點(diǎn)和經(jīng)驗(yàn)。 也許您更多地了解與此類變更相關(guān)的收益/問(wèn)題。
翻譯自: https://www.javacodegeeks.com/2016/10/really-need-instanceof.html
instanceof
總結(jié)
以上是生活随笔為你收集整理的instanceof_您真的需要instanceof吗?的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 华为MatePad Pro 13.2英寸
- 下一篇: 诸多黑科技傍身的华为智能眼镜2,这款智慧