玩Monad或如何享受Java的功能风格

如今,使用功能样式正在流行。 有很多教程/文章/帖子/等等。 并说明如何使用Java 8 Streams,RxJava或Project Reactor。 尽管所有这些解释都是有用的,但它们常常给人留下这样的印象:只要Java功能样式的方式/方法。 事实并非如此。 还有许多其他功能技术。 其中之一是莫纳德人。

As I've mentioned before Monad is a functional programming concept. Being properly applied this concept enables writing expressive, concise, easy to test code. But (there is always "but") proper application of this concept requires somewhat different thinking.

为了说明如何将此概念应用于Java代码,我将从以传统风格编写的简单Java代码开始,并说明如何将其重构为等效的代码,该代码使用选项单子。

At the beginning there was the code...

因此,原始代码看起来与传统的REST控制器/请求处理程序/等非常相似。 它接收一些参数,调用服务以获取必要的数据,并使用返回的值填充响应容器:

public UserProfileResponse getUserProfileHandler(final User.Id userId) {
    final User user = userService.findById(userId);
    if (user == null) {
        return UserProfileResponse.error(USER_NOT_FOUND);
    }

    final UserProfileDetails details = userProfileService.findById(userId);

    if (details == null) {
        return UserProfileResponse.of(user, UserProfileDetails.defaultDetails());
    }

    return UserProfileResponse.of(user, details);
}

没有什么特别的,如所承诺的。 让我们开始逐步重构此代码。 由于仅功能主体会发生变化,因此为简洁起见,将省略其余代码行。

Step 1

第一步是将细节的创建移到响应创建中。 这使我们可以省略整个分支并删除重复项:

final User user = userService.findById(userId);

if (user == null) {
    return UserProfileResponse.error(USER_NOT_FOUND);
}

final UserProfileDetails details = userProfileService.findById(userId);

return UserProfileResponse.of(user, 
                              details == null 
                              ? UserProfileDetails.defaultDetails() 
                              : details);

这个版本的可读性受到了一点影响,但是从长远来看,消除代码重复的价值却更多。

Step 2

在这一步,我将介绍使用选项。 我将从最后一条语句开始,因为它看起来不太可读:

final User user = userService.findById(userId);

if (user == null) {
    return UserProfileResponse.error(USER_NOT_FOUND);
}

final Option<UserProfileDetails> details = Option.option(userProfileService.findById(userId));

return UserProfileResponse.of(user,
                              details.otherwiseGet(UserProfileDetails::defaultDetails));

好,看起来好些了。 可读性得到恢复,但简洁性有所下降。

Step 3

在这一步,我将继续添加选项。 请注意,上一个版本中的最后两个语句是如何不变地移到我们实际上具有非null的位置的用户值:

final Option<User> user1 = Option.option(userService.findById(userId));

return user1.map(user -> {
    final Option<UserProfileDetails> details = Option.option(userProfileService.findById(userId));
    return UserProfileResponse.of(user, details.otherwiseGet(UserProfileDetails::defaultDetails));
})
            .otherwiseGet(() -> UserProfileResponse.error(USER_NOT_FOUND));

此版本存在缩进问题,并且绝对需要进行一些内联​​和其他清理。 我在这里显示它仅仅是为了显示开始使用monad时的关键时刻之一:要求Monad值的代码不变地移入lambda。 然后将lambda传递给映射(以转换monad)或应用程序方法(用于创建副作用)之一。 这种模式对所有Monad都是通用的,使用它我们可以享受Monad的全部功能。 例如,使用Monads编写异步处理就像编写传统的同步代码一样容易。

因此,让我们做最后的清理。

Step 4

最终清理,内联等,包括静态导入选项:

return option(userService.findById(userId))
        .map(user -> UserProfileResponse.of(user,
                                            option(userProfileService.findById(userId))
                                                    .otherwiseGet(UserProfileDetails::defaultDetails)))
        .otherwiseGet(() -> UserProfileResponse.error(USER_NOT_FOUND));

现在,整个处理都表示为单个紧凑语句。 可能还会进行更多清理(例如,提取恒定的错误响应),但是这种更改需要在函数主体外部进行更改。

我想在评论中看到有关最终代码版本(可读性,表达性等)的反馈。

from: https://dev.to//siy/playing-with-monad-or-how-to-enjoy-functional-style-in-java-ijb

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值