Lenshood

Software Developer @ThoughtWorks

0%

Joiner

以某种分隔符连接连接字符串序列通常不应该那么麻烦,当序列中包含 null 值的时候尤甚。流式风格的 Joiner 简化了这种操作。

1
2
Joiner joiner = Joiner.on("; ").skipNulls();
return joiner.join("Harry", null, "Ron", "Hermione");

上述代码将返回字符串:”Harry; Ron; Hermione“。除了skipNulls以外,还可以用useForNull(String)来指定将 null 替换为给定的字符串。

你也可以直接对对象使用 Joiner ,这会自动调用对象的toString()方法后进行拼接。

1
Joiner.on(",").join(Arrays.asList(1, 5, 7)); // returns "1,5,7"

注意:joiner 实例是不可变的。因此 joiner 的配置方法总会返回一个新的 Joiner,并通过这种方式来获取所需语义的 joiner。这种特性让Joiner能够线程安全,并可以将之定义为一个static final的常量。

Read more »

在我的文章 虚拟工厂:Java 线程池 中简单介绍了采用 ”工厂“ 这样一个现实生活中的概念来抽象具体的线程操作,并定义了 Worker TaskQueue 等概念。用这种方式拉近了计算机域和真实世界域之间的距离,让代码表现现实的意图。

不仅仅是线程池,从 Java 8 开始引入的Stream也一样,它用代码构建了一套流水线体系,通过流水线环节的叠加来实现对流水线上元素的各种处理。

Read more »

工厂

现实生活中,存在大大小小的工厂,他们提供特定的服务,根据用户的需求生产、加工出产品,交付给客户,并赚取服务费。

通常,我们只需要交给工厂一份图纸,工厂就会根据图纸来生产产品,而生产的过程我们是无需关心的,作为客户,我们只关心这间工厂什么时候把产品交付给我们。

类似的,在代码里我们也会存在以下场景:主流程代码将需要做的事情分成一件件任务,并交由对应的代码工厂去执行,这里的任务可以是代码逻辑的执行步骤,而最基本的代码工厂就是各种方法。合并起来,代码工厂执行任务,实际上就是调用方法执行业务逻辑。

不过有时我们期望代码工厂能够异步执行任务,这样主流程就不用一直等待它执行完成了:让互相之间不存在依赖的任务并行执行能大大缩短总体的执行时间。

Java 提供了线程机制允许我们将方法调用放在与主流程独立的线程中执行,不过直接使用线程代码写起来略为繁琐,线程本身也是较低层的概念,所以在 JDK 1.5 之后,提供了对异步任务执行的高层抽象:线程池,通过线程池我们可以更方便的执行异步任务,而不必关心线程的使用细节。

线程池以清晰的设计与简洁的代码优雅的封装了线程并实现了任务执行逻辑。阅读它的代码,就好像走进了一间虚拟的工厂,工人挥舞工具的画面似乎就在眼前。

Read more »

Git Pages 镜像到 Gitee Pages

在本站早期的文章Build your own blog site by using GithubPages + Hexo + Travis CI中描述了如何使用 Hexo 和 Travis 来在 Git Pages 服务上搭建自己的个人博客。

但使用了一段时间之后,发现 Git Pages 的方案存在一些问题: - 访问速度慢。不走 VPN 访问 Github 速度感人,Git Pages 也一样,各种资源、图片加载缓慢。 - 难以被 Baidu 收录。网友提供了各种办法来解决,但都挺麻烦。

所以当我了解到 Gitee 也提供了免费的静态网页服务后,决定试试看,但毕竟一边在 github 上更新,一边在 gitee 更新,既麻烦又容易出错,所以期望能够直接把 github 上的更新自动同步到 gitee 上。

经过一些实验后,我成功的实现了把 Git Pages 镜像到 Gitee Pages 的需求。

Read more »

一些 Java 语言 Tips

本文包括一些 Java 语言在使用中常见的小错误以及不佳实践,他们收集自我的日常开发、Code Review、以及书中所见。持续更新...

Read more »

在前一篇有关 Redis 分布式锁的文章中,我们讨论了几点有关分布式锁的要求: 1. 操作原子性 2. 可重入性 3. 效率

为了满足上述条件,采用 本地锁 + Redis 锁 的方式解决了问题。不过在文章末尾提到,Redis 不保证强一致性,因此对一致性要求很高的场景会存在安全隐患。

本文将讨论使用满足 CP 要求的 ZooKeeper 来实现强一致性的分布式锁。

Read more »

Rust Modules

rust 的 module system,类似 java 的 package,可以用于将代码分别放置在适合他们的单元内。同时 rust 还允许用户控制 module 之间的可见性(public/private)。

在 module 内,rust 允许用户放置 function,struct,trait,struct implement 以及 child module。

rust 对 module 的要求是任何 module 都应处在一颗以 root module 为根的 module tree 中。期望访问某个 module 时,能够找到一条通路从root一路向下直到该 module。

Read more »

先跑起来

不说什么具体的知识,我们先一步步的,来写个最简单的测试,并且让他啊跑起来,看看 rust 下的测试是什么样子的: 1. 创建个 lib 工程:cargo new simplest-test --lib 2. 在 src/lib.rs 里面,rust 已经自动帮我们写下了如下代码:

1
2
3
4
5
6
7
#[cfg(test)]
mod test {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
}
3. 运行一下:cargo test
1
2
3
4
5
6
7
8
9
10
11
12
13
14
   Compiling simplest-test v0.1.0 (/User/xxx/simplest-test)
Finished dev [unoptimized + debuginfo] target(s) in 5.91s
Running target/debug/deps/simplest_test-c430fbaec5f55b85

running 1 test
test tests::it_works ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

Doc-tests simplest-test

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

经过上述三步,我们已经创建了一个最简单的测试工程,并且运行了自带的测试。

在代码层面,测试本身无需多说,结构上我们看到,与业务代码不同,测试在 module 上增加了 attribute: #[cfg(test)],在测试方法上增加了 attribute: #[test]。 - #[cfg(test)]:配置编译条件,只有在 test 模式下,被标记的代码块才会被编译(换句话说,它确保 release 中不包含测试代码) - #[test]:被标记的方法将被视为测试来执行

在 output 中,还包含了两部分“running x test”,第一部分是我们已有的测试,第二部分为文档测试,本文暂不涉及。

Read more »

在如今这样一个张口分布式,闭口微服务的软件开发趋势下,多实例似乎已经不是某种选择而是一个无需多说的基本技术要求了。

多实例为我们带来稳定性提升的同时,也伴随着更复杂的技术要求,原先在本地即可处理的问题,全部扩展为分布式问题,其中就包含我们今天会聊到的多实例同步即分布式锁问题。JDK 提供的锁实现已经能够非常好的解决本地同步问题,而扩展到多实例环境下,Redis、ZooKeeper 等优秀的实现也使得我们使用分布式锁变得更加简单。

其实对于分布式锁的原理、分布式锁的 Redis 实现、ZK 实现等等各类文章不计其数,然而只要简单一搜就会发现,大多数文章都在教大家 Redis 分布式锁的原理和实现方法,但却没有几篇会写什么实现是好的实现,是适合用于生产环境,高效而考虑全面的实现。这将是本文讨论的内容。

Read more »

依赖倒置就是每一个实现都要抽一个接口出来吗?

本文的标题实际上来自于一次与项目上同事中午吃饭时的讨论:

A: 我觉得我们现在的抽象有点多,infra 层里面每一个类都抽取了接口,这些被调用的类多半只有一个实现, 我们是不是做的太细了?

B: 从依赖倒置的角度讲,domain 层和 service 层并不应该直接调用 infra 层的实现,因此我们确实是需要每一个实现都抽一个接口出来。

A: 那依赖倒置就是每一个实现都要抽一个接口出来吗?

B: 这个...

看来小伙伴 A 不经意间触碰到了 S.O.L.I.D. 的深水区...

相比于单一职责、开闭、接口隔离等原则,依赖倒置与里氏替换类似,属于更偏向操作指导的一类原则,比如从依赖倒置的定义来看:

依赖倒置:高层模块不应直接依赖低层模块,他们都应该依赖于彼此间的抽象。

以开发的角度理解:高层不要直接调用低层,而是调用抽取出来的接口。

那这么说,依赖倒置就是每一个实现都要抽一个接口出来吗?

为了解释这个问题,我们尝试来提出一个新的问题:为啥要依赖倒置?

Read more »