大家好, 这里是K字的研究.
前几天在 介绍了如何快速记住java.util.function
包下面的所有函数. 这边提一个问题复习下,LongBinaryOperator
是干嘛用的?10秒内想不起来的可以去回顾一下.
Function 有入参有返回的叫Function, 特点是有进有出 Consumer
有入参没返回的叫Consumer, 特点是只进不出
Supplier 没入参有返回的叫Supplier, 特点是只出不进 这个和Callable类似,只是那个是Java5引入的
J K L,公众号:K字的研究如何正确统计字符串里每种字符的个数-你不一定知道的Java基础知识
今天更具体的举例说说为什么要用函数式编程
-
他们解决了什么痛点 -
给Java带来了什么新打法 -
提供了什么新抓手 -
对Java的顶层设计有什么影响 -
对项目的底层架构有什么改进 -
对服务链路有什么延伸 -
对CRUD有什么场景价值 -
配合设计模式等经典内容能打出怎样的组合拳 -
…
好了好了, 不学黑话了.说着玩呢, 今天只说一件事,怎么借助函数式编程写一个工厂模式.

设计模式与函数式编程
有了函数式编程, 很多经典GOF设计模式的写法都会变的优雅. 比如最经典,基本上每个人都会入手先学一下的:工厂模式(Factory Pattern)
.
-
先假设我们有一个老婆.

-
老婆负责给我们提供每天看的东西. -
我们想看什么就告诉老婆, 她会把东西拿过来. -
老婆提供了一个固定清单, 能选择 小说
,公众号
,电视
-
说要看小说, 就会拿来本《四签名》 -
说要看公众号,就会拿来老K的《K字的研究》 -
要看电视, 就会打开《The.Hound.of.the.Baskervilles.1988.720p.BluRay》
在这里, 其实老婆就是一个工厂.东西是他买的,还是自己下载的,怎么弄的,我们完全可以不关心.
写成代码是这样的:
package sh.now.afk.viz.javaproblem;
interface Watchable {
}
class Novel implements Watchable {
}
class Mp implements Watchable {
}
class Tv implements Watchable {
}
class Aragaki {
static Watchable get(String kind) {
switch (kind) {
case "novel":
return new Novel();
case "mp":
return new Mp();
case "tv":
return new Tv();
default:
throw new IllegalArgumentException();
}
}
}
这里, 可以借助switch
. 嗯, 老婆最喜欢的switch
来完成这个模式的写法.
但是, 有了函数式接口以后, 其实有了更简单的写法.
函数式编程的工厂模式
据我们所知, 跟老婆说要看什么东西以后, 老婆是去调用了一个生成对应东西的方法的.
记得开头复习的java.util.function
内容吗? 如果有一个方法,没有入参,但是有返回类型T, 那他叫什么?
Supplier
是的, 老婆其实不需要switch.
根据一个字符串, 去找到一个Supplier
,然后调用. 这里其实是一个1v1对应关系. 最合适的类型是?
Map
Map 是提供对应关系最方便的工具. 这里用Map来改写.
import java.nio.file.Watchable;
import java.util.Map;
import java.util.Optional;
import java.util.function.Supplier;
class Aragaki {
static Watchable get(String kind) {
//这里用if也行
return Optional.ofNullable(
map.get(kind))
.map(x -> x.get())
.orElseThrow(IllegalArgumentException::new);
}
//这个是java9语法糖,这个of是一个工厂模式.(谁说工厂一定要带Factory
static final Map<String, Supplier<Watchable>> map = Map.of(
"novel", Novel::new,
"mp", Mp::new,
"tv", Tv::new
);
}
是不是感觉好像确实优雅了很多.这就是函数式编程的威力.
这里的 Novel::new
这个语法, 表示引用Novel的new方法
, 这个语法叫 Method Reference(方法引用)
, 属于函数式编程的半壁江山. 另外半壁是lambda
.
一个是有名字的函数, 一个是没名字的函数. 他们统一都是一种叫做:函数式接口的东西.
函数式接口-FunctionalInterface
打开java.util.function
内的接口,就可以发现,他们都有一个共同的注解@FunctionalInterface
. 标记当前接口是函数式接口. 特点是, 接口的抽象方法(abstract method)
只有1个
.
老Java中,有很多接收单个抽象方法
当参数的地方,现在都可以改造成接收函数式接口了.比如最常用的一个, Comparator
Comparator 函数式接口
比如前几天, 我们按顺序生成天干地支的算法.纪年之间的谁先谁后的比较, 就是自己实现的.

Seq.of('甲', '乙', '丙', '丁', '戊', '己', '庚', '辛', '壬', '癸')
.zipWithIndex().innerJoin(
Seq.of('子', '丑', '寅', '卯', '辰', '巳', '午', '未', '申', '酉', '戌', '亥')
.zipWithIndex(),
(x, y) -> x.v2 % 2 == y.v2 % 2
).sorted((x, y) ->
(int) ((((x.v1.v2) * 6 - 5 * (x.v2.v2)) + 60) % 60 -
(((y.v1.v2) * 6 - 5 * (y.v2.v2)) + 60) % 60));
这里的的sorted
接收的就是一个Comparator
接口. 传入的实现代码是:
(x, y) ->
(int) ((((x.v1.v2) * 6 - 5 * (x.v2.v2)) + 60) % 60
- (((y.v1.v2) * 6 - 5 * (y.v2.v2)) + 60) % 60)
因为这里的参数是FunctionalInterface
,所以, 可以使用lambda
,省下来了不少代码. 按照原有Java逻辑, 要写成这样.
new Comparator<Tuple2<Tuple2<Character, Long>,
Tuple2<Character, Long>>>() {
@Override
public int compare(
Tuple2<Tuple2<Character, Long>,
Tuple2<Character, Long>> x,
Tuple2<Tuple2<Character, Long>,
Tuple2<Character, Long>> y) {
return (int) ((x.v1.v2 * 6 - 5 * x.v2.v2 + 60) % 60 -
(((((y.v1.v2) * 6) - (5 * (y.v2.v2))) + 60) % 60));
}
}
看着这个, 感觉快要不能呼吸了. FunctionalInterface,改变代码信噪比,可以救命.
后话
没了, 今天这篇就是这么短. 最近在读一本叫《Java Coding Problems》的书. 看到精彩的部分就会把笔记发出来.
建议去看原书,书中代码十分精彩刺激.
文末强烈谴责一位复姓星野的同志,损公肥私.
原文始发于微信公众号(K字的研究):设计模式之函数风工厂模式
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/24879.html