Java 组合

简要概括我尝试了Java的组合模式。

结论是,函数式(组合模式)的代码可读性更高,但会消耗更多内存。速度几乎没有任何变化。

请参考

    • Combinator Pattern in Java 8

 

    • Javaラムダ式:コンビネーターパターンの改善

 

    functional programming

做法 (zuò fǎ)

程序型

    public ValidationResult imperativeValidate(Parson parson) {
        var result = ImperativeValidator.isValidName(parson.getName());
        if (result != ValidationResult.SUCCESS) {
            return result;
        }
        result = ImperativeValidator.isValidAddr(parson.getAddr());
        if (result != ValidationResult.SUCCESS) {
            return result;
        }
        return ImperativeValidator.isValidAge(parson.getAge());
    }

编程语言中的函数式

    public ValidationResult functionalValidate(Parson p) {
        return CombinatorPatternValidator.isValidName()
                .and(isValidAddr())
                .and(isValidAge())
                .apply(p);
    }

结果 – jié guǒ

JVM 堆内存函数型的大小是124 KB,而过程型的大小是66 KB。

[QUICK PERF] Measured heap allocation (test method thread): 124.14 Kilo bytes (127 120 bytes)
[QUICK PERF] Measured heap allocation (test method thread): 66.49 Kilo bytes (68 088 bytes)

性能测试我试了几次,但结果感觉并没有太大的变化,函数式的方式稍微慢一些。
(可能是由于验证内容的原因)

Benchmark                                   Mode  Cnt       Score      Error  Units
CombinatorPatternBenchmarkTest.functional  thrpt    5  859447.226 ± 5632.616  ops/s
CombinatorPatternBenchmarkTest.imperative  thrpt    5  865311.952 ± 4481.358  ops/s

附錄

整个代码

数据

@AllArgsConstructor
@Data
public class Parson {
    private String name;
    private int age;
    private String addr;
    private String addr2;
}

以下是英文句子的中文释义:
枚举

public enum ValidationResult {
    SUCCESS,
    HOGE_CANNOT_BE_USED_IN_NAME,
    ADDR_IS_TOO_LONG,
    IS_A_CHILD,
}

办理程序验证器

public class ImperativeValidator {
    public static ValidationResult isValidName(String name) {
        return name.contains("hoge") ? HOGE_CANNOT_BE_USED_IN_NAME : SUCCESS;
    }

    public static ValidationResult isValidAddr(String addr) {
        return addr.length() > 100 ? ADDR_IS_TOO_LONG : SUCCESS;
    }

    public static ValidationResult isValidAge(int age) {
        return age <= 18 ? IS_A_CHILD : SUCCESS;
    }
}

函数式 (组合器模式) 验证器

interface CombinatorPatternValidator extends Function<Parson, ValidationResult> {

    static CombinatorPatternValidator isValidName() {
        return p -> p.getName().contains("hoge") ? HOGE_CANNOT_BE_USED_IN_NAME : SUCCESS;
    }

    static CombinatorPatternValidator isValidAddr() {
        return p -> p.getAddr().length() > 100 ? ADDR_IS_TOO_LONG : SUCCESS;
    }

    static CombinatorPatternValidator isValidAge() {
        return p -> p.getAge() <= 18 ? IS_A_CHILD : SUCCESS;
    }

    default CombinatorPatternValidator and(CombinatorPatternValidator other) {
        return p -> {
            var result = this.apply(p);
            return result.equals(SUCCESS) ? other.apply(p) : result;
        };
    }
}

验证服务

public class ValidationService {

    public ValidationResult imperativeValidate(Parson parson) {
        var result = ImperativeValidator.isValidName(parson.getName());
        if (result != ValidationResult.SUCCESS) {
            return result;
        }
        result = ImperativeValidator.isValidAddr(parson.getAddr());
        if (result != ValidationResult.SUCCESS) {
            return result;
        }
        return ImperativeValidator.isValidAge(parson.getAge());
    }

    public ValidationResult functionalValidate(Parson p) {
        return CombinatorPatternValidator.isValidName()
                .and(isValidAddr())
                .and(isValidAge())
                .apply(p);
    }
}

测试代码

堆栈测量
请参考Java中的堆大小测量JUnit5部分。

@QuickPerfTest
public class CombinatorPatternTest {

    ValidationService service = new ValidationService();

    @MeasureHeapAllocation
    @Test
    void imperative() {
        service.imperativeValidate(create());
    }

    @MeasureHeapAllocation
    @Test
    void functional() {
        service.functionalValidate(create());
    }

    Parson create() {
        return new Parson(RandomStringUtils.randomAlphabetic(10), new Random().nextInt(100), RandomStringUtils.randomAlphabetic(20), RandomStringUtils.randomAlphabetic(20));
    }
}

基准测试
参考にするJava性能測定ベンチマークのJUnit5編を使用ください。

@State(value = Scope.Benchmark)
public class CombinatorPatternBenchmarkTest {

    ValidationService service = new ValidationService();

    @Benchmark
    public void imperative() {
        service.imperativeValidate(create());
    }

    @Benchmark
    public void functional() {
        service.functionalValidate(create());
    }

    @Test
    void benchMark() throws RunnerException {
        Options opt = new OptionsBuilder()
                .include(CombinatorPatternBenchmarkTest.class.getSimpleName())
                .forks(1) // 1回実行
                .warmupIterations(1) // 1回繰り返し
                .build();
        new Runner(opt).run();
    }

    Parson create() {
        return new Parson(RandomStringUtils.randomAlphabetic(10), new Random().nextInt(100), RandomStringUtils.randomAlphabetic(20), RandomStringUtils.randomAlphabetic(20));
    }
}

bannerAds