马上注册,结交更多好友,享用更多功能^_^
您需要 登录 才可以下载或查看,没有账号?立即注册
x
讲设计模式的大多都很枯燥,而且都用一些比较专业的术语去描述设计模式的角色。GOF的《设计模式》更吓人,推荐一本叫做《大话设计模式》的书籍,把设计模式讲的很有趣,配图和故事都很萌。前两天重构公司代码的一个http client的二次封装类,众所周知的是http请求参数很多,除了正常的请求url、参数之外还有http头部信息,这些东西放请求方法的参数里简直没法看:public static String httpPost(String url,String json,String charset,int socketTimeout,int connectTimeOut,Map<String,String> headers){
//...此处省略一万个字
}
在《代码整洁之道》中,方法参数是禁止超过3个的,超过3个人阅读起来非常不舒服,这里已经6个了。《代码整洁之道》提倡把超过3个参数的封装到类中。也就是说我们可以把httpPost方法中的6个参数抽成这样:
public static String httpPost(HttpPostParam param){
//...此处继续省略一万个字
}
那HttpPostParam自然就是对6个参数的setget了:
public class HttpPostParam{
private String url;
private String json;
private String charset;
private Integer socketTimeout;
private Integer connectTimeOut;
private Map<String,String> headers;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getJson() {
return json;
}
public void setJson(String json) {
this.json = json;
}
public String getCharset() {
return charset;
}
public void setCharset(String charset) {
this.charset = charset;
}
public Integer getSocketTimeout() {
return socketTimeout;
}
public void setSocketTimeout(Integer socketTimeout) {
this.socketTimeout = socketTimeout;
}
public Integer getConnectTimeOut() {
return connectTimeOut;
}
public void setConnectTimeOut(Integer connectTimeOut) {
this.connectTimeOut = connectTimeOut;
}
public Map<String, String> getHeaders() {
return headers;
}
public void setHeaders(Map<String, String> headers) {
this.headers = headers;
}
}
可以说这是我们最常用的方法了,用setget为属性进行赋值。但是在使用该类的时候,我们需要new一个对象然后挨个set方法调用,如果我们的参数更多,那书写起来更不方便,看着也不舒服,我们的代码将会被大量的set方法调用所污染:
@Test
public void testHttpPost(){
HttpPostParam param=new HttpPostParm();
param.setUrl("http://www.baidu.com");
param.setJson("{'test':'test'}");
param.setCharset("UTF-8");
param.setSocketTimeout(1000);
param.setConnectTimeOut(1000);
Map<String,String> headers=new HashMap<>();
headers.put("Connection","Keep-Alive");
param.setHeaders(headers);
HttpClientUtil.httpPost(param);//实际调用方法就一行
}
有人可能会说给HttpPostParam加入构造函数把参数传进去,尤其是可以使用编译器的快捷键自动生成带参数的构造函数:
public HttpPostParam(String url, String json, String charset, Integer socketTimeout, Integer connectTimeOut, Map<String, String> headers) {//构造函数参数依然看着这么累
this.url = url;
this.json = json;
this.charset = charset;
this.socketTimeout = socketTimeout;
this.connectTimeOut = connectTimeOut;
this.headers = headers;
}
public HttpPostParam(String url, String json, String charset, Integer socketTimeout, Integer connectTimeOut) {//如果有的参数不用还得写重载
this.url = url;
this.json = json;
this.charset = charset;
this.socketTimeout = socketTimeout;
this.connectTimeOut = connectTimeOut;
}
这样调用起来自然很方便:
@Test
public void testHttpPost(){
Map<String,String> headers=new HashMap<>();
headers.put("Connection","Keep-Alive");
param.setHeaders(headers);
HttpClientUtil.httpPost(new HttpPostParm(
"http://www.baidu.com",
"{'test':'test'}",
"UTF-8",
1000,
1000,
headers
));//实际调用方法就一行
}
但是,当这样参数加减可就要重新生成构造函数了。
于是想想之前用的google公司开源的protobuf用到的builder,那也来试试吧!
首先模仿下protobuf的Builder使用方法:
@Test
public void testHttpPost(){
Map<String,String> headers=new HashMap<>();
headers.put("Connection","Keep-Alive");
param.setHeaders(headers);
HttpClientUtil.httpPost(
HttpPostParam.Builder.newBuilder().
.setUrl("http://www.baidu.com")
.setJson("{'test':'test'}")
.setCharset("UTF-8")
.setSocketTimeout(1000)
.setConnectTimeOut(1000)
.setHeaders(headers)
.build()
)); //这似乎好读了一些,直接build构建对象,同时不要的属性可以不用set,不会像构造函数那样需要重载好几个版本
}
整体来说都很和谐,接下来看看实现:
pulic class HttpPostParam{
private String url;
private String json;
private String charset;
private Integer socketTimeout;
private Integer connectTimeOut;
private Map<String,String> headers;
//④构造函数中把builder传进来,把builder中赋过值的属性逐一赋值给成员变量,这样对于外部调用者直接get就拿到值了
private HttpPostParam(Builder builder) {
this.url = builder.url;
this.json = builder.json;
this.charset = builder.charset;
this.socketTimeout = builder.socketTimeout;
this.connectTimeOut = builder.connectTimeOut;
this.headers = builder.headers;
}
static class Builder {
private String url;
private String json;
private String charset;
private Integer socketTimeout;
private Integer connectTimeOut;
private Map<String, String> headers;
//①创建个builder对象
public static Builder newBuilder() {
return new Builder();
}
//③最后创建HttpPostParam对象并且把builder自己传给HttpPostParam的构造函数
public HttpPostParam build() {
return new HttpPostParam(this);
}
//②对builder中的成员进行赋值。同时返回this,这样做到链式编程。其他的成员属性相同,略。
public Builder setUrl(String url) {
this.url = url;
return this;
}
public Builder setJson(String json) {
this.json = json;
return this;
}
public Builder setCharset(String charset) {
this.charset = charset;
return this;
}
public Builder setSocketTimeout(Integer socketTimeout) {
this.socketTimeout = socketTimeout;
return this;
}
public Builder setConnectTimeOut(Integer connectTimeOut) {
this.connectTimeOut = connectTimeOut;
return this;
}
public Builder setHeaders(Map<String, String> headers) {
this.headers = headers;
return this;
}
}
public String getUrl() {
return url;
}
public String getJson() {
return json;
}
public String getCharset() {
return charset;
}
public Integer getSocketTimeout() {
return socketTimeout;
}
public Integer getConnectTimeOut() {
return connectTimeOut;
}
public Map<String, String> getHeaders() {
return headers;
}
}
可以看到HttpPostParam被分成两层,外层只有get方法用来获取属性,而内层构造了个静态内部类叫Builder。通过Builder的newBuilder来创建Builder对象,然后调用Builder中的set方法对属性进行赋值,需要注意的是set方法返回了builder本身,这样在外部调用的时候就可以做到链式编程,即:setUrl().setHeaders()...,在赋值之后使用对外提供的build方法把当前对象直接通过构造参数传给HttpPostParam的实例,HttpPostParam的构造函数中对外层的HttpPostParam成员变量进行了赋值。
整体流程其实不复杂,总体来说Builder模式会比单纯的setget方法构造出的类要复杂一些而且代码量多,但是要比单纯的setget和构造函数传参要灵活可变,把可变性进行了封装,api可读性强,操作也简单了一些。
另外,如果需要一些必要的参数,可以直接在Builder的newBuilder中指出:
//其他的代码略,这里只给出newBuilder的写法
public static Builder newBuilder(String url) {
this.url=url;
return new Builder();
}
builder模式在设计模式中属于建造类型的模式,相对来说比较简单。推荐使用builder模式代替通常的bean或entity的setget方法。
|