随便聊聊
之前看设计模式的步骤是:第一章:单例模式 -> 第二章:工厂模式 ->好像没啥神奇的,不看了。。 ;过段时间, 单例模式 -> 第二章:工厂模式 -> 算了,不看了。。;要面试了,硬着头皮看完一遍,第二天,我看的是啥??? 这种低效且重复的学习方式不应该再发生了。我总结了一下,将设计模式的书很多,风格也各式各样,但对我来书,都是入门,只是去了解这种模式是干啥的,看完就忘(就算按书上的例子自己模仿一遍)。所以我就认为设计模式不是初学者可以掌握的,所以我不学了 ,本片完 。 。 。 开玩笑,不学怎么变成高手呢?怎么装杯呢? 根据之前学习的经验,我发现记不住的原因就是自己的理解不深刻,怎么可以理解深刻呢?除了天赋异禀,就是勤学多练,ok,怎么练呢?整天crud,各种框架把这些设计模式整的明明白白的,就是做填空题,哪有机会练呢?于是,我想,既然框架中用,那我就看看他们怎么用的,这样跟别人讲的时候也不会干巴巴的,还能无形之间告诉别人自己看了很多源码。 zh
正文
建造者模式还是很常见的,在google提供的guava工具包中,有LoadingCache这么一个接口,可以用作本地缓存,构造这个接口的实现:
LoadingCache<String, Object> cache
= CacheBuilder.newBuilder()
// 设置并发级别为8,并发级别是指可以同时写缓存的线程数
.concurrencyLevel(500).refreshAfterWrite(1, TimeUnit.NANOSECONDS)
.concurrencyLevel(500).refreshAfterWrite(2, TimeUnit.SECONDS)
// 设置写缓存后x秒钟过期
.expireAfterWrite(1L, TimeUnit.MINUTES)
// 设置缓存容器的初始容量
.initialCapacity(100)
// 设置缓存最大容量为500,超过500之后就会按照LR0移除缓存项
.maximumSize(500)
// 设置要统计缓存的命中率
.recordStats()
// 设置缓存的移除通知
.removalListener((notification)->{
System.out.println("remove "+ notification.getKey());
})
// build方法中可以指定CacheLoader,在缓存不存在时通过CacheLoader的实现自动加载缓存
.build(new CacheLoader<String, Object>() {
@Override
public Object load(String key) throws Exception {
//System.out.println("load Object " + key);
return null;
}
});
可以看到,建造这模式适用于,构造参数多且复杂的场景。 可以看到,建造这模式适用于,构造参数多且复杂的场景。
那么根据建造者模式的方法论,我们去找一下建造LoadingCache所需要的角色分别对应了那些类
建造者(Builder)模式的主要角色如下。 产品角色(Product):它是包含多个组成部件的复杂对象,由具体建造者来创建其各个滅部件。 抽象建造者(Builder):它是一个包含创建产品各个子部件的抽象方法的接口,通常还包含一个返回复杂产品的方法 getResult()。 具体建造者(Concrete Builder):实现 Builder 接口,完成复杂产品的各个部件的具体创建方法。 指挥者(Director):它调用建造者对象中的部件构造与装配方法完成复杂对象的创建,在指挥者中不涉及具体产品的信息。
- 所谓的
Product当然是我们需要的对象LoadingCache啦,因为它是接口,所以我们实际建造的是它的其中一个实现类,这里我们建造Product的是com.google.common.cache.LocalCache.LocalLoadingCache - 抽象建造者(Builder)这里就是
com.google.common.cache.CacheBuilder,这里的getResult()就是该类的com.google.common.cache.CacheBuilder#build(com.google.common.cache.CacheLoader<? super K1,V1>)方法 - 具体建造者(Concrete Builder)在这里只有一个建造者,所以没有体现,但是我们可以明白,当一个产品的各个组成完全不同的话,可以使用多个建造者,每个建造者建造不同的产品。
- 指挥者,该角色来指挥建造的过程,比如盖房子要先打地基,再砌墙,最后加房顶。这里使用链式编程,没有使用该角色。
然后,来看一下建造removalListener的源码
com.google.common.cache.CacheBuilder#removalListener
@CheckReturnValue
public <K1 extends K, V1 extends V> CacheBuilder<K1, V1> removalListener(RemovalListener<? super K1, ? super V1> listener) {
Preconditions.checkState(this.removalListener == null);
this.removalListener = (RemovalListener)Preconditions.checkNotNull(listener);
return this;
}
可以看出,复杂的建造过程由builder负责,我们创建对象是就不用关注了,只用知道需要组装那些组件即可。这也是我们使用建造者模式的原因,否则,每次创建对象都要写一大串复杂的代码。
总结
我并没有去过多的讲建造者模式的方法论,因为这些已经有太多的文章写了。我只是根据这些方法论去源码中印证,加深印象和理解,如果你了解过建造者模式,就不难看出,实际的建造者模式和设计模式的方法论中的还是有很大区别的,设计模式不是生搬硬套,要灵活应用。