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 -eaAlthough 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