使用FunctionInterface实现业务断言 Fluent-api

383 阅读2分钟

在实现业务代码的时候, 通常会使用断言来做一些业务数据的断言,完成对业务数据的校验.

比如:  

public class Assertion {

      public static void isTrue(Boolean value, PayMessage payMessage) {
        if (BooleanUtils.isNotTrue(value)) {
            throw PayException.of(payMessage, payMessage.getMessage());
        }
    }

    public static void isTrue(Boolean value, PayMessage payMessage, String message) {
        if (BooleanUtils.isNotTrue(value)) {
            throw PayException.of(payMessage, message);
        }
    }

}

使用 : Assertion.isTrue(Objects.isNonNull(order.getAmount), PayMessage.AMOUNT_NOT_NULL, "订单金额不能为空")

这里的PayMessage是业务异常枚举类, "订单金额不为空"是自定义异常信息

这种方式的断言将条件的判断和异常信息的抛出都放在了一个语义里面. 这种实现方式是一种实现断言的方法, 但是在Java 8 FunctionInterface中可以尝试一种更清晰的语义调用: 

Assertion.condition()         
         .throwMessage()

这种方式的断言实现将断言的条件和断言的异常抛出的语义分开了, 更加明确. 

FunctionInterface断言实现:

public class Assertion {

      public static void isTrue(Boolean value, PayMessage payMessage) {
        if (BooleanUtils.isNotTrue(value)) {
            throw PayException.of(payMessage, payMessage.getMessage());
        }
    }

    public static void isTrue(Boolean value, PayMessage payMessage, String message) {
        if (BooleanUtils.isNotTrue(value)) {
            throw PayException.of(payMessage, message);
        }
    }


     public static Throw assertTrue(boolean condition) {
        // 提供连贯式(fluent api)访问
        return (message, msg) -> {
            if (msg == null) {
                // 为什么不直接抛出exception,而是调用命令-查询式接口:
                isTrue(condition, message);
            } else {
                isTrue(condition, message, msg);
            }
        };
    }

    public static Throw assertFalse(boolean condition) {
        // 提供连贯式(fluent api)访问
        return assertTrue(!condition);
    }

    @FunctionInterface
    public interface Throw {
        void throwMessage(PayMessage payMessage, String message);

        default void throwMessage(PayMessage payMessage) {
            throwMessage(payMessage, null);
        }
    }
}

通过引入Java 8 的FunctionInterface就可以实现断言的Fluent-API:

Assertion.assertTrue(StringUtils.isEmpty("")) 
         .throwMessage(PayMessage.WRONG_DATA);

如果有需要, 可以提供

Assertion.assertTrue(BooleanSupplier)
         .throwMessage(message)

的重载.

这里只是实现了对指定异常(PayException)的的断言改造,如果想要抛出任意类型的异常又需要怎么实现?

public class Assertion {
    
    public static Throws assertTrue(BooleanSupplier booleanSupplier) {
        return new Throws(booleanSupplier);
    }


    public static class Throws {

        private final BooleanSupplier booleanSupplier;

        public Throws(BooleanSupplier booleanSupplier) {
            this.booleanSupplier = booleanSupplier;
        }

        public <X extends Throwable> void thenThrow(Supplier<X> exceptionSupplier) {
            if (booleanSupplier.getAsBoolean()) {
                exceptionSupplier.get();
            }
        }
    }

}

最终的调用链就是这样的:  可以选择抛出不同的Exception

    @Test(expected = PayException.class)
    public void testAssertThrows() {
        PayAssertion.assertTrue(() -> StringUtils.isEmpty(""))
            .thenThrow(() -> {
                throw new PayException(PayMessage.WRONG_DATA);
            });
    }


    @Test(expected = IllegalArgumentException.class)
    public void testAssertThrowsIllegal() {
   
        PayAssertion.assertTrue(() -> StringUtils.isEmpty(""))
            .thenThrow(() -> {
                throw new IllegalArgumentException("不能为空");
            });
    }

Reference:

1. Fluent-API  www.martinfowler.com/bliki/Fluen…