1. 程式人生 > 實用技巧 >Java設計模式的常見應用場景

Java設計模式的常見應用場景

一、Java I/O中的設計模式

1、介面卡模式

介面卡模式就是把一個類的介面變換成客戶端所能接受的另一種介面,從而使兩個介面不匹配而無法在一起工作的兩個類能夠在一起工作。通常被用在一個專案需要引用一些開源框架來一起工作時,這些框架的內部都有一些關於環境資訊的介面,需要從外部引入,但是外部的介面不一定能匹配,在這種情況下,就需要介面卡模式來轉換介面。

Java的I/O類庫中有許多這樣的需求,如將字串轉成位元組資料儲存到檔案中,將位元組資料變成資料流等。具體來說,InputStreamReader和OutputStreamWriter就是介面卡的體現。InputStreamReader實現了Reader介面,並且持有InputStream的引用,其作用是將InputStream適配到Reader。源角色就是InputStream代表的例項物件,目標角色就是Reader類。OutputStreamWriter也是類似的方式。

2、裝飾器模式

裝飾器的作用是使得被裝飾者功能更強大,而且裝飾前後的使用方式不變。Java I/O類庫中有許多不同的功能組合情況,這些不同的功能組合都是使用裝飾器模式實現的。以FileInputStream為例,其類結構如下:

由於java I/O庫需要很多效能的各種組合,如果這些效能都是用繼承來實現,那麼每一種組合都需要一個類,這樣就會造成大量行重複的類出現。如果採用裝飾模式,那麼類的數目就會大大減少,效能的重複也可以減至最少。因此裝飾模式是java I/O庫基本模式。裝飾模式的引進,造成靈活性和複雜性的提高。因此在使用 java I/O 庫時,必須理解java I/O庫是由一些基本的原始流處理器和圍繞它們的裝飾流處理器所組成的。
InputStream類是以抽象元件的形式存在,而FileInputStream就是具體元件,它實現了抽象介面的所有方法,並且持有InputStream物件的引用。FileInputStream就是一個裝飾類,而BufferInputStream是這個裝飾類的具體實現者,它給InputStream加入了新的功能,使得InputStream讀取的資料儲存在記憶體中,從而提高讀取效能。

3、適配模式和裝飾模式的區別

適配模式是為了處理兩個藉口不一致,改變現有介面使其匹配。
裝飾模式是在不改變現有介面的前提下,加入新的功能。

二、Javac中的訪問者模式

javac 是java語言程式設計編譯器。全稱javacompilation。javac工具讀由java語言編寫的類和介面的定義,並將它們編譯成位元組程式碼的class檔案。javac 可以隱式編譯一些沒有在命令列中提及的原始檔。用 -verbose 選項可跟蹤自動編譯。當編譯原始檔時,編譯器常常需要它還沒有識別出的型別的有關資訊。對於原始檔中使用、擴充套件或實現的每個類或介面,編譯器都需要其型別資訊。這包括在原始檔中沒有明確提及、但通過繼承提供資訊的類和介面。
Javac的編譯過程涉及許多語法分析,所有就有語法分析器、語義分析器和程式碼生成器,期間需要多次遍歷語法樹。然而每次遍歷語法樹都會進行不同的處理動作,這是如何實現的呢?就是通過採用訪問者模式設計的,每次遍歷都是一次訪問者的執行過程。
訪問者模式可以使資料結構和對資料結構的操作解耦,使得增加對資料結構的操作不需要去修改資料結構,也不必去修改原有的操作,從而執行時再定義新的Visitor實現者就行了。在Javac中不同的編譯階段都定義了不同的訪問者模式實現。

三、Tomcat中的設計模式

1、門面模式

Tomcat中門面設計模式使用得很多,因為Tomcat中有很多元件,每個元件要相互互動資料,用門面設計模式隔離資料是個很好的方法。

可以看到,HttpRequestFacade類封裝了HttpRequest介面,能夠提供資料,通過HttpRequestFacade訪問到的資料被代理到HttpReauest中、通常被封裝的物件都被設為Private或者Protectd的,防止在Facade中直接訪問。

2、觀察者模式

Tomcat中觀察者模式也有多處使用,前面講的控制組件生命週期的Lifecycle就是這 種模式的體現,還有對Servlet例項的建立、Session的管理、Container等都是同樣的原理。 下面主要看一下Lifecycle的具體實現。
Lifecycle的觀察者模式結構圖如圖所示。

在上面的結構圖中,LifecycleListener代表的是抽象觀察者,它定義一個lifecycleEvent 方法,這個方法就是當主題變化時要執行的方法。ServerLifecycleListener代表的是具體的 觀察者,它實現了 LifecycleListener介面的方法,就是這個具體的觀察者具體的實現方式。 Lifecycle介面代表的是抽象主題,它定義了管理觀察者的方法和它所要做的其他方法。而 StandardServer代表的是具體主題,它實現了抽象主題的所有方法。這裡Tomcat對觀察者 做了擴充套件,謂加了另外兩個類:LifecycleSupport和LifecycleEvent,它們作為輔助類擴充套件 了觀察者的功能。LifecycleEvent使得可以定義事件類別,不同的事件可區別處理,更加靈活。LifecycleSupport類代理了主題對多觀察者的管理,將這個管理抽出來統一實現,以 後如採修改只要修改LifecycleSupport類就可以了,不需要去修改所有的具體主題,因為 所有具體主題對觀察者的操作都被代理給LifecycleSupport類了。這可以認為是觀察者模式的改進版。

3、命令設計模式

Tomcat中命令模式在Connector和Container元件之間有體現,Tomcat作為一個應用 伺服器,無疑會接收到很多請求,如何分配和執行這些請求是必須的功能。
下面分析一下Tomcat是如何實現命令模式的,下圖是Tomcat命令模式的結構圖。

Connector作為抽象請求者,HttpConnector作為具體請求者。HttpProcessor作為命令。 Container作為命令的抽象接受者,ContainerBase作為具體的接受者。客戶端就是應用伺服器Server元件了。Server首先建立命令請求者HttpConnector物件,然後建立命令 HttpProcessor物件。再把命令物件交給命令接受者ContainerBase容器來處理,命令最終是被Tomcat的Container執行的。命令可以以佇列的方式進來,Container也可以以不同的 方式來處理請求,如HTTP1.0協議和HTTP1.1的處理方式就不同。

4、責任鏈模式

Tomcat 中一個最容易發現的設計模式就是責任鏈設計模式,這個設計模式也是 Tomcat 中 Containe設計的基礎,整個容器就是通過一個鏈連線在一起的,這個鏈一直將請求正確地傳遞給最終處理請求的那個 Servlet 。在Tomcat中這種設計模式兒乎被完整地使用,Tomcat的容器設定就是責任鏈模式,從Engine到Host再到Cortex,一直到Wrapper都通過這個鏈傳遞請求。

四、Spring中的設計模式

1、簡單工廠模式

又叫做靜態工廠方法(StaticFactory Method)模式,但不屬於23種GOF設計模式之一。
簡單工廠模式的實質是由一個工廠類根據傳入的引數,動態決定應該建立哪一個產品類。
spring中的BeanFactory就是簡單工廠模式的體現,根據傳入一個唯一的標識來獲得bean物件,但是否是在傳入引數後建立還是傳入引數前建立這個要根據具體情況來定。如下配置,就是在 HelloItxxz 類中建立一個 itxxzBean。

<beans>
    <bean id="singletonBean" class="com.kang.HelloItxxz">
        <constructor-arg>
            <value>Hello! 這是singletonBean!value>
        </constructor-arg>
   </ bean>

    <bean id="itxxzBean" class="com.kang.HelloItxxz"
        singleton="false">
        <constructor-arg>
            <value>Hello! 這是itxxzBean! value>
        </constructor-arg>
    </bean>

</beans>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

2、工廠方法模式

通常由應用程式直接使用new建立新的物件,為了將物件的建立和使用相分離,採用工廠模式,即應用程式將物件的建立及初始化職責交給工廠物件。
一般情況下,應用程式有自己的工廠物件來建立bean.如果將應用程式自己的工廠物件交給Spring管理,那麼Spring管理的就不是普通的bean,而是工廠Bean。
以工廠方法中的靜態方法為例講解一下:

import java.util.Random;
public class StaticFactoryBean {
      public static Integer createRandom() {
           return new Integer(new Random().nextInt());
       }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

建一個config.xm配置檔案,將其納入Spring容器來管理,需要通過factory-method指定靜態方法名稱,createRandom方法必須是static的,才能找到

<bean id="random"
class="example.chapter3.StaticFactoryBean" factory-method="createRandom" scope="prototype"
/>
  • 1
  • 2
  • 3

3、單例模式

保證一個類僅有一個例項,並提供一個訪問它的全域性訪問點。
spring中的單例模式完成了後半句話,即提供了全域性的訪問點BeanFactory。但沒有從構造器級別去控制單例,這是因為spring管理的是是任意的java物件。 Spring下預設的bean均為singleton,可以通過singleton=“true|false” 或者 scope=“?”來指定

4、代理模式

在Spring的Aop中,使用的Advice(通知)來增強被代理類的功能。Spring實現這一AOP功能的原理就使用代理模式(1、JDK動態代理。2、CGLib位元組碼生成技術代理。)對類進行方法級別的切面增強,即,生成被代理類的代理類, 並在代理類的方法前,設定攔截器,通過執行攔截器重的內容增強了代理方法的功能,實現的面向切面程式設計。

5、模板方法模式

定義一個操作中的演算法的骨架,而將一些步驟延遲到子類中。Template Method使得子類可以不改變一個演算法的結構即可重定義該演算法的某些特定步驟。
Template Method模式一般是需要繼承的。這裡想要探討另一種對Template Method的理解。spring中的JdbcTemplate,在用這個類時並不想去繼承這個類,因為這個類的方法太多,但是我們還是想用到JdbcTemplate已有的穩定的、公用的資料庫連線,那麼我們怎麼辦呢?我們可以把變化的東西抽出來作為一個引數傳入JdbcTemplate的方法中。但是變化的東西是一段程式碼,而且這段程式碼會用到JdbcTemplate中的變數。怎麼辦?那我們就用回撥物件吧。在這個回撥物件中定義一個操縱JdbcTemplate中變數的方法,我們去實現這個方法,就把變化的東西集中到這裡了。然後我們再傳入這個回撥物件到JdbcTemplate,從而完成了呼叫。這可能是Template Method不需要繼承的另一種實現方式吧。

6、策略模式

Spring中的策略模式使用多如牛毛,所謂策略模式就是定義了演算法族,分別封裝起來,讓他們之前可以互相轉換,此模式然該演算法的變化獨立於使用演算法的客戶。Spring的事務管理機制就是典型的策略模式,Spring事務策略是通過PlatformTransactionManager介面實現的,它是整個Spring事務的核心。它是對事務策略的一個高度抽象,不依賴於任何具體的事務策略,而對於底層的具體的事務策略它相應的有不同的實現類。而對於不同的事務策略的切換通常由Spring容器來負責管理,應用程式既無須與具體的事務API耦合,也無須與特定的實現類耦合而將應用和持久化技術,事務API徹底分離開來。

五、SpringMVC中的模板模式

所謂模板模式就是定義一個操作中的演算法的骨架,而將一些步驟延遲到子類中。SpringMVC在保證整個框架流程穩定的情況下,預留很多口子,而這些口子都是所謂的模板方法,可以自由指定,從而保證了靈活性,接下來的很多使用最佳實踐都是基於這種設計模式才可以實現。例如,下面的程式碼中doResolveException(..)就是一個口子,子類方法doResolveException(..)可以定義具體如何處理異常。

public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
        Exception ex) {
    if (shouldApplyTo(request, handler)) {
        logException(ex, request);
        prepareResponse(ex, response);
        return doResolveException(request, response, handler, ex);
    } else {
        return null;
    }
}

protected abstract ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response,
        Object handler, Exception ex);