Contract design, also known as contract programming, is an approach to software development. It requires software developers to define formal, accurate, and proven interface specifications for software components that extend the usual definition of abstract data types with preconditions, postconditions, and invariants. These specifications are called "contracts", in accordance with the conceptual metaphor with the terms and obligations of business contracts.
Wikipedia
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... }
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... }
-ea
start -ea
Although the assert construct is not a complete contract construct, it can help support informal contract programming style.
Objects
class offers three methods that impose restrictions on contract programming: public static <T> T requireNonNull(T obj)
public static <T> T requireNonNull(T obj, String message)
public static <T> T requireNonNull(T obj, Supplier<String> messageSupplier)
The Supplier
argument in the last method returns an error message.
All 3 methods throw a NullPointerException
if obj
is null
.obj
not null
. This leads to the following code view: 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... }
Assert
class, which offers a variety of state checking methods:IllegalArgumentException
if the condition is not met, while checks after the state throw an IllegalStateException
exception.Preconditions.kt:
file Preconditions.kt:
require
methods implement preconditions, and if not, then IllegalArgumentException
will be thrown.check
methods implement post-conditions, and if not, then IllegalStateException
will be thrown IllegalStateException
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... }
Source: https://habr.com/ru/post/352672/
All Articles