如何处理一个每一步都可以是终端操作的流畅界面?

我正在构建一个大致像这样工作的流畅 API(假设存在一个返回 a 的Person带有 getter的类):getIdLong


String result = context.map(Person::getId)

     .pipe(Object::toString)

     .pipe(String::toUpperCase)

     .end(Function.identity())

如您所见,只有.end-function 充当终端运算符。这使上述 API 的整体使用.end(Function.identity())变得混乱,因为即使前面的.pipe-call 已经具有正确的类型,我也经常不得不以 -call 结束。


有什么方法可以制作一个 fluent-API,使其一部分既是终端操作员又是“桥接操作员”?我只是不想用专门的 pipe变体pipeTo(例如,只接受 aFunction<CurrentType, ExpectedType>和内部调用的管道.end)来模拟所述行为,因为它迫使用户考虑 API 的一个非常特定的部分,这对我来说似乎是不必要的。


编辑:根据要求简化上下文实现:


class Context<InType, CurrentType, TargetType> {

    private final Function<InType, CurrentType> getter;


    public Context(Function<InType, CurrentType> getter) {

        this.getter = getter;

    }


    public <IntermediateType> Context<InType, IntermediateType, TargetType>

    pipe(Function<CurrentType, IntermediateType> mapper) {


        return new Context<>(getter.andThen(mapper));

    }


    public Function<InType, TargetType> end(Function<CurrentType, TargetType> mapper) {

        return getter.andThen(mapper);

    }

}


//usage

Function<Person, String> mapper = new Context<Person, Long, String>(Person::getId)

    .pipe(Object::toString)

    .pipe(String::toUpperCase)

    .end(Function.identity());


mapper.apply(new Person(...))


ITMISS
浏览 82回答 3
3回答

慕斯709654

如果我了解您要查找的内容,我会重载end()并摆脱最后一个函数组合:public Function<InType, CurrentType> end() {&nbsp; &nbsp; return this.getter;}进一步思考,我认为Context可以消除类的第三个类型参数,因为仅在方法级别需要中间类型。检查这个:class OtherContext<I, O> {&nbsp; &nbsp; private final Function<I, O> getter;&nbsp; &nbsp; public OtherContext(Function<I, O> getter) {&nbsp; &nbsp; &nbsp; &nbsp; this.getter = getter;&nbsp; &nbsp; }&nbsp; &nbsp; public <T> OtherContext<I, T> pipe(Function<O, T> mapper) {&nbsp; &nbsp; &nbsp; &nbsp; return new OtherContext<I, T>(getter.andThen(mapper));&nbsp; &nbsp; }&nbsp; &nbsp; public <T> Function<I, T> end(Function<O, T> mapper) {&nbsp; &nbsp; &nbsp; &nbsp; return getter.andThen(mapper);&nbsp; &nbsp; }&nbsp; &nbsp; public Function<I, O> end() {&nbsp; &nbsp; &nbsp; &nbsp; return getter;&nbsp; &nbsp; }}

慕尼黑的夜晚无繁华

您不能在 Java 中定义具有相同名称和不同返回类型的方法。您的方法可能会返回类似的东西Wrapped<T>,而您想要返回T。一般来说,我可能会建议为*andEnd(...)您的每种方法设置类似的东西。管道也是如此pipeAndEnd(...),然后以终端操作结束。这可能会变得乏味,因此如果您有很多方法,您可能想研究一些代码生成。另一方面,您似乎正在实现自己的 Stream API 版本。除非您这样做是出于教育目的,否则使用现有且经过良好测试/记录的代码(尤其是标准 jdk 的代码部分)几乎总是比重新实现您自己的相同版本更好。

陪伴而非守候

我遇到的主要问题是任何pipe步骤都可能是终端操作。正如每个答案和主要帖子下面的讨论中所述:在java中不可能使用两次具有相同名称的函数并且一个是终端操作。我猛烈抨击这个问题并尝试了多种方法,但每种方法都不起作用。那是当我意识到我在做的事情本质上与 Javas Stream-API 相同:你有一个起源(源),做一些花哨的东西(管道)然后结束(收集)。如果我们对我的问题应用相同的方案,则不需要进行pipe终端操作,我们只需要另一个操作(例如end)作为终点。由于我对何时可能结束(当前类型必须与另一种类型匹配)有一些扩展要求,因此我end只允许一个上下文特定的函数来实现,该函数只有一个可用的合理实现(很难解释)。这是当前实现的示例(pipe已重命名为和map):endtoMapper<Person, PersonDTO> mapper = Datus.forTypes(Person.class, PersonDTO.class).immutable(PersonDTO::new)&nbsp; &nbsp; .from(Person::getFirstName).to(ConstructorParameter::bind)&nbsp; &nbsp; .from(Person::getLastName)&nbsp; &nbsp; &nbsp; &nbsp; .given(Objects::nonNull, ln -> ln.toUpperCase()).orElse("fallback")&nbsp; &nbsp; &nbsp; &nbsp; .to(ConstructorParameter::bind)&nbsp; &nbsp; .build();如您所见.to,充当终端操作员,ConstructorParameter::bind如果当前类型与预期类型不匹配,则会抱怨类型不匹配。参见这里的to部分,这里的实现ConstructorParameter以及它是如何定义的。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Java