测试、调试和重构
孤独的覆盖
ThreadLocal
能创建一个工厂,为每个线程最多只产生一个值。这是确保非线程安全的类在并发环境下安全使用的一种简单方式。
假设要在数据库查询一个艺术家,但希望每个线程值做一次这种查询:
1 2 3 4 5 6
| ThreadLocal<Album> thisAlbum = new ThreadLocal<Album>() { @Override protected Album initialValue() { return database.findCurrentAlbum(); } };
|
为工厂方法 withInitial
传入一个 Supplier
对象实例来创建对象:
1 2 3
| ThreadLocal<Album> thisAlbum = ThreadLocal.withInitial( () -> database.findCurrentAlbum() );
|
同样的东西写两遍
DRY:Don’t Repeat Yourself
WET:Write Everything Twice
不是所有的 WET
都适合 lambdas
化。有时重复是唯一可以避免系统过紧耦合的方式。
什么时候该将 WET
的代码 lambda
化?
如果有一个整体上大概相似的模式,只是行为上有所不同,就可以试着加入一个 lambda
表达式。
举个栗子:
用户想要了解购买的专辑的一些信息,比如音乐家的人数、曲目和专辑时长等。
使用命令式 Java
编写的 Order
类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| public long countMusicians() { long count = 0; for (Album album : albums) { count += album.getMusicianList().size(); } return count; }
public long countTracks() { long count = 0; for (Album album : albums) { count += album.getTrackList().size(); } return count; }
public long countRunningTime() { long count = 0; for (Album album : albums) { for (Track track : album.getTrackList()) { count += track.getLength(); } } return count; }
|
每个方法里,都有样板代码将将每个专辑里的属性和总数相加。
没有重用共有的概念,写出了更多需要测试和维护的代码。
新增 OrderStream
类,使用 Stream
来抽象 Order
类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public long countMusicians() { return albums.stream() .mapToLong(album -> album.getMusicians().count()) .sum(); }
public long countTracks() { return albums.stream() .mapToLong(album -> album.getTracks().count()) .sum(); }
public long countRunningTime() { return albums.stream() .mapToLong(album -> album.getTracks() .mapToLong(Track::getLength) .sum()) .sum(); }
|
然而这段代码仍然有重用可读性的问题,因为有一些抽象和共性只能使用领域内的知识来表达。
流不会提供一个方法统计每张专辑上的信息——这是程序猿自己要编写的领域知识。
新增 OrderStreamDSL
类,用领域方法重构 OrderStream
类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| private long countFeature(ToLongFunction<Album> function) { return albums.stream() .mapToLong(function) .sum(); }
public long countMusicians() { return countFeature(album -> album.getMusicians().count()); }
public long countTracks() { return countFeature(album -> album.getTracks().count()); }
public long countRunningTime() { return countFeature(album -> album.getTracks() .mapToLong(Track::getLength) .sum()); }
|