前言
备受争议的
Lombok
,有的人喜欢它让代码更整洁,有的人不喜欢它,巴拉巴拉一堆原因。在我看来
Lombok
唯一的缺点可能就是需要安装插件了,但是对于业务开发的项目来说,它的优点远远超过缺点。
❝
我们可以看一下,有多少项目使用了Lombok(数量还在疯涨中...)
❞
❝
尽管如此,我们今天也只是单纯的来看一下@Builder()这个东西
❞
@Builder的使用
使用@Builder修饰类
@Data @Builder public class UserDO { private Long id; private String name; }
使用建造者模式创建类
@Test public void test () { UserDO userDO = UserDO.builder() .id(1L ) .name("iisheng" ) .build(); System.out.println(userDO); }
编译后源码
执行
javac -cp ~/lombok.jar UserDO.java -verbose
将
.java
编译成
.class
文件。
通过IDE查看该
.class
源码
❝
下面展示的是被我处理后的源码,感兴趣的同学,可以自己执行上面命令,查看完整源码
❞
public class UserDO { private Long id; private String name; public String toString () { return "UserDO(id=" + this .getId() + ", name=" + this .getName() + ")" ; } UserDO(Long var1, String var2) { this .id = var1; this .name = var2; } public static UserDO.UserDOBuilder builder () { return new UserDO.UserDOBuilder(); } private UserDO () { } public static class UserDOBuilder { private Long id; private String name; UserDOBuilder() { } public UserDO.UserDOBuilder id (Long var1) { this .id = var1; return this ; } public UserDO.UserDOBuilder name (String var1) { this .name = var1; return this ; } public UserDO build () { return new UserDO(this .id, this .name); } } }
由此,我们可以看出来Builder的实现步骤:
在
UserDO
中创建静态
UserDOBuilder
编写设置属性方法,返回
UserDOBuilder
对象
编写
build()
方法,返回
UserDO
对象
是不是很简单?我曾经看过不知道哪个大佬说的一句话,整洁的代码不是说,行数更少,字数更少,而是阅读起来逻辑更清晰。所以,我觉得,哪怕我们不用@Builder,也应该多用这种建造者模式。
❝
是时候看看什么是建造者模式了!
❞
建造者模式
UML类图
❝
这是大部分书籍网络中的建造者模式类图
❞
产品类
public class Product { private String name; private Integer val; Product(String name, Integer val) { this .name = name; this .val = val; } @Override public String toString () { return "Product is " + name + " value is " + val; } }
抽象建造者
public
abstract class Builder { protected Integer val; protected String name; // 设置产品不同部分,以获得不同的产品 public abstract void setVal (Integer val) ; // 设置名字 公用方法 public void setName (String name) { this .name = name; } // 建造产品 public abstract Product buildProduct () ; }
具体建造者
public class ConcreteBuilder extends Builder { @Override public void setVal (Integer val) { /** * 产品类内部的逻辑 * 实际存储的值是 val + 100 */ this .val = val + 100 ; } @Override // 组建一个产品 public Product buildProduct () { // 这块还可以写特殊的校验逻辑 return new Product(name, val); } }
导演类
public class Director { private Builder builder = new ConcreteBuilder(); public Product getAProduct () { // 设置不同的零件,产生不同的产品 builder.setName("ProductA" ); builder.setVal(2 ); return builder.buildProduct(); } }
❝
我更喜欢这样的建造者模式类图
❞
Product
的创建,也依赖于
Builder
。代码只需要将上面的
Product
和
ConcreteBuilder
调整一下即可。
调整后的产品类
public class Product { private String name; private Integer val; Product(Builder builder) { this .name = builder.name; this .val = builder.val; } @Override public String toString () { return "Product is " + name + " value is " + val; } }
这代码只是将构造方法改了,使用
Builder
来创建
Product
对象。
调整后的具体建造者
public class ConcreteBuilder extends Builder { @Override public void setVal (Integer val) { /** * 产品类内部的逻辑 * 实际存储的值是 val + 100 */ this .val = val + 100 ; } @Override // 组建一个产品 public Product buildProduct () { // 这块还可以写特殊的校验逻辑 return new Product(this ); } }
相应的使用带
Builder
的
Product
的构造方法。
JDK中的建造者模式
❝
StringBuilder (截取部分源码)
❞
抽象建造者
abstract class AbstractStringBuilder implements Appendable , CharSequence { /** * The value is used for character storage. */ char [] value; /** * The count is the number of characters used. */ int count; public AbstractStringBuilder append (String str) { if (str == null ) return appendNull(); int len = str.length(); ensureCapacityInternal(count + len); str.getChars(0 , len, value, count); count += len; return this ; } // Documentation in subclasses because of synchro difference @Override public AbstractStringBuilder append (CharSequence s) { if (s == null ) return appendNull(); if (s instanceof String) return this .append((String)s); if (s instanceof AbstractStringBuilder) return this .append((AbstractStringBuilder)s); return this .append(s, 0 , s.length()); } public AbstractStringBuilder delete (int start, int end) { if (start 0) throw new StringIndexOutOfBoundsException(start); if (end > count) end = count; if (start > end) throw new StringIndexOutOfBoundsException(); int len = end - start; if (len > 0 ) { System.arraycopy(value, start+len, value, start, count-end); count -= len; } return
this ; } }
具体建造者
public final class StringBuilder extends AbstractStringBuilder implements java .io .Serializable , CharSequence { @Override public StringBuilder append (String str) { super .append(str); return this ; } @Override public StringBuilder append (CharSequence s) { super .append(s); return this ; } /** * @throws StringIndexOutOfBoundsException {@inheritDoc } */ @Override public StringBuilder delete (int start, int end) { super .delete(start, end); return this ; } }
StringBuilder
中的建造者模式比较简单,但是我的确没找到
StringBuilder
非要用建造者模式的原因,或许就是想让我们写下面这样的代码?
public static void main (String[] args) { StringBuilder sb = new StringBuilder(); sb.append("Love " ) .append("iisheng !" ) .insert(0 , "I " ); System.out.println(sb); }
❝
但是我希望你能通过
StringBuilder
,感受一下建造者模式的气息
❞
Guava Cache中的建造者模式
如何使用 Guava Cache?
public static void main (String[] args) { LoadingCache cache = CacheBuilder.newBuilder() // 最多存放十个数据 .maximumSize(10 ) // 缓存10秒 .expireAfterWrite(10 , TimeUnit.SECONDS) .build(new CacheLoader() { // 默认返回-1,也可以是查询操作,如从DB查询 @Override public Integer load (String key) throws Exception { return -1 ; } }); // 只查询缓存,没有命中,即返回 null System.out.println(cache.getIfPresent("key1" )); // put数据,放在缓存中 cache.put("key1" , 1 ); // 再次查询,已经存在缓存中 System.out.println(cache.getIfPresent("key1" )); //查询缓存,未命中,调用load方法,返回 -1 try { System.out.println(cache.get("key2" )); } catch (ExecutionException e) { e.printStackTrace(); } }
❝
下面是截取建造者模式相关的部分代码
❞
产品接口
@DoNotMock ("Use CacheBuilder.newBuilder().build()" )@GwtCompatible public interface Cache <K , V > { @Nullable V getIfPresent (@CompatibleWith("K" ) Object key) ; V get (K key, Callable extends V> loader) throws ExecutionException ; void put (K key, V value) ; long size () ; ConcurrentMap asMap () ; void cleanUp () ; }
另一个产品接口
@GwtCompatible public interface LoadingCache <K , V > extends Cache <K , V >, Function <K , V > { V get (K key) throws ExecutionException ; V getUnchecked (K key) ; void refresh (K key) ; @Deprecated @Override V apply (K key) ; @Override ConcurrentMap asMap () ; }
产品实现类
static class LocalManualCache <K , V > implements Cache <K , V >, Serializable { final LocalCache localCache; LocalManualCache(CacheBuilder super K, ? super V> builder) { this (new LocalCache(builder, null )); } private LocalManualCache (LocalCache localCache) { this .localCache = localCache; } // Cache methods @Override public @Nullable V getIfPresent (Object key) { return localCache.getIfPresent(key); } @Override public V get (K key, final Callable extends V> valueLoader) throws ExecutionException { checkNotNull(valueLoader); return localCache.get( key, new CacheLoader() { @Override public V load (Object key) throws Exception { return valueLoader.call(); } }); } @Override public void put (K key, V value) { localCache.put(key, value); } @Override public long size () { return localCache.longSize(); } @Override public ConcurrentMap asMap () { return localCache; } @Override public void cleanUp () { localCache.cleanUp(); } // Serialization Support private static final long serialVersionUID = 1 ; Object writeReplace () { return new ManualSerializationProxy<>(localCache); } }
另一个产品实现类
static class LocalLoadingCache <K , V > extends LocalManualCache <K , V > implements LoadingCache <K , V > { LocalLoadingCache( CacheBuilder super K, ? super V> builder, CacheLoader super K, V> loader) { super (new LocalCache(builder, checkNotNull(loader))); } // LoadingCache methods @Override public V get (K key) throws ExecutionException { return localCache.getOrLoad(key); } @Override public V getUnchecked (K key) { try { return get(key); } catch (ExecutionException e) { throw new UncheckedExecutionException(e.getCause()); } } @Override public void refresh (K key) { localCache.refresh(key); } @Override public final V apply (K key) { return getUnchecked(key); } // Serialization Support private static final long serialVersionUID = 1 ; @Override Object writeReplace () { return new LoadingSerializationProxy<>(localCache); } }
实际产品实现类LocalCache
上面两个产品类实际上,内部使用的是
LocalCache
来存储数据。我们再看下
LocalCache
的实现。
LocalCache
继承
AbstractCache
,我们先看
AbstractCache
:
@GwtCompatible public abstract class AbstractCache <K , V > implements Cache <K , V > { /** Constructor for use by subclasses. */ protected AbstractCache () {} @Override public V get (K key, Callable extends V> valueLoader) throws ExecutionException { throw new UnsupportedOperationException(); } @Override public void put (K key, V value) { throw new UnsupportedOperationException(); } @Override public void cleanUp () {} @Override public long size () { throw new UnsupportedOperationException(); } @Override public ConcurrentMap asMap () { throw new UnsupportedOperationException(); } }
再来看,
LocalCache
:
@GwtCompatible (emulated = true )class LocalCache <K , V > extends AbstractMap <K , V > implements ConcurrentMap <K , V > { /** How long after the last write to an entry the map will retain that entry. */ final long expireAfterWriteNanos; /** The default cache loader to use on loading operations. */ final @Nullable CacheLoader super