Java——設計模式(結構型模式)

1、適配器模式(不兼容結構的協調)

在適配器模式中引入了一個被稱爲適配器(Adapter)的包裝類,而它所包裝的對象稱爲適配者(Adaptee),即被適配的類。適配器的實現就是把客戶類的請求轉化爲對適配者的相應接口的調用。也就是說:當客戶類調用適配器的方法時,在適配器類的內部將調用適配者類的方法,而這個過程對客戶類是透明的,客戶類並不直接訪問適配者類。所以,適配器讓那些因爲接口不兼容而不能交互的類能夠一塊兒工做。(實際上客戶端拿到的對象,已是適配器的對象了)html

適配器模式能夠將一個類的接口和另外一個類的接口匹配起來,而無須修改原來的適配者接口和抽象目標類接口。適配器模式定義以下:java

適配器模式(Adapter Pattern):將一個接口轉換成客戶但願的另外一個接口,使接口不兼容的那些類能夠一塊兒 工做,其別名爲包裝器(Wrapper)。適配器模式既能夠做爲類結構型模式,也能夠做爲對象結構型模式。(實現不改代碼,只添加類和修改配置文件完成調用其它對象方法)web

  • Target(目標抽象類):目標抽象類定義客戶所需接口,能夠是一個抽象類或接口,也能夠是具體類。
  • Adapter(適配器類):適配器能夠調用另外一個接口,做爲一個轉換器,對Adaptee和Target進行適配,適配器類是適配器模式的核心,在對象適配器中,它經過繼承Target並關聯一個Adaptee對象使兩者產生聯繫。
  • Adaptee(適配者類):適配者即被適配的角色,它定義了一個已經存在的接口,這個接口須要適配,適配者類通常是一個具體類,包含了客戶但願使用的業務方法,在某些狀況下可能沒有適配者類的源代碼。

根據對象適配器模式結構圖,在對象適配器中,客戶端須要調用 request() 方法,而適配者類 Adaptee 沒有該 方法,可是它所提供的 specificRequest() 方法倒是客戶端所須要的。爲了使客戶端可以使用適配者類,須要提 供一個包裝類 Adapter,即適配器類。這個包裝類包裝了一個適配者的實例,從而將客戶端與適配者銜接起 來,在適配器的 request() 方法中調用適配者的 specificRequest() 方法。由於適配器類與適配者類是關聯關 系(也可稱之爲委派關係),因此這種適配器模式稱爲對象適配器模式。典型的對象適配器代碼以下所示:數據庫

/**
 * @author x5456
 */
public class AdaptorPattern {

    //抽象成績操做類:目標接口
    interface ScoreOperation {
        public int[] sort(int array[]); //成績排序

        public int search(int array[], int key); //成績查找
    }

    //快速排序類:適配者
    class QuickSort {
        public int[] quickSort(int array[]) {
            sort(array, 0, array.length - 1);
            return array;
        }

        public void sort(int array[], int p, int r) {
            int q = 0;
            if (p < r) {
                q = partition(array, p, r);
                sort(array, p, q - 1);
                sort(array, q + 1, r);
            }
        }

        public int partition(int[] a, int p, int r) {
            int x = a[r];
            int j = p - 1;
            for (int i = p; i <= r - 1; i++) {
                if (a[i] <= x) {
                    j++;
                    swap(a, j, i);
                }
            }
            swap(a, j + 1, r);
            return j + 1;
        }

        public void swap(int[] a, int i, int j) {
            int t = a[i];
            a[i] = a[j];
            a[j] = t;
        }
    }

    //二分查找類:適配者
    class BinarySearch {
        public int binarySearch(int array[], int key) {
            int low = 0;
            int high = array.length - 1;
            while (low <= high) {
                int mid = (low + high) / 2;
                int midVal = array[mid];
                if (midVal < key) {
                    low = mid + 1;
                } else if (midVal > key) {
                    high = mid - 1;
                } else {
                    return 1; //找到元素返回1
                }
            }
            return -1; //未找到元素返回-1
        }
    }

    //操做適配器:適配器
    class OperationAdapter implements ScoreOperation {
        private QuickSort sortObj; //定義適配者QuickSort對象
        private BinarySearch searchObj; //定義適配者BinarySearch對象

        public OperationAdapter() {
            sortObj = new QuickSort();
            searchObj = new BinarySearch();
        }

        public int[] sort(int array[]) {
            return sortObj.quickSort(array); //調用適配者類QuickSort的排序方法
        }

        public int search(int array[], int key) {
            return searchObj.binarySearch(array, key); //調用適配者類BinarySearch的查找方法
        }
    }
}

// 調用者
class Client {
    public static void main(String args[]) {
        ScoreOperation operation; //針對抽象目標接口編程
        operation = (ScoreOperation) XMLUtil.getBean(); //讀取配置文件,反射生成對象(只須要修改配置文件,改爲OperationAdapter就好了)
        int scores[] = {84, 76, 50, 69, 90, 91, 88, 96}; //定義成績數組
        int result[];
        int score;
        System.out.println("成績排序結果:");
        result = operation.sort(scores);
        //遍歷輸出成績 
        for (int i : scores) {
            System.out.print(i + ",");
        }
        System.out.println();
        System.out.println("查找成績90:");
        score = operation.search(result, 90);
        if (score != -1) {
            System.out.println("找到成績90。");
        } else {
            System.out.println("沒有找到成績90。");
        }
        System.out.println("查找成績92:");
        score = operation.search(result, 92);

        if (score != -1) {
            System.out.println("找到成績92。");
        } else {
            System.out.println("沒有找到成績92。");
        }
    }
}

類適配器

除了對象適配器模式以外,適配器模式還有一種形式,那就是類適配器模式,類適配器模式和對象適配器模式最大的區別在於適配器和適配者之間的關係不一樣,對象適配器模式中適配器和適配者之間是關聯關係,而類適配器模式中適配器和適配者是繼承關係編程

class Adapter extends Adaptee implements Target { 
    public void request() {
        specificRequest();
    }
}

因爲 Java、C# 等語言不支持多重類繼承,所以類適配器的使用受到不少限制,例如若是目標抽象類 Target 不 是接口,而是一個類,就沒法使用類適配器;此外,若是適配者 Adapter 爲最終(Final)類,也沒法使用類適 配器。在 Java 等面向對象編程語言中,大部分狀況下咱們使用的是對象適配器,類適配器較少使用。(類適配受java中沒法多繼承的限制)設計模式

優勢

不管是對象適配器模式仍是類適配器模式都具備以下優勢:數組

  • (1) 將目標類和適配者類解耦,經過引入一個適配器類來重用現有的適配者類,無須修改原有結構。
  • (2) 增長了類的透明性和複用性,將具體的業務實現過程封裝在適配者類中,對於客戶端類而言是透明的,並且提 高了適配者的複用性,同一個適配者類能夠在多個不一樣的系統中複用。
  • (3) 靈活性和擴展性都很是好,經過使用配置文件,能夠很方便地更換適配器,也能夠在不修改原有代碼的基礎上 增長新的適配器類,徹底符合「開閉原則」。

具體來講,類適配器模式還有以下優勢:app

  • 因爲適配器類是適配者類的子類,所以能夠在適配器類中置換一些適配者的方法,使得適配器的靈活性更強。

對象適配器模式還有以下優勢:編程語言

  • (1) 一個對象適配器能夠把多個不一樣的適配者適配到同一個目標;
  • (2) 能夠適配一個適配者的子類,因爲適配器和適配者之間是關聯關係,根據「里氏代換原則」,適配者的子類也 可經過該適配器進行適配。

缺點

類適配器模式的缺點以下:ide

  • (1) 對於 Java、C# 等不支持多重類繼承的語言,一次最多隻能適配一個適配者類,不能同時適配多個適配者;
  • (2) 適配者類不能爲最終類,如在 Java 中不能爲 final 類,C# 中不能爲 sealed 類;
  • (3) 在 Java、C# 等語言中,類適配器模式中的目標抽象類只能爲接口,不能爲類,其使用有必定的侷限性。

對象適配器模式的缺點以下:

  • 與類適配器模式相比,要在適配器中置換適配者類的某些方法比較麻煩。若是必定要置換掉適配者類的一個或多個方法,能夠先作一個適配者類的子類,將適配者類的方法置換掉,而後再把適配者類的子類當作真正的適配者進行適配,實現過程較爲複雜。

適用場景

  • (1) 系統須要使用一些現有的類,而這些類的接口(如方法名)不符合系統的須要,甚至沒有這些類的源代碼。
  • (2) 想建立一個能夠重複使用的類,用於與一些彼此之間沒有太大關聯的一些類,包括一些可能在未來引進的類一 起工做。

2、橋接模式(處理多維度變化)

引文

在正式介紹橋接模式以前,我先跟你們談談兩種常見文具的區別,它們是毛筆和蠟筆。假如咱們須要大中小 3 種 型號的畫筆,可以繪製 12 種不一樣的顏色,若是使用蠟筆,須要準備 3×12 = 36 支,但若是使用毛筆的話,只需 要提供 3 種型號的毛筆,外加 12 個顏料盒便可,涉及到的對象個數僅爲 3 + 12 = 15,遠小於36,卻能實現與 3 6 支蠟筆一樣的功能。若是增長一種新型號的畫筆,而且也須要具備 12 種顏色,對應的蠟筆需增長 12 支,而毛 筆只需增長一支。爲何會這樣呢?經過分析咱們能夠得知:在蠟筆中,顏色和型號兩個不一樣的變化維度(即兩 個不一樣的變化緣由)融合在一塊兒,不管是對顏色進行擴展仍是對型號進行擴展都勢必會影響另外一個維度;但在毛 筆中,顏色和型號實現了分離,增長新的顏色或者型號對另外一方都沒有任何影響。若是使用軟件工程中的術 語,咱們能夠認爲在蠟筆中顏色和型號之間存在較強的耦合性(從而違反了單一原則:一個類只幹一件事,不然若是另外一件事變了,那麼你這個類又要修改),而毛筆很好地將兩者解耦,使用起來很是靈 活,擴展也更爲方便。在軟件開發中,咱們也提供了一種設計模式來處理與畫筆相似的具備多變化維度的情 況,即本章將要介紹的橋接模式。

橋接模式(JDBC的設計採用該模式)

橋接模式是一種很實用的結構型設計模式,若是軟件系統中某個類存在兩個獨立變化的維度,經過該模式能夠將這兩個維度分離出來,使二者能夠獨立擴展,讓系統更加符合「單一職責原則」。與多層繼承方案不一樣,它將兩個獨立變化的維度設計爲兩個獨立的繼承等級結構,而且在抽象層創建一個抽象關聯,該關聯關係相似一條鏈接兩個獨立繼承結構的橋,故名橋接模式。

橋接模式用一種巧妙的方式處理多層繼承存在的問題,用抽象關聯取代了傳統的多層繼承,將類之間的靜態繼承關係轉換爲動態的對象組合關係,使得系統更加靈活,並易於擴展,同時有效控制了系統中類的個數。橋接定義以下:

橋接模式(Bridge Pattern):將抽象部分與它的實現部分分離,使它們均可以獨立地變化。它是一種對象結構型模式,又稱爲柄體(Handle and Body)模式或接口(Interface)模式。

橋接模式的結構與其名稱同樣,存在一條鏈接兩個繼承等級結構的橋,橋接模式結構如圖所示:

  • Abstraction(抽象類):用於定義抽象類的接口,它通常是抽象類而不是接口,其中定義了一個 Implementor(實現類接口)類型的對象並能夠維護該對象,它與 Implementor 之間具備關聯關係,它既能夠包含抽象業務方法,也能夠包含具體業務方法。
  • RefinedAbstraction(擴充抽象類):擴充由 Abstraction 定義的接口,一般狀況下它再也不是抽象類而是具體類,它實現了在 Abstraction 中聲明的抽象業務方法,在 RefinedAbstraction 中能夠調用在 Implementor 中定義的業務方法。
  • Implementor(實現類接口):定義實現類的接口,這個接口不必定要與 Abstraction 的接口徹底一致,事 實上這兩個接口能夠徹底不一樣,通常而言,Implementor 接口僅提供基本操做,而 Abstraction 定義的接口 可能會作更多更復雜的操做。Implementor 接口對這些基本操做進行了聲明,而具體實現交給其子類。經過 關聯關係,在 Abstraction 中不只擁有本身的方法,還能夠調用到 Implementor 中定義的方法,使用關聯 關係來替代繼承關係。
  • ConcreteImplementor(具體實現類):具體實現 Implementor 接口,在不一樣的 ConcreteImplementor 中提供基本操做的不一樣實現,在程序運行時,ConcreteImplementor 對象將替換其父類對象,提供給抽象 類具體的業務操做方法。

橋接模式是一個很是有用的模式,在橋接模式中體現了不少面向對象設計原則的思想,包括「單一職責原則」、「開閉原則」、「合成複用原則」、「里氏代換原則」、「依賴倒轉原則」等。熟悉橋接模式有助於咱們深刻理解這些設計原則,也有助於咱們造成正確的設計思想和培養良好的設計風格。

在使用橋接模式時,咱們首先應該識別出一個類所具備的兩個獨立變化的維度,將它們設計爲兩個獨立的繼承等級結構,爲兩個維度都提供抽象層,並創建抽象耦合。一般狀況下,咱們將具備兩個獨立變化維度的類的一些普通業務方法和與之關係最密切的維度設計爲「抽象類」層次結構(抽象部分),而將另外一個維度設計爲「實現類」層次結構(實現部分)。例如:對於毛筆而言,因爲型號是其固有的維度,所以能夠設計一個抽象的毛筆類,在該類中聲明並部分實現毛筆的業務方法,而將各類型號的毛筆做爲其子類;顏色是毛筆的另外一個維度,因爲它與毛筆之間存在一種「設置」的關係,所以咱們能夠提供一個抽象的顏色接口,而將具體的顏色做爲實現該接口的子類。在此,型號可認爲是毛筆的抽象部分,而顏色是毛筆的實現部分,結構示意圖如圖所示:

public class BridgingPattern {

    //像素矩陣類:輔助類,各類格式的文件最終都被轉化爲像素矩陣,不一樣的操做系統提供不一樣的方式顯示像素矩陣
    class Matrix {
        //此處代碼省略
    }

    //抽象圖像類:抽象類
    abstract class Image {
        protected ImageImp imp;

        public void setImageImp(ImageImp imp) {
            this.imp = imp;
        }

        public abstract void parseFile(String fileName);
    }

    //抽象操做系統實現類:實現類接口
    interface ImageImp {
        public void doPaint(Matrix m); //顯示像素矩陣
    }

    //Windows操做系統實現類:具體實現類
    class WindowsImp implements ImageImp {
        public void doPaint(Matrix m) { //調用Windows系統的繪製函數繪製像素矩陣
            System.out.print("在Windows操做系統中顯示圖像:");
        }
    }

    //Linux操做系統實現類:具體實現類
    class LinuxImp implements ImageImp {
        public void doPaint(Matrix m) { //調用Linux系統的繪製函數繪製像素矩陣
            System.out.print("在Linux操做系統中顯示圖像:");
        }
    }

    //Unix操做系統實現類:具體實現類
    class UnixImp implements ImageImp {
        public void doPaint(Matrix m) { //調用Unix系統的繪製函數繪製像素矩陣
            System.out.print("在Unix操做系統中顯示圖像:");
        }
    }

    //JPG格式圖像:擴充抽象類
    class JPGImage extends Image {
        public void parseFile(String fileName) {
            //模擬解析JPG文件並得到一個像素矩陣對象m;
            Matrix m = new Matrix();
            imp.doPaint(m);
            System.out.println(fileName + ",格式爲JPG。");
        }
    }

    //PNG格式圖像:擴充抽象類
    class PNGImage extends Image {
        public void parseFile(String fileName) {
            //模擬解析PNG文件並得到一個像素矩陣對象m;
            Matrix m = new Matrix();
            imp.doPaint(m);
            System.out.println(fileName + ",格式爲PNG。");
        }
    }
}


// 客戶端調用
class Client {
    public static void main(String args[]) {
        Image image = (Image) XMLUtil.getBean("image");
        ImageImp imp = (ImageImp) XMLUtil.getBean("os");
        image.setImageImp(imp);
        image.parseFile("小龍女");
    }
}

適配器模式與橋接模式的聯用

在某系統的報表處理模塊中,須要將報表顯示和數據採集分開,系統能夠有多種報表顯示方式也能夠有多種數據採集方式,如能夠從文本文件中讀取數據,也能夠從數據庫中讀取數據,還能夠從 Excel 文件中獲取數據。若是須要從 Excel 文件中獲取數據,則須要調用與Excel 相關的 API,而這個 API 是現有系統所不具有的,該 API 由廠商提供。使用適配器模式和橋接模式設計該模塊。

在設計過程當中,因爲存在報表顯示和數據採集兩個獨立變化的維度,所以可使用橋接模式進行初步設計;爲了 使用 Excel 相關的 API 來進行數據採集則須要使用適配器模式。系統的完整設計中須要將兩個模式聯用,如圖所示:

優勢

(1)分離抽象接口及其實現部分。橋接模式使用「對象間的關聯關係」解耦了抽象和實現之間固有的綁定關係,使 得抽象和實現能夠沿着各自的維度來變化。所謂抽象和實現沿着各自維度的變化,也就是說抽象和實現再也不在同 一個繼承層次結構中,而是「子類化」它們,使它們各自都具備本身的子類,以便任何組合子類,從而得到多維 度組合對象。

(2)在不少狀況下,橋接模式能夠取代多層繼承方案,多層繼承方案違背了「單一職責原則」,複用性較差,且類 的個數很是多,橋接模式是比多層繼承方案更好的解決方法,它極大減小了子類的個數。

(3)橋接模式提升了系統的可擴展性,在兩個變化維度中任意擴展一個維度,都不須要修改原有系統,符合「開閉原則」。

缺點

(1)橋接模式的使用會增長系統的理解與設計難度,因爲關聯關係創建在抽象層,要求開發者一開始就針對抽象層進行設計與編程。

(2)橋接模式要求正確識別出系統中兩個獨立變化的維度,所以其使用範圍具備必定的侷限性,如何正確識別兩個獨立維度也須要必定的經驗積累。

適用場景

(1)若是一個系統須要在抽象化和具體化之間增長更多的靈活性,避免在兩個層次之間創建靜態的繼承關係,經過 橋接模式可使它們在抽象層創建一個關聯關係。

(2)「抽象部分」和「實現部分」能夠以繼承的方式獨立擴展而互不影響,在程序運行時能夠動態將一個抽象化子 類的對象和一個實現化子類的對象進行組合,即系統須要對抽象化角色和實現化角色進行動態耦合。

(3)一個類存在兩個(或多個)獨立變化的維度,且這兩個(或多個)維度都須要獨立進行擴展。

(4)對於那些不但願使用繼承或由於多層繼承致使系統類的個數急劇增長的系統,橋接模式尤其適用。

3、組合模式(樹形結構的處理)

pass

4、裝飾模式(擴展系統功能)

裝飾模式能夠在不改變一個對象自己功能的基礎上給對象增長額外的新行爲,在現實生活中,這種狀況也處處存在,例如一張照片,咱們能夠不改變照片自己,給它增長一個相框,使得它具備防潮的功能,並且用戶能夠根據須要給它增長不一樣類型的相框,甚至能夠在一個小相框的外面再套一個大相框。

裝飾模式是一種用於替代繼承的技術,它經過一種無須定義子類的方式來給對象動態增長職責,使用對象之間的關聯關係取代類之間的繼承關係。在裝飾模式中引入了裝飾類,在裝飾類中既能夠調用待裝飾的原有類的方法,還能夠增長新的方法,以擴充原有類的功能。

裝飾模式定義以下:

裝飾模式(Decorator Pattern):動態地給一個對象增長一些額外的職責,就增長對象功能來講,裝飾模式比 生成子類實現更爲靈活。裝飾模式是一種對象結構型模式。

在裝飾模式中,爲了讓系統具備更好的靈活性和可擴展性,咱們一般會定義一個抽象裝飾類,而將具體的裝飾類做爲它的子類,裝飾模式結構如圖所示:

public class EncodingFilter implements Filter{
 
     
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
         
        //被加強的對象
        HttpServletRequest req = (HttpServletRequest) request;
        //加強對象
        EnhanceRequest enhanceRequest = new EnhanceRequest(req);
         
         
        chain.doFilter(enhanceRequest, response);
         
    }
 
 
}
 
class EnhanceRequest extends HttpServletRequestWrapper{     // 1>與要加強的類(HttpServletRequest類)繼承/實現同一個類/接口
     
    private HttpServletRequest request;
 
    public EnhanceRequest(HttpServletRequest request) {     // 2>傳入要加強的類
        super(request);
        this.request = request;
    }
     
    //3>對要加強的方法(getParaameter)重寫
    @Override
    public String getParameter(String name) {
        String parameter = request.getParameter(name);//亂碼
        try {
            parameter = new String(parameter.getBytes("iso8859-1"),"UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return parameter;
    }
     
}

注意事項

(1) 儘可能保持裝飾類的接口與被裝飾類的接口相同,這樣,對於客戶端而言,不管是裝飾以前的對象仍是裝飾以後 的對象均可以一致對待。這也就是說,在可能的狀況下,咱們應該儘可能使用透明裝飾模式。

(2) 儘可能保持具體構件類 ConcreteComponent 是一個「輕」類,也就是說不要把太多的行爲放在具體構件類中,咱們能夠經過裝飾類對其進行擴展。

(3) 若是隻有一個具體構件類,那麼抽象裝飾類能夠做爲該具體構件類的直接子類。

優勢

(1) 對於擴展一個對象的功能,裝飾模式比繼承更加靈活性,不會致使類的個數急劇增長。(避免了繼承->繼承->繼承)

(2) 能夠經過一種動態的方式來擴展一個對象的功能,經過配置文件能夠在運行時選擇不一樣的具體裝飾類,從而實現不一樣的行爲。

(3) 能夠對一個對象進行屢次裝飾,經過使用不一樣的具體裝飾類以及這些裝飾類的排列組合,能夠創造出不少不一樣行爲的組合,獲得功能更爲強大的對象。

(4) 具體構件類與具體裝飾類能夠獨立變化,用戶能夠根據須要增長新的具體構件類和具體裝飾類,原有類庫代碼無須改變,符合「開閉原則」。

缺點

(1) 使用裝飾模式進行系統設計時將產生不少小對象,這些對象的區別在於它們之間相互鏈接的方式有所不一樣,而不是它們的類或者屬性值有所不一樣,大量小對象的產生勢必會佔用更多的系統資源,在必定程序上影響程序的性能。

(2) 裝飾模式提供了一種比繼承更加靈活機動的解決方案,但同時也意味着比繼承更加易於出錯,排錯也很困難,對於屢次裝飾的對象,調試時尋找錯誤可能須要逐級排查,較爲繁瑣。

適用場景

(1) 在不影響其餘對象的狀況下,以動態、透明的方式給單個對象添加職責。

(2) 當不能採用繼承的方式對系統進行擴展或者採用繼承不利於系統擴展和維護時可使用裝飾模式。不能採用繼 承的狀況主要有兩類:第一類是系統中存在大量獨立的擴展,爲支持每一種擴展或者擴展之間的組合將產生大量 的子類,使得子類數目呈爆炸性增加;第二類是由於類已定義爲不能被繼承(如 Java 語言中的 final 類)。

5、外觀模式(其實web調用service層使用的就是外觀模式)

在軟件開發中,有時候爲了完成一項較爲複雜的功能,一個客戶類須要和多個業務類交互,而這些須要交互的業務類常常會做爲一個總體出現,因爲涉及到的類比較多,致使使用時代碼較爲複雜,此時,特別須要一個相似服務員同樣的角色,由它來負責和多個業務類進行交互,而客戶類只需與該類交互。外觀模式經過引入一個新的外 觀類(Facade)來實現該功能,外觀類充當了軟件系統中的「服務員」,它爲多個業務類的調用提供了一個統一 的入口,簡化了類與類之間的交互。在外觀模式中,那些須要交互的業務類被稱爲子系統(Subsystem)。若是 沒有外觀類,那麼每一個客戶類須要和多個子系統之間進行復雜的交互,系統的耦合度將很大,如圖 2(A) 所示;而 引入外觀類以後,客戶類只須要直接與外觀類交互,客戶類與子系統之間原有的複雜引用關係由外觀類來實 現,從而下降了系統的耦合度。

外觀模式中,一個子系統的外部與其內部的通訊經過一個統一的外觀類進行,外觀類將客戶類與子系統的內部複雜性分隔開,使得客戶類只須要與外觀角色打交道,而不須要與子系統內部的不少對象打交道。

外觀模式定義以下:

外觀模式(Facade Pattern):爲子系統中的一組接口提供一個統一的入口。外觀模式定義了一個高層接口,這個接口使得這一子系統更加容易使用。

外觀模式又稱爲門面模式,它是一種對象結構型模式。外觀模式是迪米特法則的一種具體實現,經過引入一個新的外觀角色能夠下降原有系統的複雜度,同時下降客戶類與子系統的耦合度

優勢

 

(1) 它對客戶端屏蔽了子系統組件,減小了客戶端所需處理的對象數目,並使得子系統使用起來更加容易。經過引 入外觀模式,客戶端代碼將變得很簡單,與之關聯的對象也不多。

(2) 它實現了子系統與客戶端之間的鬆耦合關係,這使得子系統的變化不會影響到調用它的客戶端,只須要調整外 觀類便可。

(3) 一個子系統的修改對其餘子系統沒有任何影響,並且子系統內部變化也不會影響到外觀對象。

缺點

(1) 不能很好地限制客戶端直接使用子系統類,若是對客戶端訪問子系統類作太多的限制則減小了可變性和靈活 性。

(2) 若是設計不當,增長新的子系統可能須要修改外觀類的源代碼,違背了開閉原則。

適用場景

(1) 當要爲訪問一系列複雜的子系統提供一個簡單入口時可使用外觀模式。

(2) 客戶端程序與多個子系統之間存在很大的依賴性。引入外觀類能夠將子系統與客戶端解耦,從而提升子系統的獨立性和可移植性。

(3) 在層次化結構中,可使用外觀模式定義系統中每一層的入口,層與層之間不直接產生聯繫,而經過外觀類創建聯繫,下降層之間的耦合度。

6、享元模式(Spring)

當一個軟件系統在運行時產生的對象數量太多,將致使運行代價太高,帶來系統性能降低等問題。例如在一個文 本字符串中存在不少重複的字符,若是每個字符都用一個單獨的對象來表示,將會佔用較多的內存空間,那麼 咱們如何去避免系統中出現大量相同或類似的對象,同時又不影響客戶端程序經過面向對象的方式對這些對象進 行操做?享元模式正爲解決這一類問題而誕生。享元模式經過共享技術實現相同或類似對象的重用,在邏輯上每 一個出現的字符都有一個對象與之對應,然而在物理上它們卻共享同一個享元對象,這個對象能夠出如今一個字 符串的不一樣地方,相同的字符對象都指向同一個實例,在享元模式中,存儲這些共享實例對象的地方稱爲享元池(Flyweight Pool)。咱們能夠針對每個不一樣的字符建立一個享元對象,將其放在享元池中,須要時再從享 元池取出。

享元模式(Flyweight Pattern):用共享技術有效地支持大量細粒度對象的複用。系統只使用少許的對象,而這些對象都很類似,狀態變化很小,能夠實現對象的屢次複用。因爲享元模式要求可以共享的對象必須是細粒度對象,所以它又稱爲輕量級模式,它是一種對象結構型模式。

享元模式結構較爲複雜,通常結合工廠模式一塊兒使用,在它的結構圖中包含了一個享元工廠類,其結構圖如圖所示:

  • Flyweight(抽象享元類):一般是一個接口或抽象類,在抽象享元類中聲明瞭具體享元類公共的方法,這些 方法能夠向外界提供享元對象的內部數據(內部狀態),同時也能夠經過這些方法來設置外部數據(外部狀 態)。
  • ConcreteFlyweight(具體享元類):它實現了抽象享元類,其實例稱爲享元對象;在具體享元類中爲內部 狀態提供了存儲空間。一般咱們能夠結合單例模式來設計具體享元類,爲每個具體享元類提供惟一的享元 對象。
  • UnsharedConcreteFlyweight(非共享具體享元類):並非全部的抽象享元類的子類都須要被共享,不 能被共享的子類可設計爲非共享具體享元類;當須要一個非共享具體享元類的對象時能夠直接經過實例化創 建。
  • FlyweightFactory(享元工廠類):享元工廠類用於建立並管理享元對象,它針對抽象享元類編程,將各類 類型的具體享元對象存儲在一個享元池中,享元池通常設計爲一個存儲「鍵值對」的集合(也能夠是其餘類 型的集合),能夠結合工廠模式進行設計;當用戶請求一個具體享元對象時,享元工廠提供一個存儲在享元 池中已建立的實例或者建立一個新的實例(若是不存在的話),返回新建立的實例並將其存儲在享元池中。

在享元模式中引入了享元工廠類,享元工廠類的做用在於提供一個用於存儲享元對象的享元池,當用戶須要對象時,首先從享元池中獲取,若是享元池中不存在,則建立一個新的享元對象返回給用戶,並在享元池中保存該新增對象。典型的享元工廠類的代碼以下:

class FlyweightFactory { //定義一個HashMap用於存儲享元對象,實現享元池
        private HashMap flyweights = new HashMap();

        public Flyweight getFlyweight(String key) { //若是對象存在,則直接從享元池獲取
            if (flyweights.containsKey(key)) {
                return (Flyweight) flyweights.get(key);
            }
            //若是對象不存在,先建立一個新的對象添加到享元池中,而後返回 
            else {
            Flyweight fw = newConcreteFlyweight();
            flyweights.put(key, fw);
            return fw;
            }
        }
    }

享元類的設計是享元模式的要點之一,在享元類中要將內部狀態和外部狀態分開處理,一般將內部狀態做爲享元類的成員變量,而外部狀態經過注入的方式添加到享元類中。典型的享元類代碼以下所示:

class Flyweight { //內部狀態intrinsicState做爲成員變量,同一個享元對象其內部狀態是一致的
    private String intrinsicState;

    public Flyweight(String intrinsicState) {
        this.intrinsicState = intrinsicState;
    }

    //外部狀態extrinsicState在使用時由外部設置,不保存在享元對象中,即便是同一個對象,在每一次調用時也能夠傳入不一樣的外部
    public void operation(String extrinsicState) {
        // ......
    }
}

demo:

//圍棋棋子類:抽象享元類
abstract class IgoChessman {
    public abstract String getColor();

    public void display() {
        System.out.println("棋子顏色:" + this.getColor());
    }
}

//黑色棋子類:具體享元類
class BlackIgoChessman extends IgoChessman {
    public String getColor() {
        return "黑色";
    }
}

//白色棋子類:具體享元類
class WhiteIgoChessman extends IgoChessman {
    public String getColor() {
        return "白色";
    }
}

//圍棋棋子工廠類:享元工廠類,使用單例模式進行設計
class IgoChessmanFactory {
    private static IgoChessmanFactory instance = new IgoChessmanFactory();
    private static Hashtable ht; //使用Hashtable來存儲享元對象,充當享元池

    private IgoChessmanFactory() {
        ht = new Hashtable();
        IgoChessman black, white;
        black = new BlackIgoChessman();
        ht.put("b", black);
        white = new WhiteIgoChessman();
        ht.put("w", white);
    }

    //返回享元工廠類的惟一實例
    public static IgoChessmanFactory getInstance() {
        return instance;
    }

    //經過key來獲取存儲在Hashtable中的享元對象
    public static IgoChessman getIgoChessman(String color) {
        return (IgoChessman) ht.get(color);
    }
}


class Client {
    public static void main(String args[]) {
        IgoChessman black1, black2, black3, white1, white2;
        IgoChessmanFactory factory;
        //獲取享元工廠對象
        factory = IgoChessmanFactory.getInstance();
        //經過享元工廠獲取三顆黑子
        black1 = factory.getIgoChessman("b");
        black2 = factory.getIgoChessman("b");
        black3 = factory.getIgoChessman("b");
        System.out.println("判斷兩顆黑子是否相同:" + (black1 == black2));
        //經過享元工廠獲取兩顆白子
        white1 = factory.getIgoChessman("w");
        white2 = factory.getIgoChessman("w");
        System.out.println("判斷兩顆白子是否相同:" + (white1 == white2));
        //顯示棋子 
        black1.display();
        black2.display();
        black3.display();
        white1.display();
        white2.display();
    }
}

與其餘模式的聯用

(1)在享元模式的享元工廠類中一般提供一個靜態的工廠方法用於返回享元對象,使用簡單工廠模式來生成享元對象。

(2)在一個系統中,一般只有惟一一個享元工廠,所以可使用單例模式進行享元工廠類的設計。

(3)享元模式能夠結合組合模式造成複合享元模式(爲多個內部狀態不一樣的 享元對象設置相同的外部狀態),統一對多個享元對象設置外部狀態。

優勢

(1) 能夠極大減小內存中對象的數量,使得相同或類似對象在內存中只保存一份,從而能夠節約系統資源,提升系統性能。

(2) 享元模式的外部狀態相對獨立,並且不會影響其內部狀態,從而使得享元對象能夠在不一樣的環境中被共享。

缺點

(1) 享元模式使得系統變得複雜,須要分離出內部狀態和外部狀態,這使得程序的邏輯複雜化。

(2) 爲了使對象能夠共享,享元模式須要將享元對象的部分狀態外部化,而讀取外部狀態將使得運行時間變長。

適用場景

(1) 一個系統有大量相同或者類似的對象,形成內存的大量耗費。

(2) 對象的大部分狀態均可之外部化,能夠將這些外部狀態傳入對象中。

(3) 在使用享元模式時須要維護一個存儲享元對象的享元池,而這須要耗費必定的系統資源,所以,應當在須要屢次重複使用享元對象時才值得使用享元模式。

7、代理模式

http://www.runoob.com/design-pattern/proxy-pattern.html

相關文章
相關標籤/搜索