为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

ITMISS 2022-11-10 16:39:41
我正在构建一个大致像这样工作的流畅 API(假设存在一个返回 a 的Person带有 getter的类):getIdLongString 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);    }}//usageFunction<Person, String> mapper = new Context<Person, Long, String>(Person::getId)    .pipe(Object::toString)    .pipe(String::toUpperCase)    .end(Function.identity());mapper.apply(new Person(...))
查看完整描述

3 回答

?
慕斯709654

TA贡献1840条经验 获得超5个赞

如果我了解您要查找的内容,我会重载end()并摆脱最后一个函数组合:


public Function<InType, CurrentType> end() {

    return this.getter;

}

进一步思考,我认为Context可以消除类的第三个类型参数,因为仅在方法级别需要中间类型。检查这个:


class OtherContext<I, O> {


    private final Function<I, O> getter;


    public OtherContext(Function<I, O> getter) {

        this.getter = getter;

    }


    public <T> OtherContext<I, T> pipe(Function<O, T> mapper) {


        return new OtherContext<I, T>(getter.andThen(mapper));

    }


    public <T> Function<I, T> end(Function<O, T> mapper) {

        return getter.andThen(mapper);

    }


    public Function<I, O> end() {

        return getter;

    }

}


查看完整回答
反对 回复 2022-11-10
?
慕尼黑的夜晚无繁华

TA贡献1864条经验 获得超6个赞

您不能在 Java 中定义具有相同名称和不同返回类型的方法。您的方法可能会返回类似的东西Wrapped<T>,而您想要返回T。一般来说,我可能会建议为*andEnd(...)您的每种方法设置类似的东西。管道也是如此pipeAndEnd(...),然后以终端操作结束。这可能会变得乏味,因此如果您有很多方法,您可能想研究一些代码生成。

另一方面,您似乎正在实现自己的 Stream API 版本。除非您这样做是出于教育目的,否则使用现有且经过良好测试/记录的代码(尤其是标准 jdk 的代码部分)几乎总是比重新实现您自己的相同版本更好。


查看完整回答
反对 回复 2022-11-10
?
陪伴而非守候

TA贡献1757条经验 获得超8个赞

我遇到的主要问题是任何pipe步骤都可能是终端操作。正如每个答案和主要帖子下面的讨论中所述:在java中不可能使用两次具有相同名称的函数并且一个是终端操作。


我猛烈抨击这个问题并尝试了多种方法,但每种方法都不起作用。那是当我意识到我在做的事情本质上与 Javas Stream-API 相同:你有一个起源(源),做一些花哨的东西(管道)然后结束(收集)。如果我们对我的问题应用相同的方案,则不需要进行pipe终端操作,我们只需要另一个操作(例如end)作为终点。由于我对何时可能结束(当前类型必须与另一种类型匹配)有一些扩展要求,因此我end只允许一个上下文特定的函数来实现,该函数只有一个可用的合理实现(很难解释)。这是当前实现的示例(pipe已重命名为和map):endto


Mapper<Person, PersonDTO> mapper = Datus.forTypes(Person.class, PersonDTO.class).immutable(PersonDTO::new)

    .from(Person::getFirstName).to(ConstructorParameter::bind)

    .from(Person::getLastName)

        .given(Objects::nonNull, ln -> ln.toUpperCase()).orElse("fallback")

        .to(ConstructorParameter::bind)

    .build();

如您所见.to,充当终端操作员,ConstructorParameter::bind如果当前类型与预期类型不匹配,则会抱怨类型不匹配。


参见这里的to部分,这里的实现ConstructorParameter以及它是如何定义的。


查看完整回答
反对 回复 2022-11-10
  • 3 回答
  • 0 关注
  • 81 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信