設計模式實戰——開發中經常涉及到的建造者模式
阿新 • • 發佈:2020-03-04
> 本系列部落格是自己在學習設計模式過程中收集整理的文章集合,其他文章參看[設計模式傳送門](https://www.cnblogs.com/54chensongxia/p/12394160.html)
## 建造者模式簡介
建造者模式是一種建立型設計模式,這種模式具有很好的封裝性。使用建造者模式可以有效的封裝變化,在使用建造者模式的場景中,一般產品類和建造者類是比較穩定的,因此,將主要的業務邏輯封裝在導演類中對整體而言可以取得比較好的穩定性。
在建造者模式中,客戶端不必知道產品內部組成的細節,將產品本身與產品的建立過程解耦,使得相同的建立過程可以建立不同的產品物件。
可以更加精細地控制產品的建立過程 。將複雜產品的建立步驟分解在不同的方法中,使得建立過程更加清晰,也更方便使用程式來控制建立過程。
其次,建造者模式很容易進行擴充套件。如果有新的需求,通過實現一個新的建造者類就可以完成,基本上不用修改之前已經測試通過的程式碼,因此也就不會對原有功能引入風險。符合開閉原則。
建造者模式通常包含以下角色
> 抽象建造者類(builder):為建立product物件而指定各個元件的抽象介面
> 具體建造類(concreteBuilder):實現builder介面,重寫方法構建不同的表示
> 產品類(product):具體的產品
> 指揮者類(director):構建一個使用builder介面的物件
![](https://img2020.cnblogs.com/blog/1775037/202003/1775037-20200304154745040-736866423.png)
## JDK中的建造者模式——StringBuilder
`StringBuilder`類是JDK中比較典型的建造者模式的體現。我先看下這個類的類圖:
![](https://img2020.cnblogs.com/blog/1775037/202003/1775037-20200304145024450-1055571589.png)
我們根據上圖看下各個角色的對應情況:
- StringBuilder:指揮者角色,持有具體建造者的引用,由於StringBuilder繼承了AbstractStringBuilder,這裡StringBuilder通過super來作為具體建造者的引用。
- AbstractStringBuilder:具體建造者,它實現了appendable介面的append(Character c)方法。
- Appendable:抽象建造者,定義了建立物件的介面。
- String:產品角色。
另外,StringBuffer也是使用了建造者模式。兩者的唯一區別就是StringBuffer使用了synchronized來保證執行緒安全,而StringBuilder不是執行緒安全的。
其實,建造者模式在我們平時開發中更多的體現就是像`StringBuilder.append`這樣的鏈式呼叫。其中StringBuilder就是指揮官角色,append方法是建立產品細節的過程,當我們建立完產品後就可以呼叫toString方法生成具體的產品。比如下面的程式碼
```java
String str = new StringBuilder().append()
.append()
.toString();
```
這樣類似的程式碼還有很多,比如
```java
Header header = new HeaderBuilder()
.setClientId(SOAHeader.SOAP_CLIENT_ID)
.setCorrelationId(SOAHeader.SOAP_CORRELATION_ID)
.buildHeader();
```
在平時開發過程中,如果我們看到上面類似的程式碼,可能就是用了建造者模式。我們平時要留意這樣的程式碼,看看作者為什麼要這樣設計系統,對我們的程式碼提升很有幫助。這也是我整理總結開發框架中常見設計模式的用意。
## Spring中的建造者模式
Spring是Java開發者最常用的開發框架。有人說Spring的原始碼就是設計模式的盛宴。看Spring的原始碼是很好的學習設計模式的一種方式。
在Spring框架中,常涉及到的建造者模式有:
- UriComponentsBuilder
- BeanDefinitionBuilder
其中`BeanDefinitionBuilder`較底層,我們平時不太會用到。這裡我們通過`UriComponentsBuilder`來講述Spring中的建造者模式。
下面的程式碼中,我們Spring中的restTemplate工具呼叫遠端介面。在呼叫前需要先構建URL引數。這邊就是使用了`UriComponentsBuilder`來構建的。
```java
UriComponents uriComponents = UriComponentsBuilder.fromHttpUrl("127.0.0.1:8080").
path("/test").build(true);
URI uri = uriComponents.toUri();
RequestEntity requestEntity = RequestEntity.post(uri).
//新增cookie(這邊有個問題,假如我們要設定cookie的生命週期,作用域等引數我們要怎麼操作)
header(HttpHeaders.COOKIE,"key1=value1").
//新增header
header(("MyRequestHeader", "MyValue")
accept(MediaType.APPLICATION_JSON).
contentType(MediaType.APPLICATION_JSON).
body(requestParam);
ResponseEntity responseEntity = restTemplate.exchange(requestEntity,JSONObject.class);
//響應結果
JSONObject responseEntityBody = responseEntity.getBody();
```
Spring中的設計模式有很多,我們平時使用時可以細心關注下。相信肯定會有收穫。
## MyBatis中的建造者模式
MyBatis中最經典的建造者模式肯定是獲取SqlSessionFactory的過程。
下面是獲取SqlSessionFactory的典型用法。
```
CopyClassPathResource resource = new ClassPathResource("mybatis-config.xml");
InputStream inputStream = resource.getInputStream();
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
```
通過上面程式碼發現,建立SqlSessionFactory的程式碼在SqlSessionFactoryBuilder中,進去一探究竟:
```
Copy//整個過程就是將配置檔案解析成Configration物件,然後建立SqlSessionFactory的過程
//Configuration是SqlSessionFactory的一個內部屬性
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
```
程式碼比較簡單,就不具體分析了。這裡還是對號入座,列舉下各個角色。
- SqlSessionFactoryBuilder:指揮者角色
- BaseBuilder:抽象Builder
- XMLConfigBuilder:具體的Builder
- SqlSessionFactory:需要被建立的產品
## 感悟
學習設計模式光學習不行,因為這個東西比較抽象。你必須結合具體的專案框架來看才能有比較深的感悟。
平時如果有空餘時間可以自己動手,使用設計模式寫一些小的框架。還有就是多看看那些主流開源框架的原始碼,這些程式碼中都有很對設計模式的體現。結合設計模式的理論知識,看看這些框架中為什麼要用這些模式,比你光看肯定收穫