上一篇文章中举的例子,关于Fluent编程风格的实现,其实并不太符合我本来的猜想。这篇想着再举一个我觉得比较合适的例子。
使用Curator创建Zookeeper会话时,有两种方式。
方式一:调用CuratorFrameworkFactory的静态方法 newClient()
CuratorFramework client=CuratorFrameworkFactory.newClient(……);
方式二:使用FluentAPI风格调用
CuratorFramework client=CuratorFrameworkFactory.builder().connectString(connectString).sessionTimeoutMs(sessionTimeoutMs).connectionTimeoutMs(connectionTimeoutMs).retryPolicy(retryPolicy).build();
看第一种方式newClient,其内部实现就是方式二,所以两种方式本质上是一样的。
public static CuratorFrameworkFactory.Builder builder() {
return new CuratorFrameworkFactory.Builder();
}
public static CuratorFramework newClient(String connectString, RetryPolicy retryPolicy) {
return newClient(connectString, DEFAULT_SESSION_TIMEOUT_MS, DEFAULT_CONNECTION_TIMEOUT_MS, retryPolicy);
}
public static CuratorFramework newClient(String connectString, int sessionTimeoutMs, int connectionTimeoutMs, RetryPolicy retryPolicy) {
return builder().connectString(connectString).sessionTimeoutMs(sessionTimeoutMs).connectionTimeoutMs(connectionTimeoutMs).retryPolicy(retryPolicy).build();
}
所以CuratorFrameworkFactory是将CuratorFramework的“建造”过程交给其内部类Builder来完成。
1.首先通过builder方法,返回一个建造者Builder
public static CuratorFrameworkFactory.Builder builder() {
return new CuratorFrameworkFactory.Builder();
}
2.然后开始采用类似于连缀的方式,调用connectString、sessionTimeoutMs等方法,因为这些方法的返回值都一样,所以能不断地“点下去”(这个比较符合我本来的猜想)
public CuratorFrameworkFactory.Builder connectString(String connectString) {
this.ensembleProvider = new FixedEnsembleProvider(connectString);
return this;
}
public CuratorFrameworkFactory.Builder sessionTimeoutMs(int sessionTimeoutMs) {
this.sessionTimeoutMs = sessionTimeoutMs;
return this;
}
public CuratorFrameworkFactory.Builder connectionTimeoutMs(int connectionTimeoutMs) {
this.connectionTimeoutMs = connectionTimeoutMs;
return this;
}
3.最后,通过build方法,返回“产品”——CuratorFramework
public CuratorFramework build() {
return new CuratorFrameworkImpl(this);
}
我们再来回顾一下上一篇文章中的例子:
client.create的返回值是CreateBuilder,creatingParentsIfNeeded就是CreateBuilder接口中的方法,该方法的返回值是ProtectACLCreateModePathAndBytesable,它继承了CreateModable,所以能在此基础上调用withMode。而且还继承了ACLBackgroundPathAndBytesable,这个接口又间接继承了PathAndBytesable,所以也能调用forPath方法。
调用create方法之后,可以调用这三个方法中的任意一个,但是正确的顺序,是先调用creatingParentsIfNeeded,withMode,forPath。纠正一下上篇文章中的说法。
所以总结一下,使用Fluent风格的代码,因满足方法的返回值类型是下一个方法所属的类或者父类。
设计模式
Curator框架中在创建会话上,使用了建造者模式,也叫生成器模式。上一张类图
CuratorFramework是产品
Builder是建造者
CuratorFrameworkFactory是指挥者
产品CuratorFramework的建造过程分为几步,这几步之间没有顺序要求,包括设置connectString,设置sessionTimeoutMs,设置connectionTimeoutMs。
对于每一步,具体如何实现由Builder来负责,并通过build方法,返回最终产品。而具体如何把这几步连成一个完整的建造流程,也就是组装成最终产品,由CuratorFrameworkFactory的newClient方法来负责,它负责调用这几个步骤进行组装,并调用Builder.build方法返回最后组装好的产品。
这样的好处是把建造的过程稳定在了CuratorFramework的newClient方法中,而对于建造的细节封装在了Builder。这样就将两者分离。Factory不用关心Builder在创建产品时每一个都怎么实现的,它只知道我只要按着这个固定生产流程,就能得到产品。建造者模式针对流程不怎么发生变化,但是创建流程复杂,分多步,每步可能有不同的实现。
这里虽然通过Factory的newClient方法将产品的生产流程固定下来,但这种固定强调每一步不可或缺,并没有强调这些步骤之间的顺序。从局部看这一块,要与模板方法区分开来。
总结
1.Fluent风格代码实现原理
2.Curator在创建会话时对建造者模式的应用