在翻阅apache的http client的代码的时候,看到org.apache.http.client.utils.URIBuilder.java的写法,感受甚妙。特地分析一下源码,而且对比几种不一样的URI写法。html
Apache的HttpClientjava
假设咱们有一个HttpClient类
,这个类用来发起http请求而且作出响应。以下:apache
class HttpClient{ ... }
如今要把URI
做为参数,传递给HttpClient类
,好让它知道对谁发起http请求。URI
能够是诸如 https://www.google.com/#q=编程狗的博客 这样一个简单的string,一个简单的实现就是给HttpClient类
提供一个设置URL
的方法:编程
class HttpClient{ ... public void setURI(String uri){ ... } ... }
这应该是最简单的但倒是最不成熟的作法了。它有不少弊端,但咱们暂时不讲出来,且往下看。安全
如今咱们须要更换URI
,但又不是所有更换,什么意思呢?不妨分几个部分来看看URI
:函数
Scheme -> https Host -> www.google.com Path -> /#q=编程狗的博客
https
的可能换成http
,www.google.com
可能会换成www.google.com.hk
,/#q=编程狗的博客
可能换成其余的关键词。咱们暂且把这个过程称为参数的置换。若是咱们仅仅给客户端类HttpClient
提供一个setURI(String uri)
方法,上面的置换只能经过换掉整个URI实现了。好比把https://www.google.com/#q=编程狗的博客 换成 http://www.google.com/#q=编程狗的博客,而没有办法直接把"https"换成"http"。ui
因而,咱们不妨提供再一下几个方法,以使咱们很方便的作出上面的置换:this
public void setScheme(String scheme){ ... } public void setHost(String host){ ... } public void setPath(String path){ ... }
这三个方法分别用来设置scheme
,host
和path
。为了叙述方便,没有对参数进行检查。仅仅提供这三个设置参数的方法是不能过实现置换的,由于对于传入的URI,必须先分割成scheme
,host
和path
三部分。固然,咱们能够的再添加一个splitURI
方法,实现分割;可是,咱们已然发现若是这样作了,HttpClient类
就有至少4个方法处理URI了,未来可能还会增长,HttpClient类
可能会有数十个方法的做用与http信息收发不相关,这样势必是一种混乱,而且影响扩展性,所以咱们不这样作。咱们应当有一个URI类
,专门处理URI的置换和分割。google
class URI{ //构造函数,默认把uri分割好。 public URI(String uri){ ... splitURI(); ... } //分割方法 private void splitURI(){ ... } //set方法 public void setScheme(String scheme){ ... } public void setHost(String host){ ... } public void setPath(String path){ ... } //get方法 public String getScheme(){ ... return scheme; } public String setHost(){ ... return host; } public String setPath(){ ... return path; } }
同时咱们也更新HttpClient类
:3d
class HttpClient{ ... public void setURI(URI uri){ ... } ... }
咱们能够这样使用它们:
URI uri = new URI("https://www.google.com/#q=编程狗的博客"); uri.getScheme();// https uri.getHost();// www.google.com uri.getPath();// /#q=编程狗的博客 ... uri.setScheme();// https(若是你确实须要从新设置) ... HttpClient client = new HttpClient(); client.setURI(uri);
到如今为止已经能够应付关于URI的不少需求了,可是咱们若是要求URI类是一个不可变
的类,为何不可变
呢?由于不可变
更加安全。但那些set方法就没有用处了。apache把URI的set方法去掉了,若是想设置参数,如今只能经过构造函数:
//重载构造函数 public URI(String str){ ... } /** * @param scheme Scheme name * @param userInfo User name and authorization information * @param host Host name * @param port Port number * @param path Path * @param query Query * @param fragment Fragment */ public URI(String scheme, String userInfo, String host, int port, String path, String query, String fragment){ ... } ... //用法如 URI uri = new URI("http","username:program-dog","program-dog.blogspot.com","/","","");
咱们至少有7个参数,若是要知足各类需求的组合,恐怕总共要提供∑(C^7^~i~)(i=1~7)种构造函数,显然不现实。然而,URIBuilder
既能够造出一个不可变
的URI,又能够兼顾N种参数。URIBuilder
能够这样用:
// http://www.google.com/search?q=编程狗的博客&btnG=Google+Search&aq=f&oq= URI uri = new URIBuilder() .setScheme("http") .setHost("www.google.com") .setPath("/search") .setParameter("q", "编程狗的博客") .setParameter("btnG", "Google Search") .setParameter("aq", "f") .setParameter("oq", "") .build();
URIBuilder
正是采用了Builder Pattern(建造者模式)。等号右边其实是一行,先建立一个URIBuilder
对象实例,调用实例的setScheme方法,此方法顺便返回URIBuilder
对象实例,刚刚返回的这个实例调用setHost方法,...,最后一个返回的URIBuilder
对象实例调用build方法,返回URI对象。它是如何实现的呢?
原来的URI类的set方法的基础上,添加一个返回值,返回URIBuilder
本身就够了:
class URIBuilder{ public URIBuilder setScheme(String scheme){ ... return this; } public URIBuilder setHost(String host){ ... return this; } public URIBuilder setPath(String path){ ... return this; } //built 方法,把参数拼接,而后返回一个URI类 public URI built(){ ... return uri; } }
因为URIBuilder
每次都返回它本身,因此能够连续的执行 set方法,最后经过built方法返回URI类。
到此为止,一个简单的URI类的写法已经介绍完毕了。咱们仍是尽可能把URI写成类对象,并使它不可变
,而且提供相应的Builder。
本做品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。
本文地址:https://program-dog.blogspot.com/2016/06/HowToWriteAHttpClientAboutURI.html