這周我準備介紹一個有趣的可是不多使用的方法面試
按照合約編程,又稱爲合約編程,是一種軟件設計的方法。它規定了軟件設計師應該爲軟件組件定義正式,精確和可驗證的接口規範,將常規的抽象數據類型擴展爲前置條件,後置條件和不變量。這些規則被稱爲合約,能夠比擬爲商業合同中的條件和義務。
— Wikipedia
https://en.wikipedia.org/wiki...
本質上它使得計算儘快的由於錯誤而失敗。若是從假設條件開始就不知足,那麼沒有必要繼續運行代碼。編程
讓咱們使用兩個銀行之間的轉帳操做做爲例子說明。如下是一些條件:安全
前置條件:微信
不變量:框架
轉帳以後:工具
能夠手動實現前置條件後置條件:post
public void transfer(Account source, Account target, BigDecimal amount) { if (amount.compareTo(BigDecimal.ZERO) <= 0) { throw new IllegalArgument("Amount transferred must be higher than zero (" + amount + ")"; } if (source.getBalance().compareTo(BigDecimal.ZERO) <= 0) { throw new IllegalArgument("Source account balance must be higher than zero (" + source.getBalance() + ")"; } source.transfer(target, amount); if (source.getBalance().compareTo(BigDecimal.ZERO) <= 0) { throw new IllegalState("Source account balance must be higher than zero (" + source.getBalance() + ")"; } // Other post-conditions... }
寫起來很是麻煩,並且很難閱讀。ui
檢查不變式翻譯爲既檢查前提條件又檢查後置條件
你可能已經經過assert關鍵字熟悉了前置條件和後置條件:spa
public void transfer(Account source, Account target, BigDecimal amount) { assert (amount.compareTo(BigDecimal.ZERO) <= 0); assert (source.getBalance().compareTo(BigDecimal.ZERO) <= 0); source.transfer(target, amount); assert (source.getBalance().compareTo(BigDecimal.ZERO) <= 0); // Other post-conditions... }
Java語言實現有幾個問題:翻譯
-ea
標記啓動Oracle的文檔明確說明:
雖然assert構造不是一個完整的合約編程工具,但它能夠幫助支持非正式的按照合約設計的編程風格。
自從Java 8以後,Objects
類的三個方法提供了對合約式編程的部分支持:
最後一個方法中的
Supplier
參數返回錯誤信息
全部的3個方法都會在obj爲null的時候拋出NullPointerException
。更有意思的是,他們都會在obj不是null的時候返回該對象。從而致使瞭如下風格的代碼:
public void transfer(Account source, Account target, BigDecimal amount) { if (requireNonNull(amount).compareTo(BigDecimal.ZERO) <= 0) { throw new IllegalArgument("Amount transferred must be higher than zero (" + amount + ")"; } if (requireNonNull(source).getBalance().compareTo(BigDecimal.ZERO) <= 0) { throw new IllegalArgument("Source account balance must be higher than zero (" + source.getBalance() + ")"; } source.transfer(target, amount); if (requireNonNull(source).getBalance().compareTo(BigDecimal.ZERO) <= 0) { throw new IllegalState("Source account balance must be higher than zero (" + source.getBalance() + ")"; } // Other post-conditions... }
不只功能有限,並且並不能真正提升可讀性,特別是若是添加錯誤消息參數的時候。
Spring框架提供了Assert
類並支持大量的條件驗證方法。
根據咱們本身簡單的實現,前置條件不符合會拋出IllegalArgumentException
,然後置條件不符合會拋出IllegalStateException
。
維基百科頁面還列出了幾個專用於按合同進行編程的框架:
上面的框架大多數基於註解。
讓咱們從優勢開始:註釋使條件更加明顯。
而另外一方面,它們也有如下缺陷:
它們要麼:
@email
)Kotlin的合約編程基於簡單的方法調用,位於Preconditions.kt
文件中
require
類型的方法會判斷前置條件而且在不符合時拋出IllegalArgumentException
type
類型的方法會判斷後置條件而且在不符合時拋出IllegalStateException
使用Kotlin重寫後的方法以下:
fun transfer(source: Account, target: Account, amount: BigDecimal) { require(amount <= BigDecimal.ZERO) require(source.getBalance() <= BigDecimal.ZERO) source.transfer(target, amount); check(source.getBalance() <= BigDecimal.ZERO) // Other post-conditions... }
在一般狀況下,越簡單越好。經過將檢查和異常拋出指令包裝到方法中,人們能夠很容易地實現合約式編程。儘管在Java中沒有這種即拆即用的封裝,valid4j和Kotlin都提供了這種實現。
想要了解更多開發技術,面試教程以及互聯網公司內推,歡迎關注個人微信公衆號!將會不按期的發放福利哦~