What is Fluent API?

举个 jOOQ 的例子,一目了然

  ctx.select(BOOK.ID, BOOK.NAME)
     .from(BOOK)
     .where(BOOK.AUTHOR.eq(0))
     .fetch();

image.png

Fluent API的设计方法,非常适合作为复杂对象的 Builder。构造过程流程,指引性良好,而且能够保证有序。

流式调用也可以用于构建DSL(Domain-Specific Language),用于解决特定领域问题。

更复杂的 Demo,可以参考 COLA 的 StateMachinejOOQ 的实现。

案例:封装 CompletableFuture

首先让我们看一个使用 CompletableFuture 的案例。下面这个例子是异步获取工资和年龄。这个方法内使用过多 try catch,跟 if else 过多一样,使得代码结构散乱难以理解。

    @Test
    void tooMuchTryCatch() {
        CompletableFuture<Integer> salaryFuture = CompletableFuture.supplyAsync(() -> 1000);
        CompletableFuture<Integer> ageFuture = CompletableFuture.supplyAsync(() -> 30);

        Integer salary = null;
        try {
            salary = salaryFuture.get();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        assertEquals(salary, 1000);

        Integer age = null;
        try {
            age = ageFuture.get();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        assertEquals(age, 30);
    }

这是一个来自我们项目中的场景:同时调用多个接口,如果接口异常则返回null或一个兜底值。像这样的调用模式如果经常在项目中出现,那么我们认为这是一个特定的领域问题,应该考虑使用一个DSL解法。

为了避免方法内使用过多的 try catch,我们可以使用 CompletableFuture.exceptionally()。当异步调用发生错误时,我们经常只需要打印一条日志,再返回一个默认值。因此,我们可以封装个工具来规范化 Future 的构造。

接下来开始表演。首先定义API,有三个step:FirstSecondFinal

graph TD;

First --"onFailure"--> Final;
First --fallback--> Final;
First --errMsg--> Second;
Second --fallback--> Final;
Final --run--> CompletableFuture
public interface Async { }

public interface AsyncContext {
    <T> FirstStep<T> async(Supplier<T> supplier);
}

public interface FinalStep<T> extends Async {
    CompletableFuture<T> run();
}

public interface FirstStep<T> extends FinalStep<T> {
    SecondStep<T> errMsg(String errMsg);
    FinalStep<T> fallback(T fallback);
    FinalStep<T> onFailure(Function<Throwable, ? extends T> onFailure);
}

public interface SecondStep<T> extends FinalStep<T> {
    FinalStep<T> fallback(T fallback);
}

然后是具体实现:

@Slf4j
public class AsyncImpl<T> implements FirstStep<T>, SecondStep<T> {
    private Supplier<T> supplier;
    private Function<Throwable, ? extends T> onFailure;
    private String errMsg = "异步执行异常";
    private T fallback;

    private final Executor executor;


    public AsyncImpl(Supplier<T> supplier, Executor executor) {
        this.supplier = supplier;
        this.executor = executor;
    }

    @Override
    public SecondStep<T> errMsg(String errMsg) {
        this.errMsg = errMsg;
        return this;
    }

    @Override
    public FinalStep<T> fallback(T fallback) {
        this.fallback = fallback;
        return this;
    }

    @Override
    public FinalStep<T> onFailure(Function<Throwable, ? extends T> onFailure) {
        this.onFailure = onFailure;
        return this;
    }

    @Override
    public CompletableFuture<T> run() {
        if (onFailure == null) {
            onFailure = ex -> {
                log.warn("{}", errMsg, ex);
                return fallback;
            };
        }
        CompletableFuture<T> future = executor == null ?
                CompletableFuture.supplyAsync(supplier) :
                CompletableFuture.supplyAsync(supplier, executor);
        return future.exceptionally(onFailure);
    }
}
@Component
@RequiredArgsConstructor
public class AsyncContextImpl implements AsyncContext {

    private final Executor executor;

    @Override
    public <T> FirstStep<T> async(Supplier<T> supplier) {
        return new AsyncImpl<>(supplier, executor);
    }
}

最后写个单元测试。这个单元测试介绍了这个 fluent api 的用法,无需多言。

class AsyncContextTest {

    private AsyncContext asyncCtx;

    @BeforeEach
    void setup() {
        asyncCtx = new AsyncContextImpl(null);
    }

    private Integer fuckUpTask() {
        throw new RuntimeException("FUCK UP");
    }

    @Test
    void defaultFail() {
        CompletableFuture<Integer> future = asyncCtx.async(this::fuckUpTask)
                .run();
        assertNull(future.join());
    }

    @Test
    void onFailure() {
        CompletableFuture<Integer> future = asyncCtx.async(this::fuckUpTask)
                .onFailure(ex -> -1)
                .run();
        assertEquals(future.join(), -1);
    }

    @Test
    void errMsg() {
        CompletableFuture<Integer> future = asyncCtx.async(this::fuckUpTask)
                .errMsg("FUCK UP")
                .fallback(-1)
                .run();
        assertEquals(future.join(), -1);
    }

    @Test
    void multiTasks() {
        CompletableFuture<Integer> failFuture = asyncCtx.async(this::fuckUpTask)
                .fallback(-1)
                .run();
        CompletableFuture<Integer> successFuture = asyncCtx.async(() -> 1)
                .run();
        assertEquals(failFuture.join(), -1);
        assertEquals(successFuture.join(), 1);
    }
}

Kotlin version

最近学习了一下 kotlin,顺手就写个了 kotlin 的版本。

api.kt

interface Async

interface FinalStep<T>: Async {
    fun run(): CompletableFuture<T>
}

interface FirstStep<T>: Async, FinalStep<T> {
    fun onFailure(x: (Throwable) -> T): FinalStep<T>
    fun errMsg(msg: String): SecondStep<T>
    fun fallback(back: T): FinalStep<T>
}

interface SecondStep<T>: Async, FinalStep<T> {
    fun fallback(back: T): FinalStep<T>
}

interface AsyncContext {
    fun <T> async(x: () -> T): FirstStep<T>
}

impl.kt

import java.util.concurrent.CompletableFuture
import java.util.concurrent.Executor

class AsyncContextImpl(private val executor: Executor? = null) : AsyncContext {
    override fun <T> async(x: () -> T): FirstStep<T> {
        return AsyncImpl(x, executor)
    }
}

class AsyncImpl<T>(private val supplier: () -> T,
                   private val executor: Executor?): FirstStep<T>, SecondStep<T> {

    private var onFailure: ((Throwable) -> T?)? = null
    private var errMsg: String = "FUCK UP"
    private var fallback: T? = null

    override fun onFailure(x: (Throwable) -> T): FinalStep<T> {
        this.onFailure = x
        return this
    }

    override fun errMsg(msg: String): SecondStep<T> {
        this.errMsg = msg
        return this
    }

    override fun fallback(back: T): FinalStep<T> {
        this.fallback = back
        return this
    }

    override fun run(): CompletableFuture<T> {
        if (onFailure == null)
            onFailure = { ex: Throwable ->
                log.warn("{}", errMsg, ex)
                fallback
            }
        return createFuture().exceptionally(onFailure)
    }

    private fun createFuture(): CompletableFuture<T> {
        return if (executor == null)
            CompletableFuture.supplyAsync(supplier)
        else
            CompletableFuture.supplyAsync(supplier, executor)
    }
}

骚铭科技
1 声望2 粉丝

条路自己行,扑街唔好喊