流畅地链接多个方法,类似于 Stream 而没有 Optional 类型的冗长
Chaining multiple methods fluently akin to a Stream without the verbosity of the Optional type
在 Spring 应用程序中,我倾向于在控制器方法中获取请求主体,并希望通过多个方法调用(return 沿途使用不同的类型)将其流利地传递给管道,例如以下(简化)示例:
public ResponseEntity<FooDto> postFoo(@RequestBody final FooDto requestBody) {
return Optional.of(requestBody) // Optional<FooDto>
.map(mapper::fromDto) // Optional<FooEntity>
.map(service::insertEntity) // Optional<FooEntity>
.map(mapper::fromEntity) // Optional<FooDto>
.map(dto -> ResponseEntity.created(/* ... */).body(dto).build()) // Optional<ResponseEntity<FooDto>>
.orElseThrow(IllegalStateException::new);
}
如您所见,我很想应用一些 FP 模式,但可选的 class 并不适合这样做,因为隐含的 "optionality" 是人为的,感兴趣的底层对象应该首先永远不要空着。因此,最终的异常不会(希望)被抛出,或者只是调用 Optional::get
也不是一个很好的选择,因为 Sonarlint 抱怨未经检查的 get 调用,这是理所当然的。
是否有任何可用的惯用方法,甚至可能与 vavr 或其他 FP 库结合使用,以比使用此类人工 Optional 构造更好地表达此类方法链?否则我可能不得不避免这样做并恢复到带有十几个变量的 classic 命令式方法。
编辑:如果使用 return Either<ErrorReason, Optional<FooEntity>>
的方法,我尝试使用 Optional 的方式很容易失控,这使得 Optional<Either<ErrorReason, Optional<FooEntity>>>
最终变得不再清晰。
执行您要查找的内容的最简洁方法是恢复命令式样式,例如:
public ResponseEntity<FooDto> postFoo(final FooDto requestBody) {
final FooEntity fooEntity = fromDto(requestBody);
final FooEntity updatedEntity = insertEntity(fooEntity); // should be void?
final FooDto responseDto = fromEntity(updatedEntity);
return ResponseEntity.created(/* ... */)
.body(responseDto)
.build();
}
我同意 Naman 的观点,在这种情况下命令式风格可能是最简洁的方式。
如果你真的很想做一些像流一样的Optional
风格你可以创建你自己的class
public final class Value<T> {
private final T value;
// Private constructor to force usage of static construction
private Value(T value) {
this.value = value;
}
// Static constructor Optional style
public static <T> Value<T> of(T value) {
return new Value<>(value);
}
public <R> Value<R> map(Function<? super T, ? extends R> mapper) {
return new Value<>(mapper.apply(this.value));
}
// method to unwrap value
public T get() {
return value;
}
}
然后你会像
那样使用它
public ResponseEntity<FooDto> postFoo(@RequestBody final FooDto requestBody) {
return Value.of(requestBody) // Value<FooDto>
.map(mapper::fromDto) // Value<FooEntity>
.map(service::insertEntity) // Value<FooEntity>
.map(mapper::fromEntity) // Value<FooDto>
.map(dto -> ResponseEntity.created(/* ... */).body(dto).build()) // Value<ResponseEntity<FooDto>>
.get();
}
同样,我强烈反对这种解决方案,我会选择使用命令式风格。
在 Spring 应用程序中,我倾向于在控制器方法中获取请求主体,并希望通过多个方法调用(return 沿途使用不同的类型)将其流利地传递给管道,例如以下(简化)示例:
public ResponseEntity<FooDto> postFoo(@RequestBody final FooDto requestBody) {
return Optional.of(requestBody) // Optional<FooDto>
.map(mapper::fromDto) // Optional<FooEntity>
.map(service::insertEntity) // Optional<FooEntity>
.map(mapper::fromEntity) // Optional<FooDto>
.map(dto -> ResponseEntity.created(/* ... */).body(dto).build()) // Optional<ResponseEntity<FooDto>>
.orElseThrow(IllegalStateException::new);
}
如您所见,我很想应用一些 FP 模式,但可选的 class 并不适合这样做,因为隐含的 "optionality" 是人为的,感兴趣的底层对象应该首先永远不要空着。因此,最终的异常不会(希望)被抛出,或者只是调用 Optional::get
也不是一个很好的选择,因为 Sonarlint 抱怨未经检查的 get 调用,这是理所当然的。
是否有任何可用的惯用方法,甚至可能与 vavr 或其他 FP 库结合使用,以比使用此类人工 Optional 构造更好地表达此类方法链?否则我可能不得不避免这样做并恢复到带有十几个变量的 classic 命令式方法。
编辑:如果使用 return Either<ErrorReason, Optional<FooEntity>>
的方法,我尝试使用 Optional 的方式很容易失控,这使得 Optional<Either<ErrorReason, Optional<FooEntity>>>
最终变得不再清晰。
执行您要查找的内容的最简洁方法是恢复命令式样式,例如:
public ResponseEntity<FooDto> postFoo(final FooDto requestBody) {
final FooEntity fooEntity = fromDto(requestBody);
final FooEntity updatedEntity = insertEntity(fooEntity); // should be void?
final FooDto responseDto = fromEntity(updatedEntity);
return ResponseEntity.created(/* ... */)
.body(responseDto)
.build();
}
我同意 Naman 的观点,在这种情况下命令式风格可能是最简洁的方式。
如果你真的很想做一些像流一样的Optional
风格你可以创建你自己的class
public final class Value<T> {
private final T value;
// Private constructor to force usage of static construction
private Value(T value) {
this.value = value;
}
// Static constructor Optional style
public static <T> Value<T> of(T value) {
return new Value<>(value);
}
public <R> Value<R> map(Function<? super T, ? extends R> mapper) {
return new Value<>(mapper.apply(this.value));
}
// method to unwrap value
public T get() {
return value;
}
}
然后你会像
那样使用它public ResponseEntity<FooDto> postFoo(@RequestBody final FooDto requestBody) {
return Value.of(requestBody) // Value<FooDto>
.map(mapper::fromDto) // Value<FooEntity>
.map(service::insertEntity) // Value<FooEntity>
.map(mapper::fromEntity) // Value<FooDto>
.map(dto -> ResponseEntity.created(/* ... */).body(dto).build()) // Value<ResponseEntity<FooDto>>
.get();
}
同样,我强烈反对这种解决方案,我会选择使用命令式风格。