在翻閱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