摘要:前言這周我準備介紹一個有趣的但是很少使用的方法按照合約編程,又稱為合約編程,是一種軟件設計的方法。這些規則被稱為合約,可以比擬為商業合同中的條件和義務。通過將檢查和異常拋出指令包裝到方法中,人們可以很容易地實現合約式編程。
前言
這周我準備介紹一個有趣的但是很少使用的方法
按照合約編程,又稱為合約編程,是一種軟件設計的方法。它規定了軟件設計師應該為軟件組件定義正式,精確和可驗證的接口規范,將常規的抽象數據類型擴展為前置條件,后置條件和不變量。這些規則被稱為合約,可以比擬為商業合同中的條件和義務。
— Wikipedia
https://en.wikipedia.org/wiki...
本質上它使得計算盡快的因為錯誤而失敗。如果從假設條件開始就不滿足,那么沒有必要繼續運行代碼。
讓我們使用兩個銀行之間的轉賬操作作為例子說明。以下是一些條件:
前置條件:
轉賬的數額必須大于0
不變量:
轉出的銀行賬號的余額必須為正
轉賬之后:
源銀行賬戶余額必須等于初始余額減去轉賬金額
目標銀行賬戶余額必須等于初始余額加轉移金額
簡單的實現可以手動實現前置條件后置條件:
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... }
寫起來非常麻煩,而且很難閱讀。
檢查不變式翻譯為既檢查前提條件又檢查后置條件Java語言實現
你可能已經通過assert關鍵字熟悉了前置條件和后置條件:
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語言實現
自從Java 8之后,Objects類的三個方法提供了對合約式編程的部分支持:
public static
public static
public static
最后一個方法中的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。
維基百科頁面還列出了幾個專用于按合同進行編程的框架:
OVal
Contracts for Java
Java Modeling Language
Bean Validation
valid4j
上面的框架大多數基于注解。
注解的優點和缺點讓我們從優點開始:注釋使條件更加明顯。
而另一方面,它們也有以下缺陷:
它們需要在編譯時或運行時進行字節碼操作
它們要么:
范圍有限(比如@email)
或者委托給一個外部的語言,該語言被配置為注釋字符串屬性,違背了類型安全
Kotlin的方法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都提供了這種實現。
想要了解更多開發技術,面試教程以及互聯網公司內推,歡迎關注我的微信公眾號!將會不定期的發放福利哦~
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/68749.html
摘要:什么是為執行字節碼提供一個運行環境。它的實現主要包含三個部分,描述實現規格的文檔,具體實現和滿足要求的計算機程序以及實例具體執行字節碼。該類先被轉化為一組字節碼并放入文件中。字節碼校驗器通過字節碼校驗器檢查格式并找出非法代碼。 什么是Java Development Kit (JDK)? JDK通常用來開發Java應用和插件?;旧峡梢哉J為是一個軟件開發環境。JDK包含Java Run...
摘要:它們是通過來自遠程的服務器的連接發送字節碼并在本地運行,這一點令人興奮。中有一個自定義的,它不是從本地文件系統加載類文件,而是從遠程服務器上獲取,通過加載原始字節碼,再在中轉化為類。它將字節碼解析為運行時的數據結構,檢查其有效性等。 前言 Java ClassLoader是java運行系統中一個至關重要但是經常被忽略的組件。它負責在運行時尋找并加載類文件。創建自定義的ClassLoad...
摘要:無需檢查的異常也是的子類。從低層拋出的需檢查異常強制要求調用方捕獲或是拋出該異常。當前執行的線程將會停止并報告該異常。單元測試允許我在使用中查看異常,并且作為一個可以被執行的文檔來使用。不要捕獲最高層異常繼承的異常同樣是的子類。 前言 異常處理的問題之一是知道何時以及如何去使用它。我會討論一些異常處理的最佳實踐,也會總結最近在異常處理上的一些爭論。 作為程序員,我們想要寫高質量的能夠解...
摘要:有可能一個線程中的動作相對于另一個線程出現亂序。當實際輸出取決于線程交錯的結果時,這種情況被稱為競爭條件。這里的問題在于代碼塊不是原子性的,而且實例的變化對別的線程不可見。這種不能同時在多個線程上執行的部分被稱為關鍵部分。 為什么要額外寫一篇文章來研究volatile呢?是因為這可能是并發中最令人困惑以及最被誤解的結構。我看過不少解釋volatile的博客,但是大多數要么不完整,要么難...
摘要:簡介從創建以來,就支持核心的并發概念如線程和鎖。這篇文章會幫助從事多線程編程的開發人員理解核心的并發概念以及如何使用它們。請求操作系統互斥,并讓操作系統調度程序處理線程停放和喚醒。 簡介 從創建以來,JAVA就支持核心的并發概念如線程和鎖。這篇文章會幫助從事多線程編程的JAVA開發人員理解核心的并發概念以及如何使用它們。 (博主將在其中加上自己的理解以及自己想出的例子作為補充) 概念 ...
閱讀 4293·2021-09-24 09:47
閱讀 1184·2021-09-03 10:33
閱讀 2063·2019-08-30 11:13
閱讀 1029·2019-08-30 10:49
閱讀 1753·2019-08-29 16:13
閱讀 2046·2019-08-29 11:28
閱讀 3089·2019-08-26 13:31
閱讀 3631·2019-08-23 17:14