研磨設計模式 (陳臣 / 王斌 著)

第1章 設計模式基礎 (已看)
第2章 簡單工廠 (已看)
第3章 外觀模式(Facade) (已看)
第4章 適配器模式(Adapter)  (已看)
第5章 單例模式(Singleton) (已看)
第6章 工廠方法模式(Factory Method) (已看)
第7章 抽象工廠模式(Abstract Factory) (已看)
第8章 生成器模式(Builder) (已看)
第9章 原型模式(Prototype) (已看)
第10章 中介者模式(Mediator) (已看)
第11章 代理模式(Proxy)  (已看)
第12章 觀察者模式(Observer) (已看)
第13章 命令模式(Command) (已看)
第14章 迭代器模式(Iterator) (已看)
第15章 組合模式(Composite) (已看)
第16章 模板方法模式(Template Method) (已看)
第17章 策略模式(Strategy) (已看)
第18章 狀態模式(State) (已看)
第19章 備忘錄模式(Memento) (已看)
第20章 享元模式(Flyweight) (已看)
第21章 解釋器模式(Interpreter) (已看)
第22章 裝飾模式(Decorator) (已看)
第23章 職責鏈模式(Chain of Responsibility) (已看)
第24章 橋接模式(Bridge) (已看)
第25章 訪問者模式(Visitor) (已看)
附錄A 常見面向對象設計原則 (已看)
算法

 

第1章 設計模式基礎sql

  1.1 設計模式是什麼數據庫

    1.1.1 什麼是模式編程

    1.1.2 設計模式的概念設計模式

    1.1.3 設計模式的理解api

    1.1.4 設計模式的歷史緩存

  1.2 設計模式有什麼安全

    1.2.1 設計模式的組成session

    1.2.2 設計模式的分類數據結構

  1.3 設計模式的學習

    1.3.1 爲何要學習設計模式

    1.3.2 學習設計模式的層次

    1.3.3 如何學習設計模式

  1.4 本書的組織方式

    1.4.1 本書所講述的設計模式的提綱

    1.4.2 每一個模式的講述結構

Q:什麼是設計模式

A:是指在軟件開發中,通過驗證的,用於解決在特定環境下,重複出現的,特定問題的解決方案

第2章 簡單工廠

  2.1 場景問題

    2.1.1 接口回顧

Q:接口用來幹什麼

A:經過使用接口,能夠實現不相關類的相同行爲,而不需考慮這些類之間的層次關係,接口就是實現類對外的外觀

Q:何時選用接口?何時選用抽象類?

A:  1.優先使用接口

      2.在既要定義子類的行爲,又要爲子類提供公共的功能時應選擇抽象類.

    2.1.2 面向接口編程

Q:什麼是組件

A:從設計上講,組件就是能完成必定功能的封裝體.小到一個類,大到一個系統,均可以稱爲組件

    2.1.3 不用模式的解決方案

using System;
using System.Collections.Generic;

namespace ConsoleApplication1 {
    public class Program {
        public static void Main(string[] args) {
            Api api = new Implement();
            api.test1("哈哈,沒關係張,只是個測試而已!");
        }
    }


    public interface Api {
        void test1(string s);
    }

    public class Implement : Api {
        public void test1(string s) {
            Console.WriteLine("Now In Inmplement.The input s==" + s);
        }
    }
    
}
View Code

    2.1.4 有何問題

Q:有何問題

A:客戶端不但知道了接口,同時還知道了具體的實現就是Implement.接口的思想是"封裝隔離",而實現類Implement應該是被接口Api封裝並同客戶端隔離開的.

   也就是說,客戶端根本就不該該知道具體的實現類是Implement

  2.2 解決方案

    2.2.1 使用簡單工廠來解決問題

Q:什麼是簡單工廠

A:提供一個建立對象實例的功能,而無須關心其具體實現.被建立實例的類型能夠是接口,抽象類,也能夠是具體的類.

    2.2.2 簡單工廠的結構和說明

  [API]:定義客戶所須要的功能接口

  [Impl]:具體實現Api的實現類,可能會有多個

  [Factory]:工廠,選擇合適的實現類來建立Api接口對象

  [Client]:客戶端,經過Factory來獲取Api接口對象,而後面向Api接口編程

    2.2.3 簡單工廠示例代碼

using System;

namespace Test2 {
    class Program {
        static void Main(string[] args) {
            Api api = Factory.createApi(1);
            api.operation("正在使用簡單工廠");
        }
    }

    public interface Api {
        void operation(string s);
    }

    public class ImplementA : Api {
        public void operation(string s) {
            Console.WriteLine("ImplementA s == " + s);
        }
    }

    public class ImplementB : Api {
        public void operation(string s) {
            Console.WriteLine("ImplementB s == " + s);
        }
    }

    public class Factory {
        public static Api createApi(int condition) {
            Api api = null;
            
            if (condition == 1) {
                api = new ImplementA();
            } else if (condition == 2) {
                api = new ImplementB();
            }

            return api;
        }
    }
}
View Code

    2.2.4 使用簡單工廠重寫示例

using System;

namespace Test2 {
    class Program {
        static void Main(string[] args) {
            Api api = Factory.createApi();
            api.test1("哈哈,沒關係張,只是個測試而已!");
        }
    }

    public interface Api {
        void test1(string s);
    }

    public class Implement : Api {
        public void test1(string s) {
            Console.WriteLine(s);
        }
    }

    public class Factory {
        public static Api createApi() {
            return new Implement();
        }
    }
}
View Code

  2.3 模式講解

    2.3.1 典型疑問

Q:把"new Implement()"這句話放到客戶端和放到簡單工廠裏面有什麼不一樣?

A:理解這個問題的重點就在於理解簡單工廠所處的位置

    2.3.2 認識簡單工廠

Q:簡單工廠建立對象的範圍

A:雖然從理論上講,簡單工廠什麼都能建立,但對於簡單工廠可建立對象的範圍,一般不要太大.建議控制在一個獨立的組件級別或者一個模塊級別,也就是一個組        件或模塊簡單工廠.

    不然這個簡單工廠類會職責不明,有點大雜燴的感受

 

簡單工廠的調用順序圖

       2.3.3 簡單工廠中方法的寫法

    2.3.4 可配置的簡單工廠

    2.3.5 簡單工廠的優缺點

優勢

  1.幫助封裝

    簡單工廠雖然很簡單,可是很是友好地幫助咱們實現了組件的封裝,而後讓組件外部能真正面向接口編程

  2.解耦

    經過簡單工廠,實現了客戶端和具體實現類的解耦

    若是上面的例子,客戶端根本就不知道具體是由誰來實現,也不知道具體是如何實現的,客戶端只是經過工廠獲取它須要的接口對象.

缺點

  1.可能增長客戶端的複雜度

    若是經過客戶端的參數來選擇具體的實現類,那麼就必須讓客戶端能理解各個參數所表明的具體功能和含義,這樣會增長客戶端使用的難度,

    也部分暴露了內部實現,這種狀況能夠選用可配置的方式來實現

  2.不方便擴展子工廠

    私有化簡單工廠的構造方法,使用靜態方法來建立接口,也就不能經過寫簡單工廠類的子類來改變建立接口的方法的行爲了.不過一般狀況下是不須要爲簡單工廠建立子類的

    2.3.6 思考簡單工廠

Q:簡單工廠的本質

A:選擇實現

   注意簡單工廠的重點在選擇,實現是已經作好了的.就算實現再簡單,也要由具體的實現類來實現,而不是在簡單工廠裏面來實現.簡單工廠的目的在於爲客戶端       來選擇相應的實現,

   從而是的客戶端和實現之間解耦.這樣一來,具體實現發生了變化,就不用變更客戶端了,這個變化會被簡單工廠吸取和屏蔽掉

Q:什麼時候選用簡單工廠

A:  1.若是想要徹底封裝隔離具體實現,讓外部只能經過接口來操做封裝體,那麼能夠選用簡單工廠,讓客戶端經過工廠來獲取相應的接口,而無需關心具體的實                現

   2.若是想要把對外建立對象的職責集中管理和控制,能夠選用簡單工廠,一個簡單工廠能夠建立不少的,不相關的對象,能夠把對外建立的職責集中到一個簡               單工廠來,從而實現集中管理和控制

    2.3.7 相關模式

1.簡單工廠和抽象工廠模式

  簡單工廠是用來選擇實現的,能夠選擇任意接口的實現.一個簡單工廠能夠有多個用於選擇並建立對象的方法,多個方法建立的對象能夠有關係也能夠沒有關係

  抽象工廠模式是用來選擇產品簇的實現的,也就是說通常抽象工廠裏面有多個用於選擇並建立對象的方法,可是這些方法所建立的對象之間一般是有關係的

  這些被建立的對象一般是構成一個產品簇所須要的部件對象

  因此從某種意義上來講,簡單工廠和抽象工廠是相似的,若是抽象工廠退化成爲只有一個實現,不分層次,那麼就至關於簡單工廠了.

2.簡單工廠和工廠方法模式

  簡單工廠和工廠方法模式也是很是相似的

  工廠方法的本質也是用來選擇實現的,跟簡單工廠的區別在於工廠方法是把選擇具體實現的功能延遲到子類去實現.

  若是把工廠方法中選擇的實現放到父類直接實現,那就等同於簡單工廠

3.簡單工廠和能建立對象實例的模式

  簡單工廠的本質是選擇實現,因此它能夠跟其餘任何可以具體的建立對象實例的模式配合使用,好比:單例模式,原型模式,生成器模式等

第3章 外觀模式(Facade)

  3.1 場景問題

    3.1.1 生活中的示例

外觀模式在現實生活中的示例不少,好比組裝電腦,一般會有兩種方案

方案1:

方案2:

這個專業的裝機公司就至關於本章的主角-外觀模式(Facade).有了它,咱們就不用本身去和衆多賣配件的公司打交道,只須要跟裝機公司交互就能夠了,並將組裝好的電腦返回給咱們.

把上面的過程抽象一下,若是把電子市場當作是一個系統,而各個賣配件的公司當作是模塊的話,就相似於出現了這樣一種狀況:客戶端爲了完成某個功能,須要去調用某個系統中的多個模塊

把它們稱爲A模塊,B模塊和C模塊.對於客戶端而言,那就須要知道A,B,C這三個模塊的功能,還須要知道如何組合這多個模塊提供的功能來實現本身所須要的功能,很是麻煩.

    3.1.2 代碼生成的應用

    3.1.3 不用模式的解決方案

using System;

namespace Test2 {
    class Program {
        static void Main(string[] args) {
            new Presentation().generate();
            new Business().generate();
            new DAO().generate();
        }
    }

    public class ConfigModel {
        private bool needGenPresentation = true;
        private bool needGenBusiness = true;
        private bool needGenDAO = true;

        public bool isNeedGenPresentation() {
            return needGenPresentation;
        }

        public void setNeedGenPresentation(bool needGenPresentation) {
            this.needGenPresentation = needGenPresentation;
        }

        public bool isNeedGenBusiness() {
            return needGenBusiness;
        }

        public void setNeedGenBusinees(bool needGenBusiness) {
            this.needGenBusiness = needGenBusiness;
        }

        public bool isNeedGenDAO() {
            return needGenDAO;
        }

        public void setNeedGenDAO(bool needGenDAO) {
            this.needGenDAO = needGenDAO;
        }
    }

    public class ConfigManager {
        private static ConfigManager manager = null;
        private static ConfigModel cm = null;

        private ConfigManager() {
            
        }

        public static ConfigManager getInstance() {
            if (manager == null) {
                manager = new ConfigManager();
                cm = new ConfigModel();
            }

            return manager;
        }

        public ConfigModel getConfigData() {
            return cm;
        }
    }

    public class Presentation {
        public void generate() {
            ConfigModel cm = ConfigManager.getInstance().getConfigData();

            if (cm.isNeedGenPresentation()) {
                Console.WriteLine("正在生成表現層他代碼文件");
            }
        }
    }

    public class Business {
        public void generate() {
            ConfigModel cm = ConfigManager.getInstance().getConfigData();
            if (cm.isNeedGenBusiness()) {
                Console.WriteLine("正在生成邏輯層代碼文件");
            }
        }
    }

    public class DAO {
        public void generate() {
            ConfigModel cm = ConfigManager.getInstance().getConfigData();
            if (cm.isNeedGenDAO()) {
                Console.WriteLine("正在生成數據層代碼文件");
            }
        }
    }

}
View Code

    3.1.4 有何問題

Q:有何問題

A:客戶端爲了使用生成代碼的功能,須要與生成代碼子系統內部的多個模塊交互

   這對於客戶端而言,是個麻煩,使得客戶端不能簡單地使用生成代碼的功能.並且,若是其中的某個模塊發生了變化,還可能會引發客戶端也要隨着變化

  3.2 解決方案

    3.2.1 使用外觀模式來解決問題

Q:什麼是外觀模式

A:爲子系統中的一組接口提供一個一致的界面,Facade模式定義了一個高層接口,這個接口使得這一子系統更加容易使用

Q:什麼是界面

A:這裏提到的界面,主要指的是從一個組件外部來看這個組件,可以看到什麼,這就是這個組件的界面,也就是所說的外觀

   好比,你從一個類外部來看這個類,那麼這個類的public方法就是這個類的外觀,由於你從類外部來看這個類,就能看到這些.

   再好比,你從一個模塊外部來看這個模塊,那麼這個模塊對外的接口就是這個模塊的外觀,由於你只能看到這些接口,其餘的模塊內部實現的部分是被接口封裝隔離了的.

Q:什麼是接口

A:這裏提到的接口,主要指的是外部和內部交互的這麼一個通道,一般是指一些方法,能夠是類的方法,也能夠是interface的方法,也就是說,這裏所說的接口,並不等價於interface,也有多是一個類

    3.2.2 外觀模式的結構和說明

  [Facade] 定義子系統的多個模塊對外的高層接口,一般須要調用內部多個模塊,從而把客戶的請求代理給適當的子系統對象

  [模塊] 接受Facade對象的委派,真正實現功能,各個模塊之間可能有交互

  可是請注意,Facade對象知道各個模塊,可是各個模塊不該該知道Facade對象

    3.2.3 外觀模式示例代碼

using System;

namespace Test2 {
    class Program {
        static void Main(string[] args) {
            new Facade().test();
        }
    }

    public interface AModuleApi {
        void testA();
    }

    public class AModuleImplement : AModuleApi {
        public void testA() {
            Console.WriteLine("如今在A模塊裏面操做testA方法");
        }
    }

    public interface BModuleApi {
        void testB();
    }

    public class BModuleImplement : BModuleApi {
        public void testB() {
            Console.WriteLine("如今在B模塊裏面操做testB方法");
        }
    }

    public interface CModuleApi {
        void testC();
    }

    public class CModuleImplement : CModuleApi {
        public void testC() {
            Console.WriteLine("如今在C模塊裏面操做testC方法");
        }
    }

    public class Facade {
        public void test() {
            AModuleApi a = new AModuleImplement();
            a.testA();
            BModuleApi b = new BModuleImplement();
            b.testB();
            CModuleApi c = new CModuleImplement();
            c.testC();
        }
    }
    
}
View Code

    3.2.4 使用外觀模式重寫示例

using System;

namespace Test2 {
    class Program {
        static void Main(string[] args) {
            new Facade().generate();
        }
    }

    public class ConfigModel {
        private bool needGenPresentation = true;
        private bool needGenBusiness = true;
        private bool needGenDAO = true;

        public bool isNeedGenPresentation() {
            return needGenPresentation;
        }

        public void setNeedGenPresentation(bool needGenPresentation) {
            this.needGenPresentation = needGenPresentation;
        }

        public bool isNeedGenBusiness() {
            return needGenBusiness;
        }

        public void setNeedGenBusinees(bool needGenBusiness) {
            this.needGenBusiness = needGenBusiness;
        }

        public bool isNeedGenDAO() {
            return needGenDAO;
        }

        public void setNeedGenDAO(bool needGenDAO) {
            this.needGenDAO = needGenDAO;
        }
    }

    public class ConfigManager {
        private static ConfigManager manager = null;
        private static ConfigModel cm = null;

        private ConfigManager() {
            
        }

        public static ConfigManager getInstance() {
            if (manager == null) {
                manager = new ConfigManager();
                cm = new ConfigModel();
            }

            return manager;
        }

        public ConfigModel getConfigData() {
            return cm;
        }
    }

    public class Presentation {
        public void generate() {
            ConfigModel cm = ConfigManager.getInstance().getConfigData();

            if (cm.isNeedGenPresentation()) {
                Console.WriteLine("正在生成表現層他代碼文件");
            }
        }
    }

    public class Business {
        public void generate() {
            ConfigModel cm = ConfigManager.getInstance().getConfigData();
            if (cm.isNeedGenBusiness()) {
                Console.WriteLine("正在生成邏輯層代碼文件");
            }
        }
    }

    public class DAO {
        public void generate() {
            ConfigModel cm = ConfigManager.getInstance().getConfigData();
            if (cm.isNeedGenDAO()) {
                Console.WriteLine("正在生成數據層代碼文件");
            }
        }
    }

    public class Facade {
        public void generate() {
            new Presentation().generate();
            new Business().generate();
            new DAO().generate();
        }
    }

}
View Code

  3.3 模式講解

    3.3.1 認識外觀模式

Q:外觀模式的目的

A:外觀模式的目的不是給子系統添加新的功能接口,而是爲了讓外部減小與子系統內多個模塊的交互,鬆散耦合,從而讓外部可以更簡單地使用子系統

Q:使用外觀和不使用外觀相比有何變化

A:Facade位於由A,B,C模塊組成的系統而不是客戶端,至關於屏蔽了外部客戶端和系統內部模塊的交互

 

外觀模式的調用順序圖

    3.3.2 外觀模式的實現

Q:Facade的方法實現

A:Facade的方法實現中,通常是負責把客戶端的請求轉發給子系統內部的各個模塊進行處理,Facade的方法自己並不進行功能的處理,Facade的方法實現只是實現一個功能的組合調用

   固然在Facade中實現一個邏輯處理也並非不能夠的,可是不建議這樣作,由於這不是Facade的本意,也超出了Facade的邊界

    3.3.3 外觀模式的優缺點

優勢

  1.鬆散耦合

    外觀模式鬆散了客戶端與子系統的耦合關係,讓子系統內部的模塊能更容易擴展和維護

  2.簡單易用

    外觀模式讓子系統更加易用,客戶端再也不須要了解子系統內部的實現,也不須要跟衆多子系統內部的模塊進行交互,只須要跟外觀交互就能夠了
    至關於外觀類爲外部客戶端使用子系統提供了一站式服務

  3.更好地劃分訪問的層次

    經過合理使用Facade,能夠幫助咱們更好地劃分訪問的層次.有些方法是對系統外的,有些方法是系統內部使用的.把須要暴露給外部的功能集中到外觀中
    這樣既方便客戶端使用,也很好地隱藏了內部的細節

缺點

  1.過多的或者是不合理的Facade也容易讓人迷惑.究竟是調用Facade好呢,仍是直接調用模塊好

    3.3.4 思考外觀模式

Q:外觀模式的本質

A:封裝交互,簡化調用

   Facade封裝了子系統外部和子系統內部多個模塊的交互過程,從而簡化了外部的調用.經過外觀,子系統爲外部提供一些高層的接口,以方便它們的使用

Q:對設計原則的體現

A:外觀模式不少地體現了"最少知識原則"

Q:什麼時候選用外觀模式

A:  1.若是你但願爲一個複雜的子系統提供一個簡單接口的時候,能夠考慮使用外觀模式.使用外觀對象來實現大部分客戶須要的功能,從而簡化客戶的使用

      2.若是想要讓客戶程序和抽象類的實現部分鬆散耦合,能夠考慮使用外觀模式

      3.若是構建多層結構的系統,能夠考慮使用外觀模式,使用外觀對象做爲每層的入口,這樣能夠簡化層間調用,也能夠鬆散層次之間的依賴關係

    3.3.5 相關模式

1.外觀模式和中介者模式

  這兩個模式很是相似,可是卻有本質的區別

  中介者模式主要用來封裝多個對象之間相互的交互,多用在系統內部的多個模塊之間;而外觀模式封裝的是單向的交互,是從客戶端訪問系統的調用,沒有從系統中來訪問客戶端的調用.

  在中介者模式的實現裏面,是須要實現具體的交互功能的;而外觀模式的實現裏面,通常是組合調用或是轉調內部實現的功能,一般外觀模式自己並非實現這些功能

  中介者模式的目的主要是鬆散多個模塊之間的耦合,把這些耦合關係所有放到中介者中去實現;而外觀模式的目的是簡化客戶端的調用,這點和中介者模式也不一樣

2.外觀模式和單例模式

  一般一個子系統只須要一個外觀實例,因此外觀模式能夠和單例模式組合使用,把Facade類實現稱爲單例.

3.外觀模式和抽象工廠模式

  外觀模式的外觀類一般須要和系統內部的多個模塊交互,每一個模塊通常都有本身的接口,因此在外觀類的具體實現裏面,須要獲取這些接口,而後組合這些接口來完成客戶端的功能

  那麼怎麼獲取這些接口呢?就能夠和抽象工廠一塊兒使用,外觀類經過抽象工廠來獲取所須要的接口,而抽象工廠也能夠把模塊內部的實現對Facade進行屏蔽,也就是說Facade也僅

  僅只是知道它從模塊中獲取它須要的功能,模塊內部的細節,Facade也不知道

第4章 適配器模式(Adapter)

  4.1 場景問題

    4.1.1 裝配電腦的例子

    4.1.2 同時支持數據庫和文件的日誌管理

using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;

namespace Test2 {
    class Program {
        static void Main(string[] args) {
            LogModel lml = new LogModel();
            lml.setLogId("001");
            lml.setOperateUser("admin");
            lml.setOperateTime("2010-03-02 10:08:18");
            lml.setLogContent("這是一個測試");
            
            List<LogModel> list = new List<LogModel>();
            list.Add(lml);
            LogFileOperateApi api = new LogFileOperate("");
            api.writeLogFile(list);

            string readLog = api.readLogFile();
            Console.WriteLine(readLog);
        }
    }

    public class LogModel {
        private string logId;
        private string operateUser;
        private string operateTime;
        private string logContent;

        public string getLogId() {
            return logId;
        }

        public void setLogId(string logId) {
            this.logId = logId;
        }

        public string getOperateUser() {
            return operateUser;
        }

        public void setOperateUser(string operateUser) {
            this.operateUser = operateUser;
        }

        public string getOperateTime() {
            return operateTime;
        }

        public void setOperateTime(string operateTime) {
            this.operateTime = operateTime;
        }

        public string getLogContent() {
            return logContent;
        }

        public void setLogContent(string logContent) {
            this.logContent = logContent;
        }

        public override string ToString() {
            return "logId=" + logId + ", operateUser=" + operateUser + ", operateTime=" + operateTime + ",logContent=" +
                   logContent;
        }
    }

    public interface LogFileOperateApi {
        string readLogFile();
        void writeLogFile(List<LogModel> list);
    }

    public class LogFileOperate : LogFileOperateApi {
        private string logFilePathName = "AdapterLog.log";

        public LogFileOperate(string logFilePathName) {
            if (logFilePathName != null && logFilePathName.Trim().Length > 0) {
                this.logFilePathName = logFilePathName;
            }
        }

        public string readLogFile() {
            List<LogModel> list = null;
            
            StreamReader sr = new StreamReader(logFilePathName,Encoding.Default);

            string line = sr.ReadLine();

            return line;
        }

        public void writeLogFile(List<LogModel> list) {
            FileStream fs = new FileStream(logFilePathName,FileMode.Create);
            StreamWriter sw = new StreamWriter(fs);

            foreach (LogModel logModel in list) {
                sw.Write(logModel.ToString());
            }
            sw.Flush();
            sw.Close();
            fs.Close();
        }
    }

    public interface LogDbOperateApi {
        void createLog(LogModel lm);
        void updateLog(LogModel lm);
        void removeLog(LogModel lm);
        List<LogModel> getAllLog();
    }
}
View Code

    4.1.3 有何問題

  4.2 解決方案

    4.2.1 使適配器模式來解決問題

    4.2.2 適配器模式的結構和說明

  [Client] 客戶端,調用本身須要的領域接口Target

  [Target] 定義客戶端須要的跟特定領域相關的接口

  [Adaptee] 已經存在的接口,一般能知足客戶端的相關功能,可是接口與客戶端要求的特定領域接口不一致,須要被適配

  [Adapter] 適配器,把Adapter適配爲Client須要的Target

    4.2.3 適配器模式示例代碼

using System;

namespace Test2 {
    class Program {
        static void Main(string[] args) {
            Adaptee adaptee = new Adaptee();
            Target target = new Adapter(adaptee);
            target.request();
        }
    }

    public interface Target {
        void request();
    }

    public class Adapter : Target {
        private Adaptee adaptee;

        public Adapter(Adaptee adaptee) {
            this.adaptee = adaptee;
        }

        public void request() {
            adaptee.specificRequest();
        }
    }


    public class Adaptee {
        public void specificRequest() {
            
        }
    }

}
View Code

    4.2.4 使用適配器模式來實現示例

using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;

namespace Test2 {
    class Program {
        static void Main(string[] args) {
            LogModel lml = new LogModel();
            lml.setLogId("001");
            lml.setOperateUser("admin");
            lml.setOperateTime("2010-03-02 10:08:18");
            lml.setLogContent("這是一個測試");
            
            List<LogModel> list = new List<LogModel>();
            list.Add(lml);
            LogFileOperateApi logFileApi = new LogFileOperate("");
            
            
            LogDbOperateApi api = new Adapter(logFileApi);
            
            api.createLog();
            api.updateLog();
            api.removeLog();
            api.getAllLog();
        }
    }

    public class LogModel {
        private string logId;
        private string operateUser;
        private string operateTime;
        private string logContent;

        public string getLogId() {
            return logId;
        }

        public void setLogId(string logId) {
            this.logId = logId;
        }

        public string getOperateUser() {
            return operateUser;
        }

        public void setOperateUser(string operateUser) {
            this.operateUser = operateUser;
        }

        public string getOperateTime() {
            return operateTime;
        }

        public void setOperateTime(string operateTime) {
            this.operateTime = operateTime;
        }

        public string getLogContent() {
            return logContent;
        }

        public void setLogContent(string logContent) {
            this.logContent = logContent;
        }

        public override string ToString() {
            return "logId=" + logId + ", operateUser=" + operateUser + ", operateTime=" + operateTime + ",logContent=" +
                   logContent;
        }
    }

    public interface LogFileOperateApi {
        string readLogFile();
        void writeLogFile(List<LogModel> list);
    }

    public class LogFileOperate : LogFileOperateApi {
        private string logFilePathName = "AdapterLog.log";

        public LogFileOperate(string logFilePathName) {
            if (logFilePathName != null && logFilePathName.Trim().Length > 0) {
                this.logFilePathName = logFilePathName;
            }
        }

        public string readLogFile() {
            List<LogModel> list = null;
            
            StreamReader sr = new StreamReader(logFilePathName,Encoding.Default);

            string line = sr.ReadLine();

            return line;
        }

        public void writeLogFile(List<LogModel> list) {
            FileStream fs = new FileStream(logFilePathName,FileMode.Create);
            StreamWriter sw = new StreamWriter(fs);

            foreach (LogModel logModel in list) {
                sw.Write(logModel.ToString());
            }
            sw.Flush();
            sw.Close();
            fs.Close();
        }
    }

    public interface LogDbOperateApi {
        void createLog(LogModel lm);
        void updateLog(LogModel lm);
        void removeLog(LogModel lm);
        List<LogModel> getAllLog();
    }

    public class Adapter : LogDbOperateApi {
        private LogFileOperateApi adaptee;

        public Adapter(LogFileOperateApi adaptee) {
            this.adaptee = adaptee;
        }

        public void createLog(LogModel lm) {
            List<LogModel> list = new List<LogModel>();
            adaptee.writeLogFile(list);
        }

        public List<LogModel> getAllLog() {
            return adaptee.readLogFile();
        }

        public void removeLog(LogModel lm) {
            List<LogModel> list = new List<LogModel>();
            list.Remove(lm);
        }

        public void updateLog(LogModel lm) {
            
        }
    }
}
View Code

  4.3 模式講解

    4.3.1 認識適配器模式

適配器模式的調用順序示意圖

    4.3.2 適配器模式的實現

    4.3.3 雙向適配器

    4.3.4 對象適配器和類適配器

    4.3.5 適配器模式的優缺點

優勢:

  1.更好的複用性

    若是功能是已經有了的,只是接口不兼容,那麼經過適配器模式就可讓這些功能獲得更好的複用.

  2.更好的可擴展性

    在實現適配器功能的時候,能夠調用本身開發的功能,從而天然地擴展系統的功能

缺點

  1.過多地使用適配器,會讓系統很是零亂,不容易總體進行把握

    4.3.6 思考適配器模式

Q:適配器模式的本質

A:轉換匹配,複用功能

   適配器經過轉換調用已有的實現,從而能把已有的實現匹配成須要的接口,使之能知足客戶端的須要.也就是說轉換匹配是手段,而複用已有的功能纔是目的

   在進行轉換匹配的過程當中,適配器還能夠在轉換調用的先後實現一些功能處理,也就是實現智能的適配

Q:什麼時候選用適配器模式

A:  1.若是你想要使用一個已經存在的類,可是它的接口不符合你的需求,這種狀況能夠使用適配器模式,來把已有的實現轉換成你須要的接口

   2.若是你想建立一個能夠複用的類,這個類可能和一些不兼容的類一塊兒工做,這種狀況能夠使用適配器模式,到時候須要什麼就適配什麼

   3.若是你想使用一些已經存在的子類,可是不可能對每個子類都進行適配,這種狀況能夠選用對象適配器,直接適配這些子類的父類就能夠了.

    4.3.7 相關模式

1.適配器模式與橋接模式

  其實這兩個模式除告終構略爲類似外,功能上徹底不一樣

  適配器模式是把兩個或者多個接口的功能進行轉換匹配,而橋接模式是讓接口和實現部分相分離,以便它們能夠相對獨立地變化

2.適配器模式與裝飾模式

  兩個模式有一個很大的不一樣:通常適配器適配事後是須要改變接口的,若是不改接口就沒有必要適配了,而裝飾模式是不改接口的,不管多少層裝飾都是一個接口

  所以裝飾模式能夠很容易地支持遞歸組合,而適配器就作不到.每次的接口不一樣,沒法遞歸

3.適配器和代理模式

  適配器模式能夠和代理模式組合使用,在實現適配器的時候,能夠經過代理來調用Adaptee,這樣能夠得到更大的靈活性.

4.適配器模式和抽象工廠模式

  在適配器實現的時候,一般須要獲得被適配的對象.若是被適配的是一個接口,那麼就能夠結合一些能夠創造對象實例的設計模式,來獲得被適配的對象示例

  好比抽象工廠模式,單例模式,工廠方法模式等.

第5章 單例模式(Singleton)

  5.1 場景問題

    5.1.1 讀取配置文件的內容

    5.1.2 不用模式的解決方案

using System;

namespace Test2 {
    class Program {
        static void Main(string[] args) {
            AppConfig config = new AppConfig();
            string paramA = config.getParameterA();
            string paramB = config.getParameterB();
            
            Console.WriteLine("paramA=" + paramA + ",paramB=" + paramB);
        }
    }

    public class AppConfig {
        private string parameterA = "a";
        private string parameterB = "b";

        public AppConfig() {
            readConfig();
        } 
        
        public string getParameterA() {
            return parameterA;
        }

        public string getParameterB() {
            return parameterB;
        }

        private void readConfig() {
            Console.WriteLine("readConfig");
        }
    } 
}
View Code

    5.1.3 有何問題

Q:在一個系統運行期間,某個類只須要一個類實例j就能夠了,那麼應該怎樣實現呢?

  5.2 解決方案

    5.2.1 使用單例模式來解決問題

Q:單例模式的定義

A:保證一個類僅有一個實例,並提供一個訪問它的全局訪問點

    5.2.2 單例模式的結構和說明

 

  [Singleton] 負責建立Singleton類本身的惟一實例,並提供一個getInstance方法,讓外部來訪問這個類的惟一實例

    5.2.3 單例模式示例代碼

    // 懶漢
    public class Singleton {
        private static Singleton uniqueInstance = null;

        private Singleton() {
            
        }

        public static Singleton getInstance() {
            if (uniqueInstance == null) {
                uniqueInstance = new Singleton();
            }

            return uniqueInstance;
        }

        public void singletonOperation() {
            
        }

        private string singletonData;

        public string getSingletonData() {
            return singletonData;
        }
    }
    
    // 餓漢
    public class Singleton {
        private static Singleton uniqueInstance = new Singleton();

        private Singleton() {
            
        }

        public static Singleton getInstance() {
            return uniqueInstance;
        }

        public void singletonOperation() {
            
        }
        
        private string singletonData;

        public string getSingletonData() {
            return singletonData;
        }        
    }
View Code

    5.2.4 使用單例模式重寫示例

  5.3 模式講解

    5.3.1 認識單例模式

1.單例模式的功能

  單例模式是用來保證這個類在運行期間只會被建立一個類實例,另外,單例模式還提供了一個全局惟一訪問這個類實例的訪問點

2.單例模式的範圍

3.單例模式的命名

    5.3.2 懶漢式和餓漢式實現

懶漢式調用順序示意圖

餓漢式調用順序示意圖

    5.3.3 延遲加載的思想

    5.3.4 緩存的思想

    5.3.5 Java中緩存的基本實現

    5.3.6 利用緩存來實現單例模式

    5.3.7 單例模式的優缺點

優勢:

  1.時間和空間

    懶漢式是典型的時間換空間,也就是每次獲取實例都會進行判斷,看是否須要建立實例,浪費判斷的時間.固然,若是一直沒有人使用的話,那就不會建立實例,則節約內存空間.

    餓漢式是典型的空間換時間,當類裝載的時候就會建立類實例,無論你用不用,先建立出來,而後每次調用的時候,就不須要再判斷了,節省了運行時間

  2.線程安全

    5.3.8 在Java中一種更好的單例實現方式

    5.3.9 單例和枚舉

    5.3.10 思考單例模式

Q:單例模式的本質

A:控制實例數目

Q:什麼時候選用單例模式

A:當須要控制一個類的實例只能有一個,並且客戶只能從一個全局訪問點訪問它時,能夠選用單例模式,這些功能剛好是單例模式要解決的問題

    5.3.11 相關模式

不少模式均可以使用單例模式,只要這些模式中的某個類,須要控制實例爲一個的時候,就能夠很天然地使用上單例模式.好比抽象工廠方法中的具體工廠類就一般是一個單例

第6章 工廠方法模式(Factory Method)

  6.1 場景問題

    6.1.1 導出數據的應用框架

    6.1.2 框架的基礎知識

Q:什麼是框架

A:簡單點說,框架就是能完成必定功能的半成品軟件

   就其本質而言,框架是一個軟件,並且是一個半成品的軟件.所謂半成品,就是還不能徹底實現用戶須要的功能.框架只是實現用戶須要的功能的一部分,還須要進一步加工,才能成爲一個知足用戶須要的,完整的軟件.所以框架級的軟件,它的主要客戶是開發人員,而不是最終用戶

Q:框架和設計模式的關係

A:  1.設計模式比框架更抽象

    框架已是實現出來的軟件了,雖然只是個半成品的軟件,但畢竟是已經實現出來的了;而設計模式的重心還在於解決問題的方案上,也就是還停留在思想的層面上.所以設計模式比框架更爲抽象

     2.設計模式是比框架更小的體系結構元素

    如上所述,框架是已經實現出來的軟件,並實現了一些的功能,所以一個框架一般會包含多個設計模式的應用

     3.框架比設計模式更加特里化

       框架是完成必定功能的半成品軟件,也就是說,框架的目的很明確,就是要解決某一個領域的某些問題,那是很具體的功能.不一樣的領域實現出來的框架是不同的

    而設計模式還停留在思想的層面,只要相應的問題適合某個設計模式來解決,在不一樣的領域均可以應用

       所以框架老是針對特定領域的,而設計模式更加註重從思想上,方法上來解決問題,更加通用化

    6.1.3 有何問題

  6.2 解決方案

    6.2.1 使用工廠方法模式來解決問題

Q:工廠方法模式的定義

A:定義一個用於建立對象的接口,讓子類決定實例化哪個類,Factory Method使一個類的實例化延遲到其子類.

    6.2.2 工廠方法模式的結構和說明

  [Product] 定義工廠方法所建立的對象的接口,也就是實際須要使用的對象的接口

  [ConcreteProduct] 具體的Product接口的實現對象

  [Creator] 建立器,聲明工廠方法,工廠方法一般會返回一個Product類型的實例對象,並且可能是抽象方法.也能夠在Creator裏面提供工廠方法的默認實現,讓工廠方法返回一個缺省的Product類型的實例對象

  [ConcreteCreator] 具體的建立對象,覆蓋實現Creator定義的工廠方法,返回具體的Product實例

    6.2.3 工廠方法模式示例代碼

using System;

namespace Test2 {
    class Program {
        static void Main(string[] args) {

        }

        
        public interface Product {
            
        }
        
        public class ConcreteProduct : Product {
            
        }
        
        public abstract class Creator {
            public abstract Product factoryMethod();

            public void someOperation() {
                Product product = factoryMethod();
            }
        }
        
        public class ConcreteCreator : Creator {
            public override Product factoryMethod() {
                return new ConcreteProduct();
            }
        }
    }
}
View Code

    6.2.4 使用工廠方法模式來實現示例

using System;
using System.Runtime.CompilerServices;

namespace Test2 {
    class Program {
        static void Main(string[] args) {
            ExportOperate operate = new ExportDBFileOperate();
            operate.export("測試數據");
        }

        public interface ExportFileApi {
            bool export(string data);
        }
        
        public class ExportTxtFile : ExportFileApi {
            public bool export(string data) {
                Console.WriteLine("導出數據" + data + "到文本文件");
                return true;
            }
        }
        
        public class ExportDBFile : ExportFileApi {
            public bool export(string data) {
                Console.WriteLine("導出數據" + data + "到數據庫備份文件");
                return true;
            }
        }                
        
        public abstract class ExportOperate {
            public bool export(string data) {
                ExportFileApi api = factoryMethod();
                return api.export(data);
            }
            
            protected abstract ExportFileApi factoryMethod();
        }

        
        public class ExportTxtFileOperation : ExportOperate {
            protected override ExportFileApi factoryMethod() {
                return new ExportTxtFile();
            }
        }
        
        public class ExportDBFileOperate : ExportOperate {
            protected override ExportFileApi factoryMethod() {
                return new ExportDBFile();
            }
        }
        
    }
}
View Code

  6.3 模式講解

    6.3.1 認識工廠方法模式

    6.3.2 工廠方法模式與IoC/DI

IoC-Inversion of Control,控制反轉

DI-Dependency Injection,依賴注入

Q:參與者都有誰?

A:通常都有三方參與者,一個是某個對象,另外一個是IoC/DI的容器,還有一個是某個對象的外部資源

Q:誰依賴於誰

A:固然是某個對象依賴於IoC/DI的容器.

Q:爲何須要依賴

A:對象須要IoC/DI的容器l來提供對象須要的外部資源

Q:誰注入於誰

A:很明顯是IoC/DI的容器注入某個對象

Q:到底注入什麼

A:就是注入某個對象所須要的外部資源

Q:誰控制誰

A:固然是IoC/DI的容器來控制對象了

Q:控制什麼

A:主要是控制對象實例的建立

Q:爲何叫反轉

A:反轉是相對於正向而言的.

  正向:在A類中主動獲取所須要的外部資源C,這種狀況被稱爲正向的.

  反向:就是A類再也不主動去獲取C,而是被動等待,等待IoC/DI的容器獲取一個C的實例,而後反向地注入到A類中

Q:依賴注入和控制反轉是同一律念嗎?

A:依賴注入和控制反轉是對同一事情的不一樣m描述.

  依賴注入:應用程序依賴容器建立並注入它所須要的外部資源

  控制反轉:容器控制應用程序,由容器反向地嚮應用程序注入其所須要的外部資源

using System;


namespace Test2 {
    class Program {
        static void Main(string[] args) {
        }
                        
    }

    public interface C {
        void tc();
    }
    
    public class A {
        private C c = null;

        public void setC(C c) {
            this.c = c;
        }

        public void t1() {
            c.tc();
        }
    }
    
    
}
View Code

    6.3.3 平行的類層次結構

Q:平行的類層次結構的含義

A:簡單點說,假若有兩個類層次結構,其中一個類層次中的每一個類在另外一個類層次中都有一個對應的類的結構,就被稱爲平行的類層次結構

  硬盤對象是一個類層次,硬盤的行爲也是一個類層次,並且兩個類層次中的類是對應的.臺式機希捷硬盤對象就對應着硬盤行爲裏面的臺式機希捷硬盤的行爲;筆記本IBM硬盤就對應着筆記本IBM硬盤的行爲,這就是一種典型的平行的類層次結構.

  這種平行的類層次結構用來幹什麼呢?主要用來把一個類層次中的某些行爲分離出來,讓類層次中的類把原來屬於本身的職責,委託給分離出來的類去實現,從而使得類層次自己變得更簡單,更容易擴展和複用

  通常來說,分離出去的這些類的行爲,會對應着類層次結構來組織,從而造成一個新的類層次結構,至關於原來對象行爲的類層次結構,而這個層次結構和原來的類層次結構是存在對應關係的,所以被稱爲平行的類層次結構

    6.3.4 參數化工廠方法

Q:什麼是參數化工廠方法

A:經過給工廠方法傳遞參數,讓工廠方法根據參數的不一樣來建立不一樣的產品對象

    6.3.5 工廠方法模式的優缺點

優勢:

  1.能夠在不知道具體實現的狀況下編程

    工廠方法模式可讓你在實現功能的時候,若是須要某個產品對象,只須要使用產品的接口便可,而無需關心具體的實現.選擇具體實現的任務延遲到子類去完成

  2.更容易擴展對象的新版本

    工廠方法給子類提供了一個掛鉤,使得擴展新的對象版本變得很是容易.好比上面示例的參數化工廠方法實現中,擴展一個新的導出xml文件格式的實現,已有的代碼都不會改變,只要新加入一個子類來提供新的工廠方法實現,而後在客戶端使用這個新的子類便可.

  3.鏈接平行的類層次

缺點:

  1.具體產品對象和工廠方法的耦合性

    在工廠方法模式中,工廠方法是須要建立產品對象的,也就是須要選擇具體的產品對象,並建立它們的實例,所以具體產品對象和工廠方法是耦合的.

    6.3.6 思考工廠方法模式

Q:工廠方法模式的本質

A:延遲到子類來選擇實現

Q:對設計原則的體現

A:工廠方法模式很好地體現了"依賴倒置原則"

Q:什麼時候選用工廠方法模式

A:  1.若是一個類須要建立某個接口的對象,可是又不知道具體的實現,這種狀況能夠選用工廠方法模式,把建立對象的工做延遲到子類中去實現

     2.若是一個類自己就但願由它的子類來建立所需的對象的時候,應該使用工廠方法模式

    6.3.7 相關模式

1.工廠方法模式和抽象工廠模式

  這兩個模式能夠組合使用,具體的放到抽象工廠模式中去講

2.工廠方法模式和模板方法模式

  這兩個模式外觀相似,都有一個抽象類,而後由子類來提供一些實現,可是工廠方法模式的子類專一的是建立產品對象,而模板方法模式的子類專一的是爲固定的算法骨架提供某些步驟的實現

  這兩個模式能夠組合使用,一般在模板方法模式裏面,使用工廠方法來建立模板方法須要的對象

第7章 抽象工廠模式(Abstract Factory)

  7.1 場景問題

    7.1.1 選擇組裝電腦的配件

對於裝機工程師而言,他只知道組裝一臺電腦,須要相應的配件,可是具體使用什麼樣的配件,還得由客戶說了算.也就是說裝機工程師只是負責組裝,而客戶負責選擇裝配所須要的具體的配件.所以,當裝機工程師爲不一樣的客戶組裝電腦時,只須要按照客戶的裝機方案,去獲取相應的配件,而後組裝便可

    7.1.2 不用模式的解決方案

using System;


namespace Test2 {
    class Program {
        static void Main(string[] args) {
            ComputerEngineer engineer = new ComputerEngineer();
            engineer.makeComputer(1, 1);
        }                        
    }


    public interface CPUApi {
        void calculate();
    }
    
    public class InteCPU : CPUApi {
        private int pins = 0;

        public InteCPU(int pins) {
            this.pins = pins;
        }

        public void calculate() {
            Console.WriteLine("now in Intel CPU,pins = " + pins);
        }
    }

    public class AMDCPU : CPUApi {
        private int pins = 0;

        public AMDCPU(int pins) {
            this.pins = pins;
        }

        public void calculate() {
            Console.WriteLine("now in AMD CPU,pins = " + pins);
        }
    }

    public interface MainboardApi {
        void installCPU();
    }

    public class GAMainboard : MainboardApi {
        private int cpuHoles = 0;

        public GAMainboard(int cpuHoles) {
            this.cpuHoles = cpuHoles;
        }

        public void installCPU() {
            Console.WriteLine("now in GAMainboard,cpuHoles = " + cpuHoles);
        }
    }

    public class MSIMainboard : MainboardApi {
        private int cpuHoles = 0;

        public MSIMainboard(int cpuHoles) {
            this.cpuHoles = cpuHoles;
        }

        public void installCPU() {
            Console.WriteLine("now in MSIMainboard,cpuHoles = " + cpuHoles);
        }
    }

    public class CPUFactory {
        public static CPUApi createCPUApi(int type) {
            CPUApi cpu = null;
            if (type == 1) {
                cpu = new InteCPU(1156);
            } else if (type == 2) {
                cpu = new AMDCPU(939);
            }

            return cpu;
        }
    }

    public class MainboardFactory {
        public static MainboardApi createMainboardApi(int type) {
            MainboardApi mainboard = null;

            if (type == 1) {
                mainboard = new GAMainboard(1156);
            } else if (type == 2) {
                mainboard = new MSIMainboard(939);
            }

            return mainboard;
        }
    }

    public class ComputerEngineer {
        private CPUApi cpu = null;
        private MainboardApi mainboard = null;

        public void makeComputer(int cpuType, int mainboardType) {
            // 1:首先準備好裝機所須要的配件
            prepareHardwares(cpuType,mainboardType);
            // 2:組裝機器
            // 3:測試機器
            // 4:交付客戶
        }

        private void prepareHardwares(int cpuType, int mainboardType) {
            this.cpu = CPUFactory.createCPUApi(cpuType);
            this.mainboard = MainboardFactory.createMainboardApi(mainboardType);

            this.cpu.calculate();
            this.mainboard.installCPU();
        }
    }
}
View Code

    7.1.3 有何問題

Q:有何問題

A:這些CPU對象和主板對象實際上是有關係的,是須要相互匹配的.而在上面的實現中,並無維護這種關聯關係,CPU和主板是由客戶端隨意選擇的

  7.2 解決方案

    7.2.1 使用抽象工廠模式來解決問題

Q:抽象工廠模式的定義

A:提供一個建立一些列相關或相互依賴對象的接口,而無需指定它們具體的類

    7.2.2 抽象工廠模式的結構和說明

  [AbstractFactory] 抽象工廠,定義建立一系列產品對象的操做接口

  [ConcreteFactory] 具體的工廠,實現抽象工廠定義的方法,具體實現一系列產品對象的建立

  [AbstractProduct] 定義一類產品對象的接口

  [ConcreteProduct] 具體的產品實現對象,一般在具體工廠裏面,會選擇具體的產品實現對象,來建立符合抽象工廠定義的方法返回的產品類型的對象

  [Client] 客戶端,主要使用抽象工廠來獲取一系列所須要的產品對象,而後面向這些產品對象的接口編程,以實現須要的功能

    7.2.3 抽象工廠模式示例代碼

using System;

namespace Test2 {
    class Program {
        static void Main(string[] args) {
            AbstractFactory af = new ConcreateFactory1();
            af.createProductA();
            af.createProductB();
        }                        
    }

    public interface AbstractProductA {
        
    }

    public class ProductA1 : AbstractProductA {
        
    }

    public class ProductA2 : AbstractProductA {
        
    }

    public interface AbstractProductB {
        
    }

    public class ProductB1 : AbstractProductB {
        
    }

    public class ProductB2 : AbstractProductB {
        
    }
    
    public interface AbstractFactory {
        AbstractProductA createProductA();
        AbstractProductB createProductB();
    }

    public class ConcreateFactory1 : AbstractFactory {
        public AbstractProductA createProductA() {
            return new ProductA1();
        }

        public AbstractProductB createProductB() {
            return new ProductB1();
        }
    }

    public class ConcreateFactory2 : AbstractFactory {
        public AbstractProductA createProductA() {
            return new ProductA2();
        }

        public AbstractProductB createProductB() {
            return new ProductB2();
        }
    }
    
}
View Code

    7.2.4 使用抽象工廠模式重寫示例

using System;

namespace Test2 {
    class Program {
        static void Main(string[] args) {
            ComputerEngineer engineer = new ComputerEngineer();
            AbstractFactory schema = new Schema1();
            engineer.makeComputer(schema);
        }                        
    }


    public interface CPUApi {
        void calculate();
    }
    
    public class InteCPU : CPUApi {
        private int pins = 0;

        public InteCPU(int pins) {
            this.pins = pins;
        }

        public void calculate() {
            Console.WriteLine("now in Intel CPU,pins = " + pins);
        }
    }

    public class AMDCPU : CPUApi {
        private int pins = 0;

        public AMDCPU(int pins) {
            this.pins = pins;
        }

        public void calculate() {
            Console.WriteLine("now in AMD CPU,pins = " + pins);
        }
    }

    public interface MainboardApi {
        void installCPU();
    }

    public class GAMainboard : MainboardApi {
        private int cpuHoles = 0;

        public GAMainboard(int cpuHoles) {
            this.cpuHoles = cpuHoles;
        }

        public void installCPU() {
            Console.WriteLine("now in GAMainboard,cpuHoles = " + cpuHoles);
        }
    }

    public class MSIMainboard : MainboardApi {
        private int cpuHoles = 0;

        public MSIMainboard(int cpuHoles) {
            this.cpuHoles = cpuHoles;
        }

        public void installCPU() {
            Console.WriteLine("now in MSIMainboard,cpuHoles = " + cpuHoles);
        }
    }

    public interface AbstractFactory {
        CPUApi createCPUApi();
        MainboardApi createMainboardApi();
    }

    public class Schema1 : AbstractFactory {
        public CPUApi createCPUApi() {
            return new InteCPU(1156);
        }

        public MainboardApi createMainboardApi() {
            return new GAMainboard(1156);
        }
    }

    public class Schema2 : AbstractFactory {
        public CPUApi createCPUApi() {
            return new AMDCPU(939);
        }

        public MainboardApi createMainboardApi() {
            return new MSIMainboard(939);
        }
    }
    

    public class ComputerEngineer {
        private CPUApi cpu = null;
        private MainboardApi mainboard = null;

        public void makeComputer(AbstractFactory schema) {
            // 1:首先準備好裝機所須要的配件
            prepareHardwares(schema);
            // 2:組裝機器
            // 3:測試機器
            // 4:交付客戶
        }

        private void prepareHardwares(AbstractFactory schema) {
            this.cpu = schema.createCPUApi();
            this.mainboard = schema.createMainboardApi();

            this.cpu.calculate();
            this.mainboard.installCPU();
        }
    }
}
View Code

  7.3 模式講解

    7.3.1 認識抽象工廠模式

Q:抽象工廠模式的功能

A:抽象工廠的功能是爲一系列相關對象或相互依賴對象建立一個接口.必定要注意,這個接口內的方法不是任意堆砌的,而是一系列相關或相互依賴的方法.從某種意義上看,抽象工廠實際上是一個產品系列,或者是產品簇

    7.3.2 定義可擴展的工廠

    7.3.3 抽象工廠模式和DAO

    7.3.4 抽象工廠模式的優缺點

優勢:

  1.分類接口和實現

    客戶端使用抽象工廠來建立須要的對象,而客戶端根本就不知道具體的實現是誰,客戶端只是面向產品的接口編程而已.也就是說,客戶端從具體的產品實現中解耦

  2.使得切換產品簇變得容易

    一個具體的工廠實現表明的是一個產品簇.客戶端選用不一樣的工廠實現,就至關因而在切換不一樣的產品簇

缺點:

  1.不太容易擴展新的產品

    若是須要給整個產品簇添加一個新的產品,那麼就須要修改抽象工廠,這樣就會致使修改全部的工廠實現類.

  2.容易形成類層次複雜

    7.3.5 思考抽象工廠模式

Q:抽象工廠模式的本質

A:選擇產品簇的實現

    7.3.6 相關模式

1:抽象工廠模式和工廠方法模式

  工廠方法模式通常是針對單獨的產品對象的建立,而抽象工廠模式注重產品簇對象的建立,這是它們的區別

  若是把抽象工廠建立的產品簇簡化,整個產品簇就只有一個產品,那麼這個時候的抽象工廠跟工廠方法是差很少的,也就是抽象工廠能夠退化成工廠方法,而工廠方法又能夠退化成簡單工廠,這是它們的聯繫

  在抽象工廠的實現中,還能夠使用工廠方法來提供抽象工廠的具體實現,也就是說它們能夠組合使用

2.抽象工廠模式和單例模式

  在抽象工廠模式裏面,具體的工廠實現,在整個應用中,一般一個產品系列只須要一個實例就能夠了,所以能夠把具體的工廠實現成爲單例.

第8章 生成器模式(Builder)

  8.1 場景問題

    8.1.1 繼續導出數據的應用框架

    8.1.2 不用模式的解決方案

using System;
using System.Collections.Generic;
using System.Text;

namespace Test2 {
    class Program {
        static void Main(string[] args) {
            ExportHeaderModel ehm = new ExportHeaderModel();
            ehm.setDepId("一分公司");
            ehm.setExportDate("2010-05-18");
                
            Dictionary<string,List<ExportDataModel>> mapData = new Dictionary<string, List<ExportDataModel>>();
            
            List<ExportDataModel> col = new List<ExportDataModel>();
            
            ExportDataModel edm1 = new ExportDataModel();
            edm1.setProductId("產品001號");
            edm1.setPrice(100);
            edm1.setAmount(80);
            
            ExportDataModel edm2 = new ExportDataModel();
            edm2.setProductId("產品002號");
            edm2.setPrice(99);
            edm2.setAmount(55);
            
            col.Add(edm1);
            col.Add(edm2);

            mapData["銷售記錄表"] = col;
            
            ExportFooterModel efm = new ExportFooterModel();
            efm.setExportUser("張三");
            
            
            ExportToTxt toTxt = new ExportToTxt();
            toTxt.export(ehm,mapData,efm);
            
            ExportToXml toXml = new ExportToXml();
            toXml.export(ehm,mapData,efm);
        }                        
    }

    public class ExportHeaderModel {
        private string depId;
        private string exportDate;

        public string getDepId() {
            return depId;
        }

        public void setDepId(string depId) {
            this.depId = depId;
        }

        public string getExportDate() {
            return exportDate;
        }

        public void setExportDate(string exporteDate) {
            this.exportDate = exporteDate;
        }
    }

    public class ExportDataModel {
        private string productId;
        private double price;
        private double amount;

        public string getProductId() {
            return productId;
        }

        public void setProductId(string productId) {
            this.productId = productId;
        }

        public double getPrice() {
            return price;
        }

        public void setPrice(double price) {
            this.price = price;
        }

        public double getAmount() {
            return amount;
        }

        public void setAmount(double amount) {
            this.amount = amount;
        }
    }

    public class ExportFooterModel {
        private string exportUser;

        public string getExportUser() {
            return exportUser;
        }

        public void setExportUser(string exportUser) {
            this.exportUser = exportUser;
        }
    }

    public class ExportToTxt {
        public void export(ExportHeaderModel ehm, Dictionary<string, List<ExportDataModel>> mapData,
            ExportFooterModel efm) {
            StringBuilder sb = new StringBuilder();
            sb.Append(ehm.getDepId() + "," + ehm.getExportDate() + "\n");

            foreach (string mapDataKey in mapData.Keys) {
                sb.Append(mapDataKey + "\n");
                foreach (ExportDataModel exportDataModel in mapData.GetValueOrDefault(mapDataKey)) {
                    sb.Append(exportDataModel.getProductId() + "," + exportDataModel.getPrice() + "," +
                              exportDataModel.getAmount() + "\n");
                }
            }

            sb.Append(efm.getExportUser());
            
            Console.WriteLine("輸出到文本文件的內容: \n" + sb);
        }
    }

    public class ExportToXml {
        public void export(ExportHeaderModel ehm, Dictionary<string, List<ExportDataModel>> mapData,
            ExportFooterModel efm) {
            StringBuilder buffer = new StringBuilder();
            buffer.Append("<?xml version='1.0' encoding='gb2312'?>\n");
            buffer.Append("<Report>\n");
            buffer.Append("  <Header>\n");
            buffer.Append("    <DepId>" + ehm.getDepId() + "</DepId>\n");
            buffer.Append("    <ExportDate>" + ehm.getExportDate() + "</ExportDate>\n");
            buffer.Append("  </Header>\n");
            buffer.Append("  <Body>\n");
            foreach (string mapDataKey in mapData.Keys) {
                buffer.Append("    <Datas TableName=\"" + mapDataKey + "\">\n");
                foreach (ExportDataModel exportDataModel in mapData.GetValueOrDefault(mapDataKey)) {
                    buffer.Append("      <Data>\n");
                    buffer.Append("        <ProductId>" + exportDataModel.getProductId() + "</ProductId>\n");
                    buffer.Append("        <Price>" + exportDataModel.getPrice() + "</Price>\n");
                    buffer.Append("        <Amount>" + exportDataModel.getAmount() + "</Amount>\n");
                    buffer.Append("      </Data>\n");
                }

                buffer.Append("    </Datas>\n");
            }

            buffer.Append("  </Body>\n");
            buffer.Append("  <Footer>\n");
            buffer.Append("    <ExportUser>" + efm.getExportUser() + "</ExportUser>\n");
            buffer.Append("  </Footer>\n");
            buffer.Append("</Report>\n");
            Console.WriteLine("輸出到XML文件的內容: \n" + buffer);
        }
    }
}
View Code

    8.1.3 有何問題

Q:有何問題

A:對於不一樣的輸出格式,處理步驟是同樣的,可是每步的具體實現是不同的

  1.先拼接文件頭的內容

  2.而後拼接文件體的內容

  3.再拼接文件尾的內容

  4.最後把拼接好的內容輸出去成爲文件

   構建每種輸出格式的文件內容的時候,都會重複這幾個處理步驟,應該提煉出來,造成公共的處理過程

   從此可能會有不少不一樣輸出格式的要求,這就須要在處理過程不變的狀況下,能方便地切換不一樣的輸出格式的處理

   換句化來講,也就是構建每種格式的數據文件處理過程,應該和具體的步驟實現分開,這樣就可以複用處理過程,並且能很容易地切換不一樣地輸出格式.

  8.2 解決方案

    8.2.1 使用生成器模式來解決問題

Q:生成器模式的定義

A:將一個複雜對象的構建與它的表示分離,使得一樣的構建過程能夠建立不一樣的表示.

    8.2.2 生成器模式的結構和說明

  [Builder] 生成器接口,定義建立一個Product對象所須要的各個部件的操做

  [ConcreteBuilder] 具體的生成器實現,實現各個部件的建立,並負責組裝Product對象的各個部件,同時還提供一個讓用戶獲取組裝完成後的產品對象的方法

  [Director] 指導者,也被稱爲導向者,主要用來使用Builder接口,以一個統一的過程來構建所須要的Product對象

  [Product] 產品,表示被生成器構建的複雜對象,包含多個部件

    8.2.3 生成器模式示例代碼

using System;

namespace Test2 {
    class Program {
        static void Main(string[] args) {
            
        }                        
    }

    public interface Builder {
        void buildPart();
    }

    public class ConcreteBuilder : Builder {
        private Product resultProduct;

        public Product getResult() {
            return resultProduct;
        }

        public void buildPart() {
            
        }
    }

    public interface Product {
        
    }

    public class Director {
        private Builder builder;

        public Director(Builder builder) {
            this.builder = builder;
        }

        public void construct() {
            builder.buildPart();
        }
    }

}
View Code

    8.2.4 使用生成器模式重寫示例

using System;
using System.Collections.Generic;
using System.Text;

namespace Test2 {
    class Program {
        static void Main(string[] args) {
            ExportHeaderModel ehm = new ExportHeaderModel();
            ehm.setDepId("一分公司");
            ehm.setExportDate("2010-05-18");
                
            Dictionary<string,List<ExportDataModel>> mapData = new Dictionary<string, List<ExportDataModel>>();
            
            List<ExportDataModel> col = new List<ExportDataModel>();
            
            ExportDataModel edm1 = new ExportDataModel();
            edm1.setProductId("產品001號");
            edm1.setPrice(100);
            edm1.setAmount(80);
            
            ExportDataModel edm2 = new ExportDataModel();
            edm2.setProductId("產品002號");
            edm2.setPrice(99);
            edm2.setAmount(55);
            
            col.Add(edm1);
            col.Add(edm2);

            mapData["銷售記錄表"] = col;
            
            ExportFooterModel efm = new ExportFooterModel();
            efm.setExportUser("張三");
            
            
            TxtBuilder txtBuilder = new TxtBuilder();            
            Director director = new Director(txtBuilder);
            director.constructor(ehm,mapData,efm);
            Console.WriteLine("輸出到文本文件的內容: \n" + txtBuilder.getResult());
            
            XmlBuilder xmlBuilder = new XmlBuilder();
            Director director2 = new Director(xmlBuilder);
            director2.constructor(ehm,mapData,efm);
            Console.WriteLine("輸出到XMl文件的內容: \n" + xmlBuilder.getResult());
            
            
        }                        
    }

    public class ExportHeaderModel {
        private string depId;
        private string exportDate;

        public string getDepId() {
            return depId;
        }

        public void setDepId(string depId) {
            this.depId = depId;
        }

        public string getExportDate() {
            return exportDate;
        }

        public void setExportDate(string exporteDate) {
            this.exportDate = exporteDate;
        }
    }

    public class ExportDataModel {
        private string productId;
        private double price;
        private double amount;

        public string getProductId() {
            return productId;
        }

        public void setProductId(string productId) {
            this.productId = productId;
        }

        public double getPrice() {
            return price;
        }

        public void setPrice(double price) {
            this.price = price;
        }

        public double getAmount() {
            return amount;
        }

        public void setAmount(double amount) {
            this.amount = amount;
        }
    }

    public class ExportFooterModel {
        private string exportUser;

        public string getExportUser() {
            return exportUser;
        }

        public void setExportUser(string exportUser) {
            this.exportUser = exportUser;
        }
    }

    public interface Builder {
        void buildHeader(ExportHeaderModel ehm);
        void buildBody(Dictionary<string, List<ExportDataModel>> mapData);
        void buildFooter(ExportFooterModel efm);
    }

    public class TxtBuilder : Builder {
        private StringBuilder buffer = new StringBuilder();
        
        public void buildHeader(ExportHeaderModel ehm) {
            buffer.Append(ehm.getDepId() + "," + ehm.getExportDate() + "\n");
        }

        public void buildBody(Dictionary<string, List<ExportDataModel>> mapData) {
            foreach (string mapDataKey in mapData.Keys) {
                buffer.Append(mapDataKey + "\n");
                foreach (ExportDataModel exportDataModel in mapData.GetValueOrDefault(mapDataKey)) {
                    buffer.Append(exportDataModel.getProductId() + "," + exportDataModel.getPrice() + "," +
                              exportDataModel.getAmount() + "\n");
                }
            }            
        }

        public void buildFooter(ExportFooterModel efm) {
            buffer.Append(efm.getExportUser());
        }

        public StringBuilder getResult() {
            return buffer;
        }
    }

    public class XmlBuilder : Builder {
        private StringBuilder buffer = new StringBuilder();

        public void buildHeader(ExportHeaderModel ehm) {
            buffer.Append("<?xml version='1.0' encoding='gb2312'?>\n");
            buffer.Append("<Report>\n");
            buffer.Append("  <Header>\n");
            buffer.Append("    <DepId>" + ehm.getDepId() + "</DepId>\n");
            buffer.Append("    <ExportDate>" + ehm.getExportDate() + "</ExportDate>\n");
            buffer.Append("  </Header>\n"); 
        }

        public void buildBody(Dictionary<string, List<ExportDataModel>> mapData) {
            buffer.Append("  <Body>\n");
            foreach (string mapDataKey in mapData.Keys) {
                buffer.Append("    <Datas TableName=\"" + mapDataKey + "\">\n");
                foreach (ExportDataModel exportDataModel in mapData.GetValueOrDefault(mapDataKey)) {
                    buffer.Append("      <Data>\n");
                    buffer.Append("        <ProductId>" + exportDataModel.getProductId() + "</ProductId>\n");
                    buffer.Append("        <Price>" + exportDataModel.getPrice() + "</Price>\n");
                    buffer.Append("        <Amount>" + exportDataModel.getAmount() + "</Amount>\n");
                    buffer.Append("      </Data>\n");
                }

                buffer.Append("    </Datas>\n");
            }

            buffer.Append("  </Body>\n");            
        }

        public void buildFooter(ExportFooterModel efm) {
            buffer.Append("  <Footer>\n");
            buffer.Append("    <ExportUser>" + efm.getExportUser() + "</ExportUser>\n");
            buffer.Append("  </Footer>\n");
            buffer.Append("</Report>\n");
        }

        public StringBuilder getResult() {
            return buffer;
        }
    }

    public class Director {
        private Builder builder;

        public Director(Builder builder) {
            this.builder = builder;
        }

        public void constructor(ExportHeaderModel ehm, Dictionary<string, List<ExportDataModel>> mapData,
            ExportFooterModel efm) {
            builder.buildHeader(ehm);
            builder.buildBody(mapData);
            builder.buildFooter(efm);
        }
    }
}
View Code

  8.3 模式講解

    8.3.1 認識生成器模式

Q:生成器模式的功能

A:生成器模式的主要功能是構建複雜的產品,並且是細化的,分步驟的構建產品,也就是生成器模式重在一步一步解決構造複雜對象的問題.再直白點說,生成器模式的重心在於分離構建算法和具體的構造實現,從而使得構建算法能夠重用

Q:生成器模式的構造

A:Builder模式存在兩個部分,一個部分是部件構造和產品裝配,另外一個部分是總體構建的算法

    8.3.2 生成器模式的實現

    8.3.3 使用生成器模式構建複雜對象

    8.3.4 生成器模式的優勢

優勢

  1. 鬆散耦合

  2.能夠很容易地改變產品的內部表示

  3.更好的複用性

    8.3.5 思考生成器模式

Q:生成器模式的本質

A:分離總體構建算法和部件構造

  構建一個複雜的對象,原本就有構建的過程,以及構建過程當中具體的實現.生成器模式就是用來分離這兩個部分,從而使得程序結構更鬆散,擴展更容易,複用性更好,同時也會使得代碼更清晰,意圖更明確

  雖然在生成器模式的總體構建算法中,會一步一步引導Builder來構建對象,但這並非說生成器主要就是用來實現分步驟構建對象的,生成器模式的重心仍是在於分離總體構建算法和部件構造,而分步

  構建對象不過是總體構建算法的一個簡單表現,或者說是一個附帶產物

Q:什麼時候選用生成器模式

A:  1.若是建立對象的算法,應該獨立於該對象的組成部分以及它們的裝配方式時

     2.若是同一個構建過程有着不一樣的表示時

    8.3.6 相關模式

1.生成器模式和工廠方法模式

  這兩個模式能夠組合使用

  生成器模式的Builder實現中,一般須要選擇具體的部件實現.一個可行的方案就是實現成爲工廠方法,經過工廠方法來獲取具體的部件對象,而後再進行部件的裝配

2.生成器模式和抽象工廠模式

  抽象工廠模式的主要目的是建立產品簇,這個產品簇裏面的單個產品就至關因而構成一個複雜對象的部件對象,抽象工廠對象建立完成後就當即返回整個產品簇;

  而生成器模式的主要目的是按照構造算法,一步一步來構建一個複雜的產品對象,一般要等到整個構建過程結束之後,纔會獲得最終的產品對象.

  事實上,這兩個模式是能夠組合使用的.在生成器模式的Builder實現中,須要建立各個部件對象,而這些部件對象是有關聯的,一般是構成一個複雜對象的部件對象

  也就是說,Builder實現中,須要獲取構成一個複雜對象的產品簇,那天然就能夠使用抽象工廠模式來實現,這樣一來,由抽象工廠模式負責了部件對象的建立,

  Builder實現裏面則主要負責產品對象總體的構建了

3.生成器模式和模板方法模式

  這是兩個很是相似的模式,初看之下,不會以爲這兩個模式有什麼關聯.可是仔細一思考,卻發現兩個模式在功能上很相似.模板方法模式主要是用來定義算法的骨架,把算法中某些步驟延遲到子類中實現.

  再想一想生成器模式,Director用來定義總體的構建算法,把算法中某些涉及到具體部件對象的建立和裝配的功能,委託給具體的Builder實現

  雖然生成器不是延遲到子類,是委託給Builder,但那只是具體實現方式上的差異,從實質上看兩個模式很相似,都是定義一個固定算法骨架,而後把算法中的某些具體步驟交給其餘類來完成,都能實現總體算法步驟

  和某些具體步驟實現的分離

  固然這兩個模式也有很大的區別,首先是模式的目的,生成器模式是用來構建複雜對象的,而模板方法是用來定義算法骨架的,尤爲是一些複雜的業務功能的處理算法的骨架;其次是模式的實現,生成器模式是採用委託的方法,

  而模板方法採用的是繼承的方式;另外從使用的複雜度上,生成器模式須要組合Director和Builder對象,而後才能開始構建,要等構建完後才能得到最終的對象,而模板方法就沒有這麼麻煩,直接使用子類對象便可.

4.生成器模式和組合模式

  這兩個模式能夠組合使用

  對於複雜的組合結構,能夠使用生成器模式來一步一步構建.

第9章 原型模式(Prototype)

  9.1 場景問題

    9.1.1 訂單處理系統

    9.1.2 不用模式的解決方案

using System;

namespace Test2 {
    class Program {
        static void Main(string[] args) {
            PersonOrder op = new PersonOrder();
            op.setOrderProductNum(2925);
            op.setCustomerName("張三");
            op.setProductId("P0001");
            
            OrderBusiness ob = new OrderBusiness();
            ob.saveOrder(op);
            
        }                        
    }

    public interface OrderApi {
        int getOrderProductNum();
        void setOrderProductNum(int num);
    }

    public class PersonOrder : OrderApi {
        private string customerName;
        private string productId;
        private int orderProductNum = 0;


        public string getCustomerName() {
            return customerName;
        }

        public void setCustomerName(string customerName) {
            this.customerName = customerName;
        }

        public string getProductId() {
            return productId;
        }

        public void setProductId(string productId) {
            this.productId = productId;
        }
                        
        public int getOrderProductNum() {
            return orderProductNum;
        }

        public void setOrderProductNum(int num) {
            this.orderProductNum = num;
        }

        public override string ToString() {
            return "本我的訂單的訂購人是 = " + this.customerName + ", 訂購產品是 = " + this.productId + ", 訂購數量爲 = " +
                   this.orderProductNum;
        }
    }

    public class EnterpriseOrder : OrderApi {
        private string enterpriseName;
        private string productId;
        private int orderProductNum = 0;

        public string getEnterpriseName() {
            return enterpriseName;
        }

        public void setEnterpriseName(string enterpriseName) {
            this.enterpriseName = enterpriseName;
        }

        public string getProductId() {
            return productId;
        }

        public void setProductId(string productId) {
            this.productId = productId;
        }

        public int getOrderProductNum() {
            return orderProductNum;
        }

        public void setOrderProductNum(int num) {
            this.orderProductNum = num;
        }

        public override string ToString() {
            return "本企業訂單的訂購企業是 = " + this.enterpriseName + ", 訂購產品是 = " + this.productId + ", 訂購數量爲 = " +
                   this.orderProductNum;
        }
    }

    public class OrderBusiness {
        public void saveOrder(OrderApi order) {
            while (order.getOrderProductNum() > 1000) {
                OrderApi newOrder = null;
                if (order is PersonOrder) {
                    PersonOrder p2 = new PersonOrder();
                    PersonOrder p1 = (PersonOrder) order;
                    p2.setCustomerName(p1.getCustomerName());
                    p2.setProductId(p1.getProductId());
                    p2.setOrderProductNum(1000);
                    newOrder = p2;
                } else if (order is EnterpriseOrder) {
                    EnterpriseOrder e2 = new EnterpriseOrder();
                    EnterpriseOrder e1 = (EnterpriseOrder) order;
                    e2.setEnterpriseName(e1.getEnterpriseName());
                    e2.setProductId(e1.getProductId());
                    e2.setOrderProductNum(1000);
                    newOrder = e2;
                }
                order.setOrderProductNum(order.getOrderProductNum() - 1000);
                
                Console.WriteLine("拆分生成訂單 == " + newOrder);
            }
            
            Console.WriteLine("訂單 == " + order);
        }
    }

}
View Code

    9.1.3 有何問題

有何問題

已經有了某個對象實例後,如何可以快速簡單地建立出更多的這種對象?

  9.2 解決方案

    9.2.1 使用原型模式來解決問題

Q:原型模式的定義

A:用原型實例指定建立對象的種類,並經過拷貝這些原型建立新的對象

    9.2.2 原型模式的結構和說明

  [Prototype] 聲明一個克隆自身的接口,用來約束想要克隆本身的類,要求它們都要實現這裏定義的克隆方法

  [ConcretePrototype] 實現Prototype接口的類,這些類真正實現了克隆自身的功能.

  [Client] 使用原型的客戶端,首先要獲取到原型實例對象,而後經過原型實例克隆自身來建立新的對象實例

    9.2.3 原型模式示例代碼

using System;

namespace Test2 {
    class Program {
        static void Main(string[] args) {
           
        }                        
    }

    public interface Prototype {
        Prototype clone();
    }

    public class ConcretePrototype1 : Prototype {
        public Prototype clone() {
            Prototype prototype = new ConcretePrototype1();
            return prototype;
        }
    }

    public class ConcretePrototype2 : Prototype {
        public Prototype clone() {
            Prototype prototype = new ConcretePrototype2();
            return prototype;
        }
    }

    public class Client {
        private Prototype prototype;

        public Client(Prototype prototype) {
            this.prototype = prototype;
        }

        public void operation() {
            Prototype newPrototype = prototype.clone();
        }
    }
}
View Code

    9.2.4 使用原型模式重寫示例

using System;
using System.Collections.Generic;


namespace ConsoleApplication1 {
    public class Program {
        public static void Main(string[] args) {
            OrderApi oa1 = new PersonalOrder();
            oa1.setOrderProductNum(100);
            Console.WriteLine("這是第一次獲取的對象實例 === " + oa1.getOrderProductNum());

            OrderApi oa2 = (OrderApi) oa1.cloneOrder();
            oa2.setOrderProductNum(80);
            Console.WriteLine("輸出克隆出來的實例 === " + oa2.getOrderProductNum());
            
            Console.WriteLine("再次輸出原型實例 === " + oa1.getOrderProductNum());
        }
        
        public interface OrderApi {
            int getOrderProductNum();
            void setOrderProductNum(int num);
            OrderApi cloneOrder();
        }
        
        public class PersonalOrder : OrderApi {
            private string customerName;
            private string productId;
            private int orderProductNum = 0;

            public string getCustomerName() {
                return customerName;
            }

            public void setCustomerName(string customerName) {
                this.customerName = customerName;
            }

            public string getProductId() {
                return productId;
            }

            public void setProductId(string productId) {
                this.productId = productId;
            }

            public int getOrderProductNum() {
                return this.orderProductNum;
            }

            public void setOrderProductNum(int num) {
                this.orderProductNum = num;
            }

            public override string ToString() {
                return "本我的訂單的訂購人是 = " + this.customerName + ", 訂購產品是 = " + this.productId + ", 訂購數量 = " +
                       this.orderProductNum;
            }

            public OrderApi cloneOrder() {
                PersonalOrder order = new PersonalOrder();
                order.setCustomerName(this.customerName);
                order.setProductId(this.productId);
                order.setOrderProductNum(this.orderProductNum);

                return order;
            }
        }
        
        public class EnterpriseOrder : OrderApi {
            private string enterpriseName;
            private string productId;
            private int orderProductNum = 0;

            public string getEnterpriseName() {
                return enterpriseName;
            }

            public void setEnterpriseName(string enterpriseName) {
                this.enterpriseName = enterpriseName;
            }

            public string getProductId() {
                return productId;
            }

            public void setProductId(string productId) {
                this.productId = productId;
            }

            public int getOrderProductNum() {
                return this.orderProductNum;
            }

            public void setOrderProductNum(int num) {
                this.orderProductNum = num;
            }

            public override string ToString() {
                return "本企業訂單的訂購企業是 = " + this.enterpriseName + ", 訂購產品 = " + this.productId + ", 訂購數量爲 = " +
                       this.orderProductNum;
            }

            public OrderApi cloneOrder() {
                EnterpriseOrder order = new EnterpriseOrder();
                order.setEnterpriseName(this.enterpriseName);
                order.setProductId(this.productId);
                order.setOrderProductNum(this.orderProductNum);

                return order;
            }
        }
        
        public class OrderBusiness {
            public void saveOrder(OrderApi order) {
                while (order.getOrderProductNum() > 1000) {
                    OrderApi newOrder = order.cloneOrder();
                    newOrder.setOrderProductNum(1000);
                    
                    order.setOrderProductNum(order.getOrderProductNum() - 1000);
                    
                    Console.WriteLine("拆分生成訂單 == " + newOrder);
                }
                
                Console.WriteLine("訂單 === " + order);
            }
        }
    }
}
View Code

  9.3 模式講解

    9.3.1 認識原型模式

Q:原型模式的功能

A:  1.一個是經過克隆來建立新的對象實例

      2.另外一個是爲克隆出來的新的對象實例複製原型實例屬性的值.

    原型模式要實現的主要功能就是:經過克隆來建立新的對象實例.通常來說,新建立出來的實例的數據是和原型實例同樣的.可是具體如何實現克隆,須要由程序自行實現,原型模式並無統一的要求和實現算法

Q:原型與new

A:  原型模式從某種意義上說,就像是new操做,在前面的例子實現中,克隆方法就是使用new來實現的.但請注意,只是"相似於new"而不是"就是new".

     克隆方法和new操做最明顯的不一樣在於:new一個對象實例,通常屬性是沒有值的,或者是隻有默認值;若是是克隆獲得的一個實例,一般屬性是有值的,屬性的值就是原型對象實例在克隆的時候,原型對象實例的屬性的值

Q:原型實例和克隆的實例

A:  原型實例和克隆出來的實例,本質上是不一樣的實例,克隆完成後,它們之間是沒有關聯的,若是克隆完成後,克隆出來的實例的屬性值發生了改變,是不會影響到原型實例的.

    9.3.2 Java中的克隆方法

    9.3.3 淺度克隆和深度克隆

using System;
using System.Collections.Generic;


namespace ConsoleApplication1 {
    public class Program {
        public static void Main(string[] args) {
            Product product = new Product();
            product.setName("產品1");
            
            PersonalOrder oa1 = new PersonalOrder();
            oa1.setProduct(product);
            oa1.setOrderProductNum(100);
            Console.WriteLine("這是第一次獲取的對象實例 = " + oa1);

            PersonalOrder oa2 = (PersonalOrder)oa1.cloneOrder();
            oa2.getProduct().setName("產品2");
            oa2.setOrderProductNum(80);
            Console.WriteLine("輸出克隆出來的實例 = " + oa2);
            
            Console.WriteLine("再次輸出原型實例 = " + oa1);
        }
        
        public interface ProductPrototype {
            ProductPrototype cloneProduct();
        }
        
        public class Product : ProductPrototype {
            private string productId;
            private string name;

            public string getProductId() {
                return productId;
            }

            public void setProductId(string productId) {
                this.productId = productId;
            }
            
            public string getName() {
                return name;
            }

            public void setName(string name) {
                this.name = name;
            }

            public override string ToString() {
                return "產品編號 = " + this.productId + ", 產品名稱 = " + this.name;
            }

            public ProductPrototype cloneProduct() {
                Product product = new Product();
                product.setProductId(this.productId);
                product.setName(this.name);
                return product;
            }
        }
        
        public interface OrderApi {
            void setOrderProductNum(int num);
        }
        
        public class PersonalOrder : OrderApi {
            private string custormerName;
            private int orderProductNum = 0;
            private Product product = null;

            public int getOrderProductNum() {
                return this.orderProductNum;
            }

            public void setOrderProductNum(int num) {
                this.orderProductNum = num;
            }

            public string getCustomerName() {
                return custormerName;
            }

            public void setCustomerName(string customerName) {
                this.custormerName = customerName;
            }

            public Product getProduct() {
                return product;
            }

            public void setProduct(Product product) {
                this.product = product;
            }

            public override string ToString() {
                return "訂購產品是 = " + this.product.getName() + ", 訂購數量爲 = " + this.orderProductNum;
            }

            public OrderApi cloneOrder() {
                PersonalOrder order = new PersonalOrder();
                order.setCustomerName(this.custormerName);
                order.setOrderProductNum(this.orderProductNum);
                order.setProduct((Product)this.product.cloneProduct());
                return order;
            }
        }
    }
}
View Code

    9.3.4 原型管理器

using System;
using System.Collections.Generic;


namespace ConsoleApplication1 {
    public class Program {
        public static void Main(string[] args) {
            Prototype p1 = new ConcretePrototype1();
            PrototypeManager.setPrototype("Prototype1", p1);

            Prototype p3 = PrototypeManager.getPrototype("Prototype1").clone();
            p3.setName("張三");
            Console.WriteLine("第一個實例: " + p3);
            
            Prototype p2 = new ConcretePrototype2();
            PrototypeManager.setPrototype("Prototype1",p2);

            Prototype p4 = PrototypeManager.getPrototype("Prototype1").clone();
            p4.setName("李四");
            Console.WriteLine("第二個實例: " + p4);
            
            PrototypeManager.removePrototype("Prototype1");

            Prototype p5 = PrototypeManager.getPrototype("Prototyp1").clone();
            p5.setName("王五");
            Console.WriteLine("第三個實例: " + p5);
            
            
        }
    }

    public interface Prototype {
        Prototype clone();
        string getName();
        void setName(string name);
    }

    public class ConcretePrototype1 : Prototype {
        private string name;

        public string getName() {
            return name;
        }

        public void setName(string name) {
            this.name = name;
        }

        public Prototype clone() {
            ConcretePrototype1 prototype = new ConcretePrototype1();
            prototype.setName(this.name);
            return prototype;
        }

        public override string ToString() {
            return "Now in Prototype1,name = " + name;
        }
        
    }

    public class ConcretePrototype2 : Prototype {
        private string name;

        public string getName() {
            return name;
        }

        public void setName(string name) {
            this.name = name;
        }

        public Prototype clone() {
            ConcretePrototype2 prototype = new ConcretePrototype2();
            prototype.setName(this.name);
            return prototype;
        }

        public override string ToString() {
            return "Now in Prototype2, name = " + name;
        }
    }

    public class PrototypeManager {
        private static Dictionary<string,Prototype> map = new Dictionary<string, Prototype>();

        private PrototypeManager() {
            
        }

        public static void setPrototype(string prototypeId, Prototype prototype) {
            map[prototypeId]  = prototype;
        }

        public static void removePrototype(string prototypeId) {
            map.Remove(prototypeId);
        }

        public static Prototype getPrototype(string prototypeId) {
            Prototype prototype = map[prototypeId];

            if (prototype == null) {
                throw new Exception("您但願獲取的原型尚未註冊或已被銷燬");
            }

            return prototype;
        }
    }
}
View Code

    9.3.5 原型模式的優缺點

優勢

  1.對客戶端隱藏具體的實現類型

     原型模式的客戶端只知道原型接口的類型,並不知道具體的實現類型,從而減小了客戶端對這些具體實現類型的依賴

  2.在運行時動態改變具體的實現類型

     原型模式能夠在運行期間,由客戶來註冊符合原型接口的實現類型,也能夠動態地改變具體的實現類型,看起來接口沒有任何變化,但其實運行的已是另一個類實例了.由於克隆一個原型就相似於實例化一個類

缺點

  1.原型模式最大的缺點就在於每一個原型的子類都必須實現clone的操做,尤爲在包含引用類型的對象時,clone方法會比較麻煩,必需要可以遞歸地讓全部的相關對象都要正確地實現克隆

    9.3.6 思考原型模式

Q:原型模式的本質

A:克隆生成對象

   克隆是手段,目的是生成新的對象實例.正是由於原型的目的是爲了生成新的對象實例,原型模式一般是被歸類爲建立型的模式

   原型模式也能夠用來解決"只知接口而不知實現的問題",使用原型模式,能夠出現一種獨特的"接口造接口"的景象,這在面向接口編程中頗有用.一樣的功能也能夠考慮使用工廠來實現

   另外,原型模式的中心仍是在建立新的對象實例,至於建立出來的對象,其屬性的值是否必定要和原型對象屬性的值徹底同樣,這個並無強制規定,只不過在目前大多數實現中,克隆出來的對象和原型對象的屬性值是同樣的

   也就是說,能夠經過克隆來創造值不同的實例,可是對象類型必須同樣.能夠有部分甚至是所有的屬性的值不同,能夠有選擇性地克隆,就當是標準原型模式的一個變形使用吧

Q:什麼時候使用原型模式

A:  1.若是一個系統想要獨立於它想要使用的對象時,能夠使用原型模式,讓系統只面向接口編程,在系統須要新的對象的時候,能夠經過克隆原型來獲得.

     2.若是須要實例化的類是在運行時刻動態指定時,能夠使用原型模式,經過克隆原型來獲得須要的實例

    9.3.7 相關模式

1.原型模式和抽象工廠模式

  功能上類似,都是用來獲取一個新的對象實例的

  不一樣之處在於,原型模式的着眼點是在如何創造出實例對象來,最後選擇的方案是經過克隆;而抽象工廠模式的着眼點則在於如何創造產品簇,至於具體如何建立出產品簇中的每一個對象實例,抽象工廠模式則不是很關注

  正式由於它們的關注點不同,因此它們也能夠配合使用,好比在抽象工廠模式裏面,具體建立每一種產品的時候就能夠使用該產品的原型,也就是抽象工廠管產品簇,具體的每種產品怎麼建立則能夠選擇原型模式

2.原型模式和生成器模式

  這兩種模式能夠配合使用

  生成器模式關注的是構建的過程,而在構建的過程當中,極可能須要某個部件的實例,那麼很天然地就能夠應用上原型模式,經過原型模式來獲得部件的實例

第10章 中介者模式(Mediator)

  10.1 場景問題

    10.1.1 若是沒有主板

    10.1.2 有何問題

Q:有何問題

A:若是上面的狀況發生在軟件開發上呢?

   若把每一個電腦配件都抽象成爲一個類或者是子系統,那就至關於出現了多個類之間相互交互,並且交互很繁瑣,致使每一個類都必須知道全部須要交互的類,也就是咱們常說的類和類耦合了.

   在軟件開發中出現這種狀況可就不秒了,不但開發的時候每一個類會複雜,由於要兼顧其餘的類,更要命的是每一個類在發生改動的時候,須要通知全部相關的類一塊兒修改,由於接口或者是功能發生了變更,使用它的地方都得變

   那該如何來簡化這種多個對象之間的交互呢?

    10.1.3 使用電腦來看電影

  10.2 解決方案

    10.2.1 使用中介者模式來解決問題

Q:中介者模式的定義

A:用一箇中介對象來封裝一系列的對象交互.中介者使得各對象不須要顯式地相互引用,從而使其耦合鬆散,並且能夠獨立地改變它們之間的交互

Q:應用中介者模式來解決問題的思路

A:中介者模式的解決思路很簡單,跟電腦的例子同樣,中介者模式經過引入一箇中介對象,讓其餘的對象都只和中介對象交互,而中介對象知道如何和其餘全部的對象交互,這樣對象之間的交互關係就沒有了,從而實現了對象之間的解耦

   對於中介對象而言,全部相互交互的對象,被視爲同事類,中介對象就是來維護各個同事之間的關係,而全部的同事類都只是和中介對象交互.

   每一個同事對象,當本身發生變化的時候,不須要知道這會引發其餘對象有什麼變化,它只須要通知中介者就能夠了,而後由中介者去與其餘對象交互.這樣鬆散耦合帶來的好處是,除了讓同事對象之間相互沒有關聯外,還有利於功能的修改和擴展.

   有了箇中介者以後,全部的交互都封裝到中介者對象裏面,各個對象就再也不須要維護這些關係了.擴展關係的時候也只須要擴展或修改中介者對象就能夠了.

    10.2.2 中介者模式的結構和說明

  [Mediator] 中介者接口.在裏面定義各個同事之間交互須要的方法,能夠是公共的通訊方法,好比changed方法,你們都用,也能夠是小範圍的交互方法

  [ConcreteMediator] 具體中介者實現對象,它須要瞭解並維護各個同事對象,並負責具體的協調各同事對象的交互關係

  [Colleague] 同事類的定義,一般實現成爲抽象類,主要負責約束同事對象的類型,並實現一些具體同事類之間的公共功能.好比,每一個具體同事類都應該知道中介者對象,也就是具體同事類都會持有中介者對象,均可以定義到這個類裏面

  [ConcreteColleague] 具體的同事類,實現本身的業務,在須要與其餘同事通訊的時候,就與持有的中介者通訊,中介者會負責與其餘的同事交互

    10.2.3 中介者模式示例代碼

using System;
using System.Collections.Generic;


namespace ConsoleApplication1 {
    public class Program {
        public static void Main(string[] args) {

        }
        
    
    }

    public abstract class Colleague {
        private Mediator mediator;

        public Colleague(Mediator mediator) {
            this.mediator = mediator;
        }

        public Mediator getMediator() {
            return mediator;
        }
    }

    public class ConcreteColleagueA : Colleague {
        public ConcreteColleagueA(Mediator mediator) : base(mediator) {
            
        }

        public void someOperation() {
            getMediator().changed(this);
        }
    }

    public class ConcreteColleagueB : Colleague {
        public ConcreteColleagueB(Mediator mediator) : base(mediator) {
            
        }

        public void someOperation() {
            getMediator().changed(this);
        }
    }

    public interface Mediator {
        void changed(Colleague colleague);
    }


    public class ConcreteMediator : Mediator {
        private ConcreteColleagueA colleagueA;
        private ConcreteColleagueB colleagueB;

        public void setConcreteColleagueA(ConcreteColleagueA colleagueA) {
            this.colleagueA = colleagueA;
        }

        public void setConcreteColleagueB(ConcreteColleagueB colleagueB) {
            this.colleagueB = colleagueB;
        }
        
        public void changed(Colleague colleague) {
            
        }
    }
}
View Code

    10.2.4 使用中介者模式來實現示例

using System;

namespace ConsoleApplication1 {
    public class Program {
        public static void Main(string[] args) {
            MotherBoard mediator = new MotherBoard();

            CDDriver cd = new CDDriver(mediator);
            CPU cpu = new CPU(mediator);
            VideoCard vc = new VideoCard(mediator);
            SoundCard sc = new SoundCard(mediator);

            mediator.setCdDriver(cd);
            mediator.setCpu(cpu);
            mediator.setVideoCard(vc);
            mediator.setSoundCard(sc);
            
            cd.readCD();
        }
    }


    public abstract class Colleague {
        private Mediator mediator;

        public Colleague(Mediator mediator) {
            this.mediator = mediator;
        }

        public Mediator getMediator() {
            return mediator;
        }
    }

    public class CDDriver : Colleague {
        private string data = "";
        
        public CDDriver(Mediator mediator) : base(mediator) {
            
        }

        public string getData() {
            return this.data;
        }

        public void readCD() {
            this.data = "設計模式,值得好好研究";
            this.getMediator().changed(this);
        }
    }

    public class CPU : Colleague {
        private string videoData = "";
        private string soundData = "";
        
        public CPU(Mediator mediator) : base(mediator) {
            
        }

        public string getVideoData() {
            return videoData;
        }

        public string getSoundData() {
            return soundData;
        }

        public void executeData(string data) {
            string[] ss = data.Split(',');
            this.videoData = ss[0];
            this.soundData = ss[1];
            this.getMediator().changed(this);
        }
    }

    public class VideoCard : Colleague {
        public VideoCard(Mediator mediator) : base(mediator) {
            
        }

        public void showData(string data) {
            Console.WriteLine("您正觀看的是: " + data);
        }
    }

    public class SoundCard : Colleague {
        public SoundCard(Mediator mediator) : base(mediator) {
            
        }

        public void soundData(string data) {
            Console.WriteLine("畫外音: " + data);
        }
    }

    public interface Mediator {
        void changed(Colleague colleague);
    }

    public class MotherBoard : Mediator {
        private CDDriver cdDriver = null;
        private CPU cpu = null;
        private VideoCard videoCard = null;
        private SoundCard soundCard = null;

        public void setCdDriver(CDDriver cdDriver) {
            this.cdDriver = cdDriver;
        }

        public void setCpu(CPU cpu) {
            this.cpu = cpu;
        }

        public void setVideoCard(VideoCard videoCard) {
            this.videoCard = videoCard;
        }

        public void setSoundCard(SoundCard soundCard) {
            this.soundCard = soundCard;
        }

        public void changed(Colleague colleague) {
            if (colleague == cdDriver) {
                this.openCDDriverReadData((CDDriver)colleague);
            } else if (colleague == cpu) {
                this.openCPU((CPU)colleague);
            }
        }

        private void openCDDriverReadData(CDDriver cd) {
            string data = cd.getData();
            this.cpu.executeData(data);
        }

        private void openCPU(CPU cpu) {
            string videoData = cpu.getVideoData();
            string soundData = cpu.getSoundData();

            this.videoCard.showData(videoData);
            this.soundCard.soundData(soundData);
        }
    }
}
View Code

  10.3 模式講解

    10.3.1 認識中介者模式

Q:中介者模式的功能

A:中介者的功能很是簡單,就是封裝對象之間的交互.若是一個對象的操做會引發其餘相關對象的變化,或者是某個操做須要引發其餘對象的後續或連帶操做,而這個對象又不但願本身來處理這些關係,那麼就能夠找中介者,把全部的麻煩扔給它,只在須要的時候通知中介者,其餘的就讓中介者去處理就能夠了

   反過來,其餘的對象在操做的時候,可能會引發這個對象的變化,也能夠這麼作.最後對象之間就徹底分離了,誰都不直接跟其餘對象交互,那麼相互的關係所有被集中到中介者對象裏面了,全部的對象就只是跟中介者對象進行通訊,相互之間再也不有聯繫.

   把全部對象之間的交互都封裝在中介者當中,無形中還能夠獲得另一個好處,就是可以集中地控制這些對象的交互關係,這樣當有變化的時候,修改起來就很方便.

Q:須要Mediator接口嗎

A:取決因而否會提供多個不一樣的中介者實現.

Q:同事關係

A:在標準的中介者模式中,將使用中介者對象來交互的那些對象稱爲同事類.

Q:同事和中介者的關係

A:在中介者模式中,當一個同事對象發生了改變,須要主動通知中介者,讓中介者去處理與其餘同事對象相關的交互

   這就致使了同事對象和中介者對象之間必須有關係,首先是同事對象須要知道中介者對象是誰;反過來,中介者對象也須要知道相關的同事對象,這樣它才能與同事對象進行交互.也就是說中介者對象和同事對象之間是互相依賴的.

Q:如何實現同事和中介者的通訊

A:一個同事對象產生了改變,會通知中介者對象,中介者對象會處理與其餘同事的交互,這就產生了同事對象和中介者對象的相互通訊.怎麼實現這種通訊關係呢?

   一種實現方式是在Mediator接口中定義一個特殊的通知接口,做爲一個通用的方法,讓各個同事類來調用這個方法,在中介者模式結構圖裏畫的就是這種方式.在前面實例的也是這種方式,定義了一個通用的changed方法,而且把同事對象看成參數傳入,這樣在中介者對象裏面,就能夠去獲取這個同事對象的實例的數據了.

  另一種實現方式是能夠採用觀察者模式,把Mediator實現稱爲觀察者,而各個同事類實現成爲Subject,這樣同事類發生了改變,會通知Mediator.Mediator在接到通知之後,會與相應的同事對象進行交互

    10.3.2 廣義中介者

仔細查看中介者的結構,定義和示例,會發現幾個問題,使得中介者模式在實際使用的時候,變得繁瑣或困難

1.是否有必要爲同事對象定義一個公共的父類?

  你們都知道,JAVA/C#是單繼承的,爲了使用中介者模式,就讓這些同事對象繼承一個父類,這是很很差的;再說了,這個父類目前也沒有什麼特別的功能,也就是說繼承它也得不到多少好處

  在實際開發中,不少相互交互的對象自己是沒有公共父類的,強行加上一個父類,會讓這些對象實現起來特別彆扭

2.同事類有必要持有中介者對象嗎?

  同事類須要知道中介者對象,以便當它們發生改變的時候可以通知中介者對象.可是是否須要做爲屬性並經過構造方法傳入這麼強的依賴關係呢?

  也能夠用簡單的方式去通知中介對象,好比把中介對象作成單例,直接在同事類的方法裏面去調用中介者對象.

3.是否須要中介者接口?

  在實際開發中,很常見的狀況是不須要中介者接口的,並且中介者對象也不須要建立不少個實例.由於中介者是用來封裝和處理同事對象的關係的,它通常是沒有狀態須要維護的,所以中介者一般能夠實現成單例

4.中介者對象是否須要持有全部的同事?

  雖然說中介者對象須要知道全部的同事類,這樣中介者才能與它們交互.可是是否須要做爲屬性這麼強烈的依賴關係,並且中介者對象在不一樣的關係維護上,可能會須要不一樣的同事對象的實例,所以能夠在中介者處理的方法裏面去建立,或者獲取,或者從參數傳入須要的同事對象

5.中介者對象只是提供一個公共的方法來接受同事對象的通知嗎?

  從示例中能夠看出來,在公共方法裏,仍是要去區分究竟是誰調過來,這仍是簡單的,尚未去區分究竟是什麼樣的業務觸發調用過來的,由於不一樣的業務,引發的與其餘對象的交互是不同的.

  所以在實際開發中,一般會提供具體的業務通知方法,這樣就不用再去判斷究竟是什麼對象,具體是什麼業務了

基於上面的考慮,在實際應用開發中,常常會簡化中介者模式,來使得開發變得簡單,好比有以下的簡化

  1.一般會去掉同事對象的父類,這樣可讓任意的對象,只要須要相互交互,就能夠成爲同事

  2.一般不定義Mediator接口,把具體的中介者對象實現成爲單例

  3.同事對象再也不持有中介者,而是在須要的時候直接獲取中介者對象並調用;中介者也再也不持有同事對象,而是在具體處理方法裏面去建立,或者獲取,或者從參數傳入須要的同事對象

把這樣通過簡化,變形使用的狀況稱爲廣義中介者

例子:

using System;
using System.Collections.Generic;

namespace ConsoleApplication1 {
    public class Program {
        public static void Main(string[] args) {
            DepUserMediatorImpl mediator = DepUserMediatorImpl.getInstance();
            
            Dep dep = new Dep();
            dep.setDepId("d1");
            Dep dep2 = new Dep();
            dep2.setDepId("d2");
            
            User user = new User();
            user.setUserId("u1");

            Console.WriteLine("撤銷部門前------");
            mediator.showUserDeps(user);

            dep.deleteDep();
            Console.WriteLine("撤銷部門後------");
            mediator.showUserDeps(user);
            
            Console.WriteLine("人員離職前------");
            mediator.showDepUsers(dep2);
            user.dismission();
            Console.WriteLine("人員離職後------");
            mediator.showDepUsers(dep2);
        }
    }

    public class Dep {
        private string depId;
        private string depName;

        public string getDepId() {
            return depId;
        }

        public void setDepId(string depId) {
            this.depId = depId;
        }

        public string getDepName() {
            return depName;
        }

        public void setDepName(string depName) {
            this.depName = depName;
        }

        public bool deleteDep() {
            DepUserMediatorImpl mediator = DepUserMediatorImpl.getInstance();
            mediator.deleteDep(depId);

            return true;
        }
    }

    public class User {
        private string userId;
        private string userName;

        public string getUserId() {
            return userId;
        }

        public void setUserId(string userId) {
            this.userId = userId;
        }

        public string getUserName() {
            return userName;
        }

        public void setUserName(string userName) {
            this.userName = userName;
        }

        public bool dismission() {
            DepUserMediatorImpl mediator = DepUserMediatorImpl.getInstance();
            mediator.deleteUser(userId);

            return true;
        }
    }

    public class DepUserModel {
        private string depUserId;
        private string depId;
        private string userId;

        public string getDepUserId() {
            return depUserId;
        }

        public void setDepUserId(string depUserId) {
            this.depUserId = depUserId;
        }

        public string getDepId() {
            return depId;
        }

        public void setDepId(string depId) {
            this.depId = depId;
        }

        public string getUserId() {
            return userId;
        }

        public void setUserId(string userId) {
            this.userId = userId;
        }
    }

    public class DepUserMediatorImpl {
        private static DepUserMediatorImpl mediator = new DepUserMediatorImpl();

        private List<DepUserModel> depUserCol = new List<DepUserModel>();

        private DepUserMediatorImpl() {
            initTestData();
        }

        public static DepUserMediatorImpl getInstance() {
            return mediator;
        }

        private void initTestData() {
            DepUserModel du1 = new DepUserModel();
            du1.setDepUserId("du1");
            du1.setDepId("d1");
            du1.setUserId("u1");
            depUserCol.Add(du1);
            
            
            DepUserModel du2 = new DepUserModel();
            du2.setDepUserId("du2");
            du2.setDepId("d1");
            du2.setUserId("u2");
            depUserCol.Add(du2);
            
            DepUserModel du3 = new DepUserModel();
            du3.setDepUserId("du3");
            du3.setDepId("d2");
            du3.setUserId("u3");
            depUserCol.Add(du3);
            
            DepUserModel du4 = new DepUserModel();
            du4.setDepUserId("du4");
            du4.setDepId("d2");
            du4.setUserId("u4");
            depUserCol.Add(du4);
            
            DepUserModel du5 = new DepUserModel();
            du5.setDepUserId("du5");
            du5.setDepId("d2");
            du5.setUserId("u1");
            depUserCol.Add(du5);
        }

        public bool deleteDep(string depId) {
            depUserCol.RemoveAll((depUserModel)=> {
                if (depUserModel.getDepId().Equals(depId)) {
                    return true;
                } else {
                    return false;
                }
            });

            return true;
        }

        public bool deleteUser(string userId) {
            depUserCol.RemoveAll((depUserModel)=> {
                if (depUserModel.getDepId().Equals(userId)) {
                    return true;
                } else {
                    return false;
                }
            });

            return true;
        }

        public void showDepUsers(Dep dep) {
            foreach (DepUserModel depUserModel in depUserCol) {
                if (depUserModel.getDepId().Equals(dep.getDepId())) {
                    Console.WriteLine("部門編號 = " + dep.getDepId() + "下面擁有人員,其編號是: " + depUserModel.getUserId());
                }
            }
        }

        public void showUserDeps(User user) {
            foreach (DepUserModel depUserModel in depUserCol) {
                if (depUserModel.getUserId().Equals(user.getUserId())) {
                    Console.WriteLine("人員編號 = " + user.getUserId() + "屬於部門編號是: " + depUserModel.getDepId());
                }
            }
        }

        public bool changeDep(string userId, string oldDepId, string newDepId) {
            return false;
        }

        public bool joinDep(List<string> colDepIds, Dep newDep) {
            return false;
        }
    }
}
View Code

    10.3.3 中介者模式的優缺點

優勢

  1.鬆散耦合

    中介者模式經過把多個同事對象之間的交互封裝到中介者對象裏面,從而使得同事對象之間鬆散耦合,基本上能夠作到互不依賴,這樣一來,同事對象就能夠獨立地變化和複用,而再也不像之前那樣"牽一髮而動全身"了

  2.集中控制交互

    多個同事對象的交互,被封裝在中介者對象裏面集中管理,使得這些交互行爲發生變化的時候,只須要修改中介者對象就能夠了,固然若是是已經作好的系統,那就擴展中介者對象,而各個同事類不須要作修改

  3.多對多變成一對多

    沒有使用中介者模式的時候,同事對象之間的關係一般是多對多的,引入中介者對象之後,中介者對象和同事對象的關係一般變成了雙向的一對多,這會讓對象的關係更容易理解和實現

缺點

  1.中介者模式的一個潛在缺點是,過渡集中化.若是同事對象的交互很是多,並且比較複雜,當這些複雜性所有集中到中介者的時候,會致使中介者對象變得十分複雜,並且難於管理和維護

    10.3.4 思考中介者模式

Q:中介者模式的阿本質

A:封裝交互

   中介者模式的目的,就是用來封裝多個對象的交互,這些交互的處理多在中介者對象裏面實現.所以中介對象的複雜程度,就取決於它封裝的交互的複雜程度

   只要是實現封裝對象之間的交互功能,就能夠應用中介者模式,而沒必要過於拘泥於中介者模式自己的結構.標準的中介者模式限制不少,致使能徹底按照標準使用中介者模式的地方並非不少,並且多集中在界面實現上.只要本質不變,稍稍變形一下,簡化一下,或許能更好地使用中介者模式

Q:什麼時候選用中介者模式

A:  1.若是一組對象之間的通訊方式比較複雜,致使相互依賴,結構混亂,能夠採用中介者模式,把這些對象相互的交互管理起來,各個對象都只須要和中介者交互,從而使得各個對象鬆散耦合,結構也更清晰易懂.

   2.若是一個對象引用不少的對象,並直接跟這些對象交互,致使難以複用該對象,能夠採用中介者模式,把這個對象跟其餘對象的交互封裝到中介者對象裏面,這個對象只須要和中介者對象交互就能夠了.

    10.3.5 相關模式

1.中介者模式和外觀模式

  這兩個模式有類似的地方,也存在很大的不一樣

  外觀模式多用來封裝一個子系統內部的多個模塊,目的是向子系統外部提供簡單易用的接口.也就是說外觀模式封裝的是子系統外部和子系統內部模塊間的交互;而中介者模式是提供多個平等的同事對象之間的交互關係的封裝,通常是用在內部實現上

  另外,外觀模式是實現單向的交互,是從子系統外部來調用子系統內部,不會反着來,而中介者模式實現的是內部多個模塊間的多向的交互

2.中介者模式和觀察者模式

  這兩個模式能夠組合使用

  中介者模式能夠組合使用觀察者模式,來實現當同事對象發生改變的時候,通知中介對象,讓中介對象去進行與其餘相關對象的交互

第11章 代理模式(Proxy)

  11.1 場景問題

    11.1.1 訪問多條數據

    11.1.2 不用模式的解決方案

using System;
using System.Collections.Generic;

namespace Test2 {
    class Program {
        static void Main(string[] args) {
            UserManager userManager = new UserManager();
            List<UserModel> col = userManager.getUserByDepId("0101");
            Console.WriteLine(col);
        }                        
    }

    public class UserModel {
        private string userId;
        private string name;
        private string depId;
        private string sex;

        public string getUserId() {
            return userId;
        }

        public void setUserId(string userId) {
            this.userId = userId;
        }

        public string getName() {
            return name;
        }

        public void setName(string name) {
            this.name = name;
        }

        public string getDepId() {
            return depId;
        }

        public void setDepId(string depId) {
            this.depId = depId;
        }

        public string getSex() {
            return sex;
        }

        public void setSex(string sex) {
            this.sex = sex;
        }

        public override string ToString() {
            return "userId = " + userId + ", name = " + name + ", depId = " + depId + ", sex = " + sex + "\n";
        }          
    }

    public class UserManager {
        public List<UserModel> getUserByDepId(string depId) {
            List<UserModel> col = new List<UserModel>();
            Connection conn = null;
            conn = this.getConnection();
            string sql = "select * from tbl_user u,tbl_dep d where u.depId = d.depId and d.depId like ?";
            PreparedStatement pstmt = conn.prepareStatement(sql);
            pstmt.setString(1, depId + "%");

            ResultSet rs = pstmt.executeQuery();
            while (rs.next()) {
                UserModel um = new UserModel();
                um.setUserId(rs.getString("userId"));
                um.setName(rs.getString("name"));
                um.setDepId(rs.getString("depId"));
                um.setSex(rs.getString("sex"));
                
                col.Add(um);
            }

            rs.close();
            pstmt.close();
        }

        private Connection getConnection() {
            return DriverManager.getConnection("鏈接數據庫的URL", "用戶名", "密碼");
        }
    }
}
View Code

    11.1.3 有何問題

Q:有何問題

A:上面的實現看起來很簡單,功能也正確,可是蘊含一個較大的問題.那就是,當一次性訪問的數據條數過多,並且每條描述的數據量又很大的話,將會消耗較多的內存.

   前面也說i了,對於用戶表,其實是不少字段的,不只僅是示例的幾個,再加上不使用翻頁,一次性訪問的數據就可能會有不少條.若是一次性須要訪問的數據較多,內存開銷將會比較大

   可是從客戶使用的角度來講i,有很大的隨機性.客戶有可能訪問每一條數據,也有可能一條都不訪問.也就是說,一次性訪問不少條數據,消耗了大量內存,可是極可能是浪費掉了,客戶根本就不會去訪問那麼多數據,對於每條數據,客戶只須要看看姓名而已.

    那麼該怎麼實現,才能既能把多條用戶數據的姓名顯示出來,而又能節省內存空間?固然還要實如今客戶想要看到更多數據的時候,能正確訪問到數據呢

  11.2 解決方案

    11.2.1 使用代理模式來解決問題

Q:代理模式的定義

A:爲其餘對象提供一種代理以控制對這個對象的訪問.

    11.2.2 代理模式的結構和說明

  [Proxy] 代理對象,一般具備以下功能.實現與具體的對象同樣的接口,這樣就能夠使用代理來代替具體的目標對象.保存一個指向具體目標對象的引用,能夠在須要的時候調用具體的目標對象.能夠控制對具體目標對象的訪問,並能夠負責建立和刪除它

  [Subject] 目標接口,定義代理和具體目標對象的接口,這樣就能夠在任何使用具體目標對象的地方使用代理對象

  [RealSubject] 具體的目標對象,真正實現目標接口要求的功能

    11.2.3 代理模式示例代碼

using System;
using System.Collections.Generic;

namespace Test2 {
    class Program {
        static void Main(string[] args) {
            
        }                        
    }


    public interface Subject {
        void request();
    }

    public class RealSubject : Subject {
        public void request() {
            
        }
    }

    public class Proxy : Subject {
        private RealSubject realSubject = null;

        public Proxy(RealSubject realSubject) {
            this.realSubject = realSubject;
        }

        public void request() {
            realSubject.request();
        }
    }
}
View Code

    11.2.4 使用代理模式重寫示例

using System;
using System.Collections.Generic;

namespace Test2 {
    class Program {
        static void Main(string[] args) {
            UserManager userManager = new UserManager();
            List<UserModelApi> col = userManager.getUserByDepId("0101");
            foreach (UserModelApi userModelApi in col) {
                Console.WriteLine("用戶編號: = " + userModelApi.getUserId() + ",用戶姓名: = " + userModelApi.getName());
            }

            foreach (UserModelApi userModelApi in col) {
                Console.WriteLine("用戶編號: = " + userModelApi.getUserId() + ",用戶姓名: = " + userModelApi.getName() +
                                  ",所屬部門: = " + userModelApi.getDepId());
            }
        }                                    
    }

    public interface UserModelApi {
        string getUserId();
        void setUserId(string userId);
        string getName();
        void setName(string name);
        string getDepId();
        void setDepId(string depId);
        string getSex();
        void setSex(string sex);
    }
    
    public class UserModel : UserModelApi {
        private string userId;
        private string name;
        private string depId;
        private string sex;

        public string getUserId() {
            return userId;
        }

        public void setUserId(string userId) {
            this.userId = userId;
        }

        public string getName() {
            return name;
        }

        public void setName(string name) {
            this.name = name;
        }

        public string getDepId() {
            return depId;
        }

        public void setDepId(string depId) {
            this.depId = depId;
        }

        public string getSex() {
            return sex;
        }

        public void setSex(string sex) {
            this.sex = sex;
        }

        public override string ToString() {
            return "userId = " + userId + ", name = " + name + ", depId = " + depId + ", sex = " + sex + "\n";
        }          
    }

    public class Proxy : UserModelApi {
        private bool loaded = false;
        
        private UserModel realSubject = null;

        public Proxy(UserModel realSubject) {
            this.realSubject = realSubject;
        }

        public string getUserId() {
            return realSubject.getUserId();
        }

        public void setUserId(string userId) {
            realSubject.setUserId(userId);
        }

        public string getName() {
            return realSubject.getName();
        }

        public void setName(string userName) {
            realSubject.setName(userName);
        }

        public string getDepId() {
            if (!this.loaded) {
                reload();
                this.loaded = true;
            }

            return realSubject.getDepId();
        }

        public void setDepId(string depId) {
            realSubject.setDepId(depId);
        }

        public string getSex() {
            if (!this.loaded) {
                reload();
                this.loaded = true;
            }

            return realSubject.getSex();
        }

        private void reload() {
            Console.WriteLine("從新查詢數據庫獲取完整的用戶數據,userId = " + realSubject.getUserId());

            Connection conn = null;
            conn = this.getConnection();
            string sql = "select * from tbl_user where userId = ?";
            PreparedStatement pstmt = conn.prepareStatement(sql);
            pstmt.setString(1, realSubject.getUserId());
            ResultSet rs = pstmt.executeQuery();
            if (rs.next()) {
                realSubject.setDepId(rs.getString("depId"));
                realSubject.setSex(rs.getString("sex"));
            }

            rs.close();
            pstmt.close();
        }

        private Connection getConnection() {
            return DriverManager.getConnection("鏈接數據庫的ULR", "用戶名", "密碼");
        }
        
        public override string ToString() {
            return "userId = " + getUserId() + ", name = " + getName() + ", depId = " + getDepId() + ", sex = " + getSex() + "\n";
        }        
        
    }
    
    public class UserManager {
        public List<UserModelApi> getUserByDepId(string depId) {
            List<UserModelApi> col = new List<UserModelApi>();
            Connection conn = null;
            conn = this.getConnection();
            string sql = "select u.userId,u.name from tbl_user u,tbl_dep d where u.depId = d.depId and d.depId like ?";
            PreparedStatement pstmt = conn.prepareStatement(sql);
            pstmt.setString(1, depId + "%");

            ResultSet rs = pstmt.executeQuery();
            while (rs.next()) {
                Proxy proxy = new Proxy(new UserModel());
                proxy.setUserId(rs.getString("userId"));
                proxy.setName(rs.getString("name"));

                col.Add(proxy);
            }

            rs.close();
            pstmt.close();
        }

        private Connection getConnection() {
            return DriverManager.getConnection("鏈接數據庫的URL", "用戶名", "密碼");
        }
    }
    
    
}
View Code

  11.3 模式講解

    11.3.1 認識代理模式

Q:代理模式的功能

A:代理模式是經過建立一個代理對象,用這個代理對象去表明真實的對象,客戶端獲得這個代理對象後,對客戶端並無什麼影響,就跟獲得了真實對象同樣來使用

   當客戶端操做這個代理對象的時候,實際上功能最終仍是會由真實的對象來完成.只不過是經過代理操做的,也就是客戶端操做代理,代理操做真正的對象.

   正是由於由代理對象夾在客戶端和被代理的真實對象中間,至關於一箇中轉,那麼在中轉的時候就有不少花招能夠玩,好比,判斷一下權限,若是沒有足夠的權限那就不給你中轉,等等

Q:代理的分類

A:  1:虛代理:根據須要來建立開銷很大的對象,該對象只有在須要的時候纔會被真正建立.

     2:用來在不一樣的地址空間上表明同一個對象,這個不一樣的地址空間能夠是在本機,也能夠在其餘機器上.

     3:copy-on-write代理:在客戶端操做的時候,只有對象確實改變了,纔會真的拷貝(或克隆)一個目標對象,算是虛代理的一個分支

     4:保護代理:控制對原始對象的訪問,若是有須要,能夠給不一樣的用戶提供不一樣的訪問權限,以控制他們對原始對象的訪問.

     5:Cache代理:爲那些昂貴操做的結果提供臨時的存儲空間,以便多個客戶端能夠共享這些結果

     6:防火牆代理:保護對象不被惡意用戶訪問和操做

     7:同步代理:使多個用戶可以同時訪問目標對象而沒有衝突

     8:智能指引:在訪問對象時執行一些復加操做,好比,對指向實際對象的引用計數,第一次引用一個持久對象時,將它裝入內存等.

    11.3.2 保護代理

using System;

namespace Test2 {
    class Program {
        static void Main(string[] args) {
            OrderApi order = new OrderProxy(new Order("設計模式",100,"張三"));
            order.setOrderNum(123,"李四");
            Console.WriteLine("李四修改後訂單記錄沒有變化:" + order);
            order.setOrderNum(123,"張三");
            Console.WriteLine("張三修改後,訂單記錄: " + order);
        }                                    
    }


    public interface OrderApi {
        string getProductName();
        void setProductName(string productName, string user);
        int getOrderNum();
        void setOrderNum(int orderNum, string user);
        string getOrderUser();
        void setOrderUser(string orderUser, string user);
    }

    public class Order : OrderApi {
        private string productName;
        private int orderNum;
        private string orderUser;

        public Order(string productName, int orderNum, string orderUser) {
            this.productName = productName;
            this.orderNum = orderNum;
            this.orderUser = orderUser;
        }

        public string getProductName() {
            return productName;
        }

        public void setProductName(string productName, string user) {
            this.productName = productName;
        }

        public int getOrderNum() {
            return orderNum;
        }

        public void setOrderNum(int orderNum, string user) {
            this.orderNum = orderNum;
        }

        public string getOrderUser() {
            return orderUser;
        }

        public void setOrderUser(string orderUser, string user) {
            this.orderUser = orderUser;
        }
    }

    public class OrderProxy : OrderApi {
        private Order order = null;

        public OrderProxy(Order realSubject) {
            this.order = realSubject;
        }

        public void setProductName(string productName, string user) {
            if (user != null && user.Equals(this.getOrderUser())) {
                order.setProductName(productName, user);
            } else {
                Console.WriteLine("對不起" + user + ",您無權修改訂單中的產品名稱.");  
            }
        }
        
        public void setOrderNum(int orderNum, string user) {
            if (user != null && user.Equals(this.getOrderUser())) {
                order.setOrderNum(orderNum, user);
            } else {
                Console.WriteLine("對不起" + user + ",您無權修改訂單中的產品名稱.");  
            }
        }

        public void setOrderUser(string orderUser, string user) {
            if (user != null && user.Equals(this.getOrderUser())) {
                order.setOrderUser(orderUser, user);
            } else {
                Console.WriteLine("對不起" + user + ",您無權修改訂單中的產品名稱.");  
            }
        }
        
        public int getOrderNum() {
            return this.order.getOrderNum();
        }

        public string getOrderUser() {
            return this.order.getOrderUser();
        }

        public string getProductName() {
            return this.order.getProductName();
        }
        
        public override string ToString() {
            return "productName = " + this.getProductName() + ", orderNum = " + this.getOrderNum() + ", orderUser = " +
                   this.getOrderUser();
        }
    }
    
}
View Code

    11.3.3 Java中的代理

    11.3.4 代理模式的特色

    11.3.5 思考代理模式

Q:代理模式的本質

A:控制對象訪問

   代理模式經過代理目標對象,把代理對象插入到客戶和目標對象之間,從而爲客戶和目標對象引入必定的間接性.正是這個間接性,給了代理對象不少的活動空間.代理對象能夠在調用具體的目標對象先後,附加不少操做,從而實現新的功能或是擴展目標對象的功能.更狠的是,代理對象還能夠不去建立和調用目標對象,也就是說,目標對象被徹底代理掉了,或是被替換掉了.

Q:什麼時候選用代理模式

A:  1.須要爲一個對象在不一樣的地址空間提供局部表明的時候,就能夠使用遠程代理\

     2.須要按照須要建立開銷很大的對象的時候,能夠使用虛代理

     3.須要控制對原始對象的訪問的時候,能夠使用保護代理

     4.須要在訪問對象執行一些附加操做的時候,能夠使用智能指引代理.

    11.3.6 相關模式

1.代理模式和適配器模式

  這兩個模式有必定的類似性,但也有差別

  這兩個模式有類似性,它們都爲另外一個對象提供間接性的訪問,並且都是從自身之外的一個接口向這個對象轉發請求

  可是從功能上,兩個模式是不同的.適配器模式主要用來解決接口之間不匹配的問題,它一般是爲所適配的對象提供一個不一樣的接口;而代理模式會實現和目標對象相同的接口

2.代理模式和裝飾模式

  這兩個模式從實現上類似,可是功能上是不一樣的.

  裝飾模式的實現和保護代理的實現上是相似的,都是在轉調其餘對象的先後執行必定的功能.可是它們的目的和功能都是不一樣的

  裝飾模式的目的是爲了讓你不生成子類就能夠給對象添加職責,也就是爲了動態地增長功能;而代理模式的主要目的是控制對對象的訪問

第12章 觀察者模式(Observer)

  12.1 場景問題

    12.1.1 訂閱報紙的過程

    12.1.2 訂閱報紙的問題

 

  12.2 解決方案

    12.2.1 使用觀察者模式來解決問題

Q:觀察者者模式的定義

A:定義對象間的一種一對多的依賴關係.當一個對象的狀態發生改變時,全部依賴於它的對象都獲得通知並被自動更新

Q:應用觀察者模式來解決的思路

A:在前面描述的訂閱報紙的例子裏面,對於報社來碩,在一開始,它並不清楚究竟有多少個訂閱者會來訂閱報紙,所以,報社須要維護一個訂閱者的列表,這樣,當報社出版報紙的時候,纔可以把報紙發送到全部的訂閱者手裏.對於訂閱者來講,訂閱者也就是看報的讀者,多個訂閱者會訂閱同一份報紙
   這就出現了一個典型的一對多的對象關係,一個報紙對象,會有多個訂閱者對象來訂閱,當報紙出版的時候,也就是報紙對象改變的時候,須要通知全部的訂閱者對象.那麼怎麼來創建並維護這樣的關係呢?
   觀察者模式能夠處理這種問題,觀察者模式把這多個訂閱者稱爲觀察者:Observer,多個觀察者觀察的對象被稱爲目標:Subject
   一個目標能夠有任意多個觀察者對象,一旦目標的狀態發生了改變,全部註冊的觀察者都會獲得通知,而後各個觀察者會對通知作出相應的響應,執行相應的業務功能處理,並使本身的狀態和目標對象的狀態保持一致.

    12.2.2 觀察者模式的結構和說明

  [Subject] 目標對象,一般具備以下功能

    1.一個目標能夠被多個觀察者觀察

    2.目標提供對觀察者註冊和退訂的維護

    3.當目標的狀態發生變化時,目標負責通知全部註冊的,有效的觀察者

  [Observer] 定義觀察者的接口,提供目標通知時對應的更新方法,這個更新方法進行相應的業務處理.能夠在這個方法裏面回調目標對象,以獲取目標對象的數據

  [ConcreteSubject] 具體的目標實現對象,用來維護目標狀態,當目標對象的狀態發生改變時,通知全部註冊的,有效的觀察者,讓觀察者執行相應的處理

  [ConcreteObserver] 觀察者的具體實現對象,用來接收目標的通知,並進行相應的後續處理,好比更新自身的狀態以保持和目標的相應狀態一致

    12.2.3 觀察者模式示例代碼

using System;
using System.Collections.Generic;

namespace Test2 {
    class Program {
        static void Main(string[] args) {

        }                                    
    }

    public class Subject {
        private List<Observer> observers = new List<Observer>();

        public void attach(Observer observer) {
            observers.Add(observer);
        }

        public void detach(Observer observer) {
            observers.Remove(observer);
        }

        protected void notifyObservers() {
            foreach (Observer observer in observers) {
                
            }
        }
    }

    public class ConcreteSubject : Subject {
        private string subjectState;

        public string getSubjectState() {
            return subjectState;
        }

        public void setSubjectState(string subjectState) {
            this.subjectState = subjectState;
            this.notifyObservers();
        }
    }

    public interface Observer {
        void update(Subject subject);
    }

    public class ConcreteObserver : Observer {
        private string observerState;

        public void update(Subject subject) {
            observerState = ((ConcreteSubject) subject).getSubjectState();
        }
    }
}
View Code

    12.2.4 使用觀察者模式實現示例

using System;
using System.Collections.Generic;

namespace ConsoleApplication1 {
    public class Program {
        public static void Main(string[] args) {
            NewsPaper subject = new NewsPaper();
            
            Reader reader1 = new Reader();
            reader1.setName("張三");
            
            Reader reader2 = new Reader();
            reader2.setName("李四");
            
            Reader reader3 = new Reader();
            reader3.setName("王五");
            
            
            
            subject.attach(reader1);
            subject.attach(reader2);
            subject.attach(reader3);
            
            subject.setContent("本期內容是觀察者模式");

        }
    }

    public class Subject {
        private List<Observer> readers = new List<Observer>();

        public void attach(Observer reader) {
            readers.Add(reader);
        }

        public void detach(Observer reader) {
            readers.Remove(reader);
        }

        protected void notifyObservers() {
            foreach (Observer observer in readers) {
                observer.update(this);
            }
        }
    }

    public interface Observer {
        void update(Subject subject);
    }

    public class NewsPaper : Subject {
        private string content;

        public string getContent() {
            return content;
        }

        public void setContent(string content) {
            this.content = content;
            notifyObservers();
        }
    }

    public class Reader : Observer {
        private string name;

        public void update(Subject subject) {
            Console.WriteLine(name + "收到報紙了,閱讀它.內容是 === " + ((NewsPaper)subject).getContent());
        }

        public string getName() {
            return name;
        }

        public void setName(string name) {
            this.name = name;
        }
    }
}
View Code

  12.3 模式講解

    12.3.1 認識觀察者模式

Q:目標和觀察者之間的關係

A:按照模式的定義,目標和觀察者之間是典型的一對多的關係

   可是要注意,若是觀察者只有一個,也是能夠的,這樣就變相實現了目標和觀察者之間一對一的關係,這也使得在處理一個對象的狀態變化會影響到另外一個對象的時候,也能夠考慮使用觀察者模式

   一樣地,一個觀察者也能夠觀察多個目標,若是觀察者爲多個目標定義的通知更新方法都是update方法的話,這會帶來麻煩,由於須要接收多個目標的通知,若是是一個update的方法,那就須要在方法內部區分,到底這個更新的通知來自於哪個目標,不一樣的目標有不一樣的後續操做

   通常狀況下,觀察者應該爲不一樣的觀察者定義不一樣的回調方法,這樣實現最簡單,不須要在update方法內部進行區分

Q:單向依賴

A:在觀察者模式中,觀察者和目標是單向依賴的,只有觀察者依賴於目標,而目標是不會依賴於觀察者的

   它們之間聯繫的主動權掌握在目標手中,只有目標知道何時須要通知觀察者.在整個過程當中,觀察者始終是被動的,被動地等待目標的通知,等待目標傳值給它

   對目標而言,全部的觀察者都是同樣的,目標會一視同仁地對待,固然也能夠經過在目標中進行控制,實現有區別地對待觀察者,好比某些狀態變化,只須要通知部分觀察者,但那是屬於稍微變形的用法了,不屬於標準的,原始的觀察者模式了.

    12.3.2 推模式和拉模型

1.推模型

  目標對象主動向觀察者推送目標的詳細信息,無論觀察者是否須要,推送的信息一般是目標對象的所有或部分數據,至關因而在廣播通訊

2.拉模型

  目標對象在通知觀察者的時候,只傳遞少許信息.若是觀察者須要更具體的信息,由觀察者主動到目標對象中獲取,至關因而觀察者從目標對象中拉數據.通常這種模型的實現中,會把目標對象自身經過update方法傳遞給觀察者,這樣在觀察者須要獲取數據的時候,就能夠經過這個引用來獲取了(前面的例子就是典型的拉模型)

推模型例子

using System;
using System.Collections.Generic;

namespace Test2 {
    class Program {
        static void Main(string[] args) {

            
        }                                    
    }

    public interface Observer {
        void update(string content);
    }
    

    public class Reader : Observer {
        private string name;

        public string getName() {
            return name;
        }

        public void setName(string name) {
            this.name = name;
        }
        
        public void update(string content) {
            Console.WriteLine(name + "收到報紙了,閱讀它.內容是 === " + content);
        }
    }

    public class Subject {
        private List<Observer> readers = new List<Observer>();

        public void attach(Observer reader) {
            readers.Add(reader);
        }

        public void detach(Observer reader) {
            readers.Remove(reader);
        }

        protected void notifyObservers(string content) {
            foreach (Observer observer in readers) {
                observer.update(content);
            }
        }
    }

    public class NewsPaper : Subject {
        private string content;

        public string getContent() {
            return content;
        }

        public void setContent(string content) {
            this.content = content;
            notifyObservers(content);
        }
    }

}
View Code

兩種模型的比較

  1.推模型是假定目標對象知道觀察者須要的數據,而拉模型是目標對象不知道觀察者具體須要什麼數據,沒有辦法的狀況下,乾脆把自身傳給觀察者,讓觀察者本身去按需取值

  2.推模型可能會使得觀察者對象難以複用,由於觀察者定義的update方法是按需而定義的,可能沒法兼顧沒有考慮到的使用狀況.這就意味着出現新的狀況的時候,就可能須要提供新的update方法,或者是乾脆從新實現觀察者

  而拉模型就不會形成這樣的狀況,由於拉模型下,update方法的參數是目標對象自己,這基本上是目標對象能傳遞的最大數據集合了,基本上能夠適應各類狀況的須要

    12.3.3 Java中的觀察者模式

    12.3.4 觀察者模式的優缺點

優勢:

  1.觀察者模式實現了觀察者和目標之間的抽象耦合

    本來目標對象在狀態發生改變的時候,須要直接調用全部的觀察者對象,可是抽象出觀察者接口之後,目標和觀察者就只是在抽象層面上耦合了,也就是說目標只是知道觀察者接口,並不知道具體的觀察者的類,從而實現目標類和具體的觀察者類之間解耦

  2.觀察者模式實現了動態聯動

    所謂聯動,就是作一個操做會引發其餘相關的操做.因爲觀察者模式對觀察者註冊實行管理,那就能夠在運行期間,經過動態地控制註冊的觀察者,來控制某個動做的聯動範圍,從而實現動態聯動

  3.觀察者模式支持廣播通訊

缺點:

  1.可能會引發無謂的操做

    因爲觀察者模式每次都是廣播通訊,無論觀察者需不須要,每一個觀察者都會調用update方法,若是觀察者不須要執行相應處理,那麼此次操縱就浪費了.其實浪費了還好,最怕引發誤更新,那就麻煩了,好比,本應該在執行此次狀態更新前把某個觀察者刪除掉,這樣通知的時候就沒有這個觀察者了,可是如今忘掉了,那麼就會引發誤操做

    12.3.5 思考觀察者模式

Q:觀察者模式的本質

A:觸發聯動

   當修改目標對象的狀態時,就會觸發相應的通知,而後會循環調用全部註冊的觀察者對象的相應方法,其實就至關於聯動調用這些觀察者的方法

   並且這個聯動仍是動態的,能夠經過註冊和取消註冊來控制觀察者,於是能夠在程序運行期間,經過動態地控制觀察者,來變相地實現添加和刪除某些功能處理,這些功能j就是觀察者在update的時候執行的功能

   同時目標對象和觀察者對象的解耦,又保證了不管觀察者發生怎樣的變化,目標對象老是可以正確地聯動起來

   理解這個本質對咱們很是有用,對於咱們識別和使用觀察者模式有很是重要的意義,尤爲是在變形使用的時候.萬變不離其宗

Q:什麼時候選用觀察者模式

  1.當一個抽象模型有兩個方面,其中一個方面的操做依賴於另外一個方面的狀態變化,那麼就能夠選用觀察者模式,將這二者封裝成觀察者和目標對象,當目標對象變化的時候,依賴於它的觀察者對象也會發生相應的變化.這樣就把抽象模型的這兩個方面分離開了,使得它們能夠獨立地改變和複用。

  2.若是在更改一個對象的時候,須要同時連帶改變其餘的對象,並且不知道究竟應該有多少對象須要被連帶改變,這種狀況能夠選用觀察者模式,被更改的那一個對象很明顯就至關因而目標對象,而須要連帶修改的多個其餘對象,就做爲多個觀察者對象了.

  3.當一個對象必須通知其餘的對象,可是你又但願這個對象和其餘被通知的對象是鬆散耦合的.也就是說這個對象其實不想知道具體被通知的對象.這種狀況能夠選用觀察者模式,這個對象就至關因而目標對象,而被它通知的對象就是觀察者對象了.

    12.3.6 Swing中的觀察者

    12.3.7 簡單變形示例-區別對待觀察者

using System;
using System.Collections.Generic;

namespace Test2 {
    class Program {
        static void Main(string[] args) {
            WaterQulity subject = new WaterQulity();
            WatcherObserver watcher1 = new Watcher();
            watcher1.setJob("監測人員");
            WatcherObserver watcher2 = new Watcher();
            watcher2.setJob("預警人員");
            WatcherObserver watcher3 = new Watcher();
            watcher3.setJob("監測部門領導");

            subject.attach(watcher1);
            subject.attach(watcher2);
            subject.attach(watcher3);
            
            Console.WriteLine("當水質爲正常的時候------");
            subject.setPolluteLevel(0);
            Console.WriteLine("當水質爲輕度污染的時候------");
            subject.setPolluteLevel(1);
            Console.WriteLine("當水質爲中度污染的時候------");
            subject.setPolluteLevel(2);
        }                                    
    }

    public abstract class WaterQualitySubject {
        protected List<WatcherObserver> observers = new List<WatcherObserver>();

        public void attach(WatcherObserver observer) {
            observers.Add(observer);
        }

        public void detach(WatcherObserver observer) {
            observers.Remove(observer);
        }

        public abstract void notifyWatchers();
        public abstract int getPolluteLevel();
    }

    public interface WatcherObserver {
        void update(WaterQualitySubject subject);
        void setJob(string job);
        string getJob();
    }

    public class WaterQulity : WaterQualitySubject {
        private int polluteLevel = 0;

        public override int getPolluteLevel() {
            return polluteLevel;
        }

        public void setPolluteLevel(int polluteLevel) {
            this.polluteLevel = polluteLevel;
            this.notifyWatchers();
        }

        public override void notifyWatchers() {
            foreach (WatcherObserver watcherObserver in observers) {
                if (this.polluteLevel >= 0) {
                    if ("監測人員".Equals(watcherObserver.getJob())) {
                        watcherObserver.update(this);
                    }
                }

                if (this.polluteLevel >= 1) {
                    if ("預警人員".Equals(watcherObserver.getJob())) {
                        watcherObserver.update(this);
                    }
                }

                if (this.polluteLevel >= 2) {
                    if ("監測部門領導".Equals(watcherObserver.getJob())) {
                        watcherObserver.update(this);
                    }
                }
            }
        }
    }

    public class Watcher : WatcherObserver {
        private string job;

        public string getJob() {
            return job;
        }

        public void setJob(string job) {
            this.job = job;
        }

        public void update(WaterQualitySubject subject) {
            Console.WriteLine(job + "獲取到通知,當前污染級別爲: " + subject.getPolluteLevel());
        }
    }
}
View Code

    12.3.8 相關模式

1.觀察者模式和狀態模式

  觀察者模式和狀態模式是有類似之處的.

  觀察者模式是當目標狀態發生改變時,觸發並通知觀察者,讓觀察者去執行相應的操做.而狀態模式是根據不一樣的狀態,選擇不一樣的實現,這個實現類的主要功能就是針對狀態相應地操做,它不像觀察者,觀察者自己還有不少其餘的功能,接收通知並執行相應處理只是觀察者的部分功能.
  固然觀察者模式和狀態模式是能夠結合使用的.觀察者模式的重心在觸發聯動,可是到底決定哪些觀察者會被聯動,這時就能夠採用狀態模式來實現了,也能夠採用策略模式來進行選擇須要聯動的觀察者

2.觀察者模式和中介者模式

  觀察者模式和中介者模式是能夠結合使用的

  前面的例子中目標都只是簡單地通知一下,而後讓各個觀察者本身去完成更新就結束了.若是觀察者和被觀察的目標之間的交互很複雜,好比,有一個界面,裏面有三個下拉列表組件,分別是選擇國家,省份/州,具體的城市,很明顯這是一個三級聯動,當你選擇一個國家的時候,省份/州應該相應改變數據,省份/州一改變,具體的城市也須要改變.
  這種狀況下,很明顯須要相關的狀態都聯動準備好了,而後再一次性地通知觀察者.也就是界面作更新處理,不會僅國家改變一下,省份和城市尚未改,就通知界面更新.這種狀況就能夠使用中介者模式來封裝觀察者和目標的關係.
  在使用Swing的小型應用裏面,也能夠使用中介者模式,好比,把一個界面全部的事件用一個對象來處理,把一個組件觸發事件之後,須要操做其餘組件的動做都封裝在一塊兒,這個對象就是典型的中介者

第13章 命令模式(Command)

  13.1 場景問題

    13.1.1 如何開機

    13.1.2 與我何干

    13.1.3 有何問題

  13.2 解決方案

    13.2.1 使用命令模式來解決問題

Q:命令模式的定義

A:將一個請求封裝爲一個對象,從而使你可用不一樣的請求對客戶進行參數化,對請求排隊或記錄請求日誌,以及支持可撤銷的操做

    13.2.2 命令模式的結構和說明

  [Command] 定義命令的接口,聲明執行的方法

  [ConcreteCommand] 命令接口實現對象,是"虛"的實現;一般會持有接收者,並調用接收者的功能來完成命令要執行的操做

  [Receiver] 接收者,真正執行命令的對象,任何類均可能成爲一個接收者,只要它可以實現命令要求實現的相應功能

  [Invoker] 要求命令對象執行請求,一般會持有命令對象,能夠持有不少的命令對象.這個是客戶端真正觸發命令並要求命令執行相應操做的地方,也就是說至關於使用命令對象的入口

  [Client] 建立具體的命令對象,而且設置命令對象的接收者.注意這個不是咱們常規意義上的客戶端,而是在組裝命令對象和接收者,或許,把這個Client成爲裝配者會更好理解,由於真正使用命令的客戶端是從Invoker來觸發執行

    13.2.3 命令模式示例代碼

using System;
using System.Collections.Generic;

namespace Test2 {
    class Program {
        static void Main(string[] args) {
            Receiver receiver = new Receiver();
            Command command = new ConcreteCommand(receiver);
            Invoker invoker = new Invoker();
            invoker.setCommand(command);
            invoker.runCommand();
        }                                    
    }

    public interface Command {
        void execute();
    }

    public class ConcreteCommand : Command {
        private Receiver receiver = null;
        private string state;

        public ConcreteCommand(Receiver receiver) {
            this.receiver = receiver;
        }

        public void execute() {
            receiver.action();
        }
    }

    public class Receiver {
        public void action() {
            Console.WriteLine("action");
        }
    }

    public class Invoker {
        private Command command = null;

        public void setCommand(Command command) {
            this.command = command;
        }

        public void runCommand() {
            command.execute();
        }
    }
}
View Code

    13.2.4 使用命令模式來實現示例

using System;
using System.Collections.Generic;

namespace Test2 {
    class Program {
        static void Main(string[] args) {
            MainBoardApi mainBoard = new GigaMainBoard();
            OpenCommand openCommand = new OpenCommand(mainBoard);
            Box box = new Box();
            box.setCommand(openCommand);
            box.openButtonPressed();
        }                                    
    }

    // Receiver
    public interface MainBoardApi {
        void open();
    }

    public class GigaMainBoard : MainBoardApi {
        public void open() {
            Console.WriteLine("技嘉主板如今正在開機,請等候");
            Console.WriteLine("接通電源......");
            Console.WriteLine("設備檢查......");
            Console.WriteLine("裝載系統......");
            Console.WriteLine("機器正常運轉起來......");
            Console.WriteLine("機器已經正常打開,請操做");
        }
    }

    // Command
    public interface Command {
        void execute();
    }

    public class OpenCommand : Command {
        private MainBoardApi mainBoard = null;

        public OpenCommand(MainBoardApi mainBoard) {
            this.mainBoard = mainBoard;
        }

        public void execute() {
            mainBoard.open();
        }
    }

    // Invoker
    public class Box {
        private Command openCommand;

        public void setCommand(Command command) {
            this.openCommand = command;
        }

        public void openButtonPressed() {
            openCommand.execute();
        } 
    }
}
View Code

  13.3 模式講解

    13.3.1 認識命令模式

Q:命令模式的關鍵

A:命令模式的關鍵之處就是把請求封裝成對象,也就是命令對象,並定義了統一的執行操做的接口,這個命令對象能夠被存儲,轉發,記錄,處理,撤銷等,整個命令模式都是圍繞整個對象在進行

Q:命令模式的組裝和調用

A:在命令模式中常常會有一個命令的組裝者,用它來維護命令的"虛"實現和真實實現之間的關係.若是是超級智能的命令,也就是說命令對象本身徹底實現好了,不須要接收者,那就是命令模式的退化,不須要接收者,天然也不須要組裝者了.
   而真正的用戶就是具體化請求的內容,而後提交請求進行觸發就能夠了.真正的用戶會經過Invoker來觸發命令
   在實際開發過程當中,Client和Invoker能夠融合在一塊兒,由客戶在使用命令模式的時候,先進行命令對象和接收者的組裝,組裝完成後,就能夠調用命令執行請求

Q:命令模式的接收者

A:接收者能夠是任意的類,對它沒有什麼特殊要求,這個對象知道如何真正執行命令的操做,執行時是從Command的實現l類裏面轉調過來

   一個接收者對象能夠處理多個命令,接收者和命令之間沒有約定的對應關係.接收者提供的方法個數,名稱,功能和命令中的能夠不同,只要可以經過調用接收者的方法來實現命令對應的功能就能夠了.

Q:智能命令

A:在標準的命令模式裏面,命令的實現類是沒有真正實現命令要求的功能的,真正執行命令的功能的是接收者

   若是命令的實現對象比較智能,它本身就能真實地實現命令要求的功能,而再也不須要調用接收者,那麼這種狀況就稱爲智能命令

   也能夠有半智能的命令,命令對象知道部分實現,其餘的仍是須要調用接收者來完成,也就是說命令的功能由命令對象和接收者功能來完成

Q:發起請求的對象和真正實現的對象是解耦的

A:請求究竟由誰處理?如何處理?發起請求的對象是不知道的,也就是發起請求的對象和真正實現的對象是解耦的.發起請求的對象只管發出命令,其餘的就無論了

    13.3.2 參數化配置

Q:命令模式的參數化配置

A:能夠用不一樣的命令對象,去參數化配置客戶的請求

using System;
using System.Collections.Generic;

namespace Test2 {
    class Program {
        static void Main(string[] args) {
            MainBoardApi mainBoard = new GigaMainBoard();
            OpenCommand openCommand = new OpenCommand(mainBoard);
            ResetCommand resetCommand = new ResetCommand(mainBoard);
            
            Box box = new Box();
            box.setOpenCommand(openCommand);
            box.setResetCommand(resetCommand);
            
            box.openButtonPressed();
            box.resetButtonPressed();
        }                                    
    }

    // Receiver
    public interface MainBoardApi {
        void open();
        void reset();
    }

    public class GigaMainBoard : MainBoardApi {
        public void open() {
            Console.WriteLine("技嘉主板如今正在開機,請等候");
            Console.WriteLine("接通電源......");
            Console.WriteLine("設備檢查......");
            Console.WriteLine("裝載系統......");
            Console.WriteLine("機器正常運轉起來......");
            Console.WriteLine("機器已經正常打開,請操做");
        }

        public void reset() {
            Console.WriteLine("技嘉主板如今正在從新啓動機器,請等候");
            Console.WriteLine("機器已經正常打開,請操做");
        }
    }

    // Command
    public interface Command {
        void execute();
    }

    public class OpenCommand : Command {
        private MainBoardApi mainBoard = null;

        public OpenCommand(MainBoardApi mainBoard) {
            this.mainBoard = mainBoard;
        }

        public void execute() {
            mainBoard.open();
        }
    }

    public class ResetCommand : Command {
        private MainBoardApi mainBoard = null;

        public ResetCommand(MainBoardApi mainBoard) {
            this.mainBoard = mainBoard;
        }

        public void execute() {
            this.mainBoard.reset();
        }
    }

    // Invoker
    public class Box {
        private Command openCommand;
        private Command resetCommand;

        public void setOpenCommand(Command command) {
            this.openCommand = command;
        }

        public void setResetCommand(Command command) {
            this.resetCommand = command;
        }

        public void openButtonPressed() {
            openCommand.execute();
        }

        public void resetButtonPressed() {
            resetCommand.execute();
        }
    }
}
View Code

    13.3.3 可撤銷的操做

Q:可撤銷的操做

A:可撤銷操做的意思是:放棄該操做,回到未執行該操做前的狀態

   有兩種基本的思路來實現可撤銷的操做:

  1.補償式,又稱反操做式,好比被撤銷的操做是加的功能,那撤銷的實現就變成減的功能;同理被撤銷的操做是打開的功能,那麼撤銷的實現就變成關閉的功能

  2.存儲恢復式,意思就是把操做前的狀態記錄下來,而後要撤銷操做的時候就直接恢復回去就能夠了(放在備忘錄模式中講解)

using System;
using System.Collections.Generic;

namespace Test2 {
    class Program {
        static void Main(string[] args) {
            OperationApi operation = new Operation();
            
            AddCommand addCmd = new AddCommand(operation,5);
            SubstractCommand substractCommand = new SubstractCommand(operation,3);
            
            Calculator calculator = new Calculator();
            calculator.setAddCmd(addCmd);
            calculator.setSubstractCmd(substractCommand);
            
            Console.WriteLine("補償式(反操做式)");
            
            calculator.addPressed();
            Console.WriteLine("一次加法運算後的結果: " + operation.getResult());
            calculator.substractPressed();
            Console.WriteLine("一次減法運算後的結果: " + operation.getResult());
            
            calculator.undoPressed();
            Console.WriteLine("撤銷一次後的結果爲: " + operation.getResult());
            
            calculator.undoPressed();
            Console.WriteLine("再撤銷一次後的結果爲: " + operation.getResult());
            
            calculator.redoPressed();
            Console.WriteLine("恢復操做一次後的結果爲: " + operation.getResult());
            
            calculator.redoPressed();
            Console.WriteLine("再恢復操做一次後的結果爲: " + operation.getResult());
        }                                    
    }

    public interface OperationApi {
        int getResult();
        void setResult(int result);
        void add(int num);
        void substract(int num);
    }

    public class Operation : OperationApi {
        private int result;

        public int getResult() {
            return result;
        }

        public void setResult(int result) {
            this.result = result;
        }

        public void add(int num) {
            result += num;
        }

        public void substract(int num) {
            result -= num;
        }
    }

    public interface Command {
        void execute();
        void undo();
    }

    public class AddCommand : Command {
        private OperationApi operation = null;
        private int operNum;
        
        public AddCommand(OperationApi operation, int operNum) {
            this.operation = operation;
            this.operNum = operNum;
        }

        public void execute() {
            this.operation.add(operNum);
        }

        public void undo() {
            this.operation.substract(operNum);
        }
    }

    public class SubstractCommand : Command {
        private OperationApi operation = null;
        private int operNum;

        public SubstractCommand(OperationApi operation, int operNum) {
            this.operation = operation;
            this.operNum = operNum;
        }

        public void execute() {
            this.operation.substract(operNum);
        }

        public void undo() {
            this.operation.add(operNum);
        }
    }

    public class Calculator {
        private Command addCmd = null;
        private Command substractCmd = null;
        
        private List<Command> undoCmds = new List<Command>();
        private List<Command> redoCmds = new List<Command>();

        public void setAddCmd(Command command) {
            this.addCmd = command;
        }

        public void setSubstractCmd(Command command) {
            this.substractCmd = command;
        }

        public void addPressed() {
            this.addCmd.execute();
            undoCmds.Add(addCmd);
        }

        public void substractPressed() {
            this.substractCmd.execute();
            undoCmds.Add(substractCmd);
        }

        public void undoPressed() {
            if (undoCmds.Count > 0) {
                Command cmd = undoCmds[undoCmds.Count - 1];
                cmd.undo();
                this.redoCmds.Add(cmd);
                undoCmds.Remove(cmd);
                
            } else {
                Console.WriteLine("很抱歉,沒有可撤銷的命令");
            }
        }

        public void redoPressed() {
            if (redoCmds.Count > 0) {
                Command cmd = redoCmds[redoCmds.Count - 1];
                cmd.execute();
                undoCmds.Add(cmd);
                redoCmds.Remove(cmd);
            } else {
                Console.WriteLine("很抱歉,沒有可恢復的命令");
            }
        }
    }
}
View Code

    13.3.4 宏命令

Q:什麼是宏命令

A:簡單點說就是包含多個命令的命令,是一個命令的組合

using System;
using System.Collections.Generic;

namespace Test2 {
    class Program {
        static void Main(string[] args) {
            Waiter waiter = new Waiter();
            
            Command chop = new ChopCommand();
            Command duck = new DuckCommand();
            Command meat = new CoolMeatCommand();
            
            waiter.orderDish(chop);
            waiter.orderDish(duck);
            waiter.orderDish(meat);
            
            waiter.orderOver();
        }                                    
    }

    public interface CookApi {
        void cook(string name);
    }

    public class HotCook : CookApi {
        public void cook(string name) {
            Console.WriteLine("本廚師正在作: " + name);
        }
    }

    public class CoolCook : CookApi {
        public void cook(string name) {
            Console.WriteLine("涼菜" + name + "已經作好,本廚師正在裝盤");
        }
    }

    public interface Command {
        void execute();
    }

    public class ChopCommand : Command {
        private CookApi cookApi = null;

        public void setCookApi(CookApi cookApi) {
            this.cookApi = cookApi;
        }

        public void execute() {
            this.cookApi.cook("綠豆排骨煲");
        }
    }

    public class DuckCommand : Command {
        private CookApi cookApi = null;

        public void setCookApi(CookApi cookApi) {
            this.cookApi = cookApi;
        }

        public void execute() {
            this.cookApi.cook("北京烤鴨");
        }
    }

    public class CoolMeatCommand : Command {
        private CookApi cookAPi = null;

        public void setCookAPi(CookApi cookApi) {
            this.cookAPi = cookApi;
        }

        public void execute() {
            this.cookAPi.cook("蒜泥白肉");
        }
    }

    // 宏命令對象
    public class MenuCommand : Command {
        private List<Command> col = new List<Command>();

        public void addCommand(Command command) {
            col.Add(command);
        }

        public void execute() {
            foreach (Command command in col) {
                command.execute();
            }
        }
    }

    // 負責組合菜單,負責組裝每一個菜單和具體的實現者,還負責執行調用,至關於標準Command模式的Client+Invoker
    public class Waiter {
        private MenuCommand menuCommand = new MenuCommand();

        public void orderDish(Command cmd) {
            CookApi hotCook = new HotCook();
            CookApi coolCook = new CoolCook();

            if (cmd is DuckCommand) {
                ((DuckCommand) cmd).setCookApi(hotCook);
            } else if (cmd is ChopCommand) {
                ((ChopCommand) cmd).setCookApi(hotCook);
            } else if (cmd is CoolMeatCommand) {
                ((CoolMeatCommand) cmd).setCookAPi(coolCook);
            }
            
            menuCommand.addCommand(cmd);
        }

        public void orderOver() {
            menuCommand.execute();
        }
    }
}
View Code

    13.3.5 隊列請求

    13.3.6 日誌請求

    13.3.7 命令模式的優勢

    13.3.8 思考命令模式

Q:命令模式的本質

A:封裝請求

   前面講了,命令模式的關鍵就是把請求封裝成爲命令對象,而後就能夠對這個對象進行一系列的處理了,不如上面講到的參數化配置,可撤銷操做,宏命令,隊列請求,日誌請求等功能處理.

Q:什麼時候選用命令模式

A:  1.若是須要抽象出須要執行的動做,並參數化這些對象,能夠選用命令模式,將這些須要執行的動做抽象成爲命令,而後實現命令的參數化配置

   2.若是須要在不一樣的時刻指定,排列和執行請求,能夠選用命令模式.將這些請求封裝成爲命令對象,而後實現將請求隊列化

   3.若是須要支持取消操做,能夠選用命令模式,經過管理命令對象,能很容易地實現命令的恢復和重作功能

   4.若是須要支持當系統崩潰時,能將系統的操做功能從新執行一遍,能夠選用命令模式,將這些操做功能的請求封裝成命令對象,而後實現日誌命令,就能夠在系統恢復之後,經過日誌獲取命令列表,從而從新執行一遍功能.

   5.在須要事務的系統中,能夠選用命令模式,命令模式提供了對事務進行建模的方法,命令模式有一個別名就是Transaction

    13.3.9 退化的命令模式

using System;
using System.Collections.Generic;

namespace Test2 {
    class Program {
        static void Main(string[] args) {
            Command cmd = new PrintService("退化的命令模式示例");
            
            Invoker invoker = new Invoker();
            invoker.setCmd(cmd);
            
            invoker.startPrint(cmd);
        }                            
    }

    public interface Command {
        void execute();
    }

    public class PrintService : Command {
        private string str = "";

        public PrintService(string s) {
            str = s;
        }

        public void execute() {
            // 智能的體現,本身知道怎麼實現命令所要求的功能,並真正地實現了相應的功能,再也不轉調接收者了
            Console.WriteLine("打印的內容爲 = " + str);
        }
    }

    public class Invoker {
        private Command cmd = null;

        public void setCmd(Command cmd) {
            this.cmd = cmd;
        }

        public void startPrint() {
            this.cmd.execute();
        }

        public void startPrint(Command cmd) {
            Console.WriteLine("在Invoker中,輸出服務前");
            cmd.execute();
            Console.WriteLine("輸出服務結束");
        }
    }
    
    
}
View Code

    13.3.10 相關模式

1.命令模式和組合模式

  這兩個模式能夠組合使用

  在命令模式中,實現宏命令的功能就能夠使用組合模式來實現.前面的示例並無按照組合模式來作,那是爲了保持示例的簡單,還有突出命令模式的實現,這點請注意

2.命令模式和備忘錄模式

  這兩個模式能夠組合使用

  在命令模式中,實現可撤銷操做功能時,前面講了有兩種實現方式,其中有一種就是保存命令執行前的狀態,撤銷的時候就把狀態恢復.若是採用這種方式實現,就能夠考慮使用備忘錄模式

  若是狀態存儲在命令對象中,那麼還能夠使用原型模式,把命令對象看成原型來克隆一個新的對象,而後將克隆出來的對象經過備忘錄模式存放

3.命令模式和模板方法

  這兩個模式從某種意義上有類似的功能,命令模式能夠做爲模板方法的一種替代模式,也就是說命令模式能夠模仿實現模板方法模式的功能

  如同前面講述的退化的命令模式能夠實現Java的回調,而Invoker智能化後向服務進化,若是Invoker的方法就是一個算法骨架,其中有兩步在這個骨架裏面沒有具體實現,須要外部來實現,這個時候就能夠經過回調命令接口來實現.

  而相似的功能在模板方法中,是先調用抽象方法,而後等待子類來實現.能夠看出雖然實現方式不同,可是能夠實現相同的功能.

第14章 迭代器模式(Iterator)

  14.1 場景問題

    14.1.1 工資表數據的集合

    14.1.2 有何問題.

Q:有何問題

A:如何可以以一個統一的方式來訪問內部實現不一樣的聚合對象

  14.2 解決方案

    14.2.1 使用迭代器模式解決問題

Q:迭代器模式的定義

A:提供一種方法順序訪問一個聚合對象中的各個元素,而又不需暴露該對象的內部表示

所謂聚合是指一組對象的組合結構

Q:應用迭代器模式來解決問題的思路

A:仔細分析上面的問題,要以一個統一的方式來訪問內部實現不一樣的聚合對象,那麼首先須要把這個統一的訪問方式定義出來,按照這個統一的訪問方式定義出來的接口,在迭代器模式中對應的就是Iterator接口

   迭代器迭代的是具體的聚合對象,那麼不一樣的聚合對象就應該有不一樣的迭代器,爲了讓迭代器以一個統一的方式來操做聚合對象,所以給全部的聚合對象抽象出一個公共的父類,讓它提供操做聚合對象的公共接口,這個抽象的公共父類在迭代器模式中對應的就是Aggregate對象

    14.2.2 迭代器模式的結構和說明

  [Iterator] 迭代器接口,定義訪問和遍歷元素的接口

  [ConcreteIterator] 具體的迭代器實現對象,實現對聚合對象的遍歷,並跟蹤遍歷時的當前位置

  [Aggregate] 聚合對象.定義建立相應迭代器對象的接口

  [ConcreteAggregate] 具體聚合對象.實現建立相應的迭代器對象

    14.2.3 迭代器模式示例代碼

using System;
using System.Collections.Generic;

namespace ConsoleApplication1 {
    public class Program {
        public static void Main() {
            string[] names = {"1", "2", "3"};
            
            Aggregate aggregate = new ConcreteAggregate(names);
            Iterator it = aggregate.createIterator();
            
            it.first();
            while (!it.isDone()) {
                Object obj = it.currentItem();
                Console.WriteLine("the object = " + obj);
                it.next();
            }
        }

    }

    public interface Iterator {
        void first();
        void next();
        bool isDone();
        object currentItem();
    }

    public abstract class Aggregate {
        public abstract Iterator createIterator();
    }

    public class ConcreteIterator : Iterator {
        private ConcreteAggregate aggregate;
        private int index = -1;

        public ConcreteIterator(ConcreteAggregate aggregate) {
            this.aggregate = aggregate;
        }

        public void first() {
            index = 0;
        }

        public void next() {
            if (index < aggregate.size()) {
                index = index + 1;
            }
        }

        public bool isDone() {
            if (index == aggregate.size()) {
                return true;
            } else {
                return false;
            }
        }

        public object currentItem() {
            return aggregate.get(index);
        }
    }

    public class ConcreteAggregate : Aggregate {

        private string[] ss = null;

        public ConcreteAggregate(string[] ss) {
            this.ss = ss;
        }

        public override Iterator createIterator() {
            return new ConcreteIterator(this);
        }

        public Object get(int index) {
            Object obj = null;
            if (index < ss.Length) {
                obj = ss[index];
            }

            return obj;
        }

        public int size() {
            return this.ss.Length;
        }
    }

}
View Code

    14.2.4 使用迭代器模式來實現示例

 

using System;
using System.Collections;
using System.Collections.Generic;

namespace Test2 {
    class Program {
        static void Main(string[] args) {
            PayManager payManager = new PayManager();
            payManager.calcPay();
            Console.WriteLine("集團工資列表: ");
            test(payManager.createIterator());
            
            SalaryManager salaryManager = new SalaryManager();
            salaryManager.calcSalary();
            Console.WriteLine("新收購的公司工資列表: ");
            test(salaryManager.createIterator());            
        }

        private static void test(Iterator it) {
            it.first();
            while (!it.isDone()) {
                Object obj = it.currentItem();
                Console.WriteLine("the obj == " + obj);
                it.next();
            }
        }
    }

    public interface Iterator {
        void first();
        void next();
        bool isDone();
        object currentItem();
    }

    public abstract class Aggregate {
        public abstract Iterator createIterator();
    }

    public class ArrayIteratorImpl : Iterator {
        private SalaryManager aggregate = null;
        private int index = -1;

        public ArrayIteratorImpl(SalaryManager aggregate) {
            this.aggregate = aggregate;
        }

        public void first() {
            index = 0;
        }

        public void next() {
            if (index < aggregate.size()) {
                index = index + 1;
            }
        }

        public bool isDone() {
            if (index == aggregate.size()) {
                return true;
            }

            return false;
        }

        public object currentItem() {
            return aggregate.get(index);
        }
    }

    public class CollectionIteratorImpl : Iterator {
        private PayManager aggregate = null;
        private int index = -1;

        public CollectionIteratorImpl(PayManager aggregate) {
            this.aggregate = aggregate;
        }

        public void first() {
            index = 0;
        }

        public void next() {
            if (index < aggregate.size()) {
                index = index + 1;
            }
        }

        public bool isDone() {
            if (index == aggregate.size()) {
                return true;
            }

            return false;
        }

        public object currentItem() {
            return aggregate.get(index);
        }
    }

    public class PayModel {
        private string userName;
        private double pay;

        public string getUserName() {
            return userName;
        }

        public void setUserName(string userName) {
            this.userName = userName;
        }

        public double getPay() {
            return pay;
        }

        public void setPay(double pay) {
            this.pay = pay;
        }

        public override string ToString() {
            return "userName = " + userName + ",pay = " + pay;
        }
    }

    public class PayManager : Aggregate {
        private ArrayList list = new ArrayList();
        
        
        public override Iterator createIterator() {
            return new CollectionIteratorImpl(this);
        }

        public Object get(int index) {
            Object obj = null;
            if (index < this.list.Count) {
                obj = list[index];
            }

            return obj;
        }

        public int size() {
            return list.Count;
        }
        

        public ArrayList getPayList() {
            return list;
        }

        public void calcPay() {
            PayModel pm1 = new PayModel();
            pm1.setPay(3800);
            pm1.setUserName("張三");
            
            PayModel pm2 = new PayModel();
            pm2.setPay(5800);
            pm2.setUserName("李四");

            list.Add(pm1);
            list.Add(pm2);
        }
    }

    public class SalaryManager : Aggregate {
        private PayModel[] pms = null;

        public override Iterator createIterator() {
            return new ArrayIteratorImpl(this);
        }

        public object get(int index) {
            Object obj = null;
            if (index < pms.Length) {
                obj = pms[index];
            }

            return obj;
        }

        public int size() {
            return pms.Length;
        }
        
        public PayModel[] getPays() {
            return pms;
        }

        public void calcSalary() {
            PayModel pm1 = new PayModel();
            pm1.setPay(2200);
            pm1.setUserName("王五");
            
            PayModel pm2 = new PayModel();
            pm2.setPay(3600);
            pm2.setUserName("趙六");

            pms = new PayModel[2];
            pms[0] = pm1;
            pms[1] = pm2;
        }
    }
}
View Code

  14.3 模式講解

    14.3.1 認識迭代器模式

Q:迭代器模式的功能

A:迭代器模式的功能主要在於提供對聚合對象的迭代訪問.迭代器就圍繞着這個"訪問"作文章,延伸出不少的功能來,好比:

  1.以不一樣的方式遍歷聚合對象,好比向前,向後等.

  2.對同一個聚合同時進行多個遍歷

  3.以不一樣的遍歷策略來遍歷聚合,好比是否須要過濾等

  4.多態迭代,含義是:爲不一樣的聚合結構提供統一的迭代接口,也就是說經過一個迭代接口能夠訪問不一樣的聚合結構,這就叫作多態迭代.上面的示例就已經實現了多態迭代.事實上,標準的迭代模式實現基本上都是支持多態迭代的.

Q:迭代器模式的關鍵思想

A:迭代器模式的關鍵思想就是把對聚合對象的遍歷和訪問從聚合對象中分離出來,放入單獨的迭代器中,這樣聚合對象會變得簡單一些;並且迭代器和聚合對象能夠獨立地變化和發展,會大大增強系統的靈活性.

Q:內部迭代器和外部迭代器

A:  所謂內部迭代器,指的是由迭代器本身來控制迭代下一個元素的步驟,客戶端沒法干預

     所謂外部迭代器,指的是由客戶端來控制迭代下一個元素的步驟

    14.3.2 使用Java的迭代器

    14.3.3 帶迭代策略的迭代器

    14.3.4 雙向迭代器

    14.3.5 迭代器模式的優勢

優勢:

  1.更好的封裝性

  2.迭代器模式可讓你訪問一個聚合對象的內容,而無須暴露該聚合對象的內部表示,從而提升聚合對象的封裝性

  3.能夠以不一樣的遍歷方式來遍歷一個聚合

  4.使用迭代器模式,使得聚合對象的內容和具體的迭代算法分離開.這樣就能夠經過使用不一樣的迭代器的實例,不一樣的遍歷方式來遍歷一個聚合對象了.

  5.迭代器簡化了聚合的接口

  6.有了迭代器的接口,則聚合自己就不須要再定義這些接口了,從而簡化了聚合的接口定義

  7.簡化客戶端調用

  8.迭代器爲遍歷不一樣的聚合對象提供了一個統一的接口,使得客戶端遍歷聚合對象的內容變得更簡單

  9.同一個聚合上能夠有多個遍歷

  10.每一個迭代器保持它本身的遍歷狀態,好比前面實現中的迭代索引位置,所以能夠對同一個聚合對象同時進行多個遍歷

    14.3.6 思考迭代器模式

Q:迭代器模式的本質

A:控制訪問聚合對象中的元素

Q:什麼時候選用迭代器模式

A:  1.若是你但願提供訪問一個聚合對象的內容,可是又不想暴露它的內部表示的時候,能夠使用迭代器模式來提供迭代器接口,從而讓客戶端只是經過迭代器的接口來訪問聚合對象,而無須關係聚合對象的內部實現

   2.若是你但願有多種遍歷方式能夠訪問聚合對象,能夠使用迭代器模式

   3.若是你但願爲遍歷不一樣的聚合對象提供一個統一的接口,能夠使用迭代器模式

    14.3.7 翻頁迭代

    14.3.8 相關模式

1.迭代器模式和組合模式

  這兩個模式能夠組合使用

  組合模式是一種遞歸的對象結構,在枚舉某個組合對象的子對象的時候,一般會使用迭代器模式

2.迭代器模式和工廠方法模式

  這兩個模式能夠組合使用

  在聚合對象建立迭代器的時候,一般會採用工廠方法模式來實例化相應的迭代器對象

第15章 組合模式(Composite)

  15.1 場景問題

    15.1.1 商品類別樹

商品類別樹:

  1.有一個根節點,好比服裝,它沒有父節點,它能夠包含其餘的節點

  2.樹枝節點,有一類節點能夠包含其餘的節點,稱之爲樹枝節點,好比男裝,女裝

  3.葉子節點,有一類節點沒有子節點,稱之爲葉子節點,好比襯衣,夾克,裙子,套裝

    15.1.2 不用模式的解決方案

要管理商品類別樹,就是要管理樹的各個節點,如今樹上的節點有三類,根節點,樹枝節點和葉子節點,再進一步分析發現,根節點和樹枝節點是相似的,都是能夠包含其餘節點的節點,把它們稱爲容器節點

這樣一來,商品類別樹的節點就被分紅了兩種,一種是容器節點,另外一種是葉子節點.容器節點能夠包含其餘的容器節點或者葉子節點.把它們分別實現成爲對象,也就是容器對象和葉子對象,容易對象能夠包含其餘的容器對象或者葉子對象,換句話說,容器對象是一種組合對象

using System;
using System.Collections;
using System.Collections.Generic;

namespace Test2 {
    class Program {
        static void Main(string[] args) {
            Composite root = new Composite("服裝");
            Composite c1 = new Composite("男裝");
            Composite c2 = new Composite("女裝");
            
            Leaf leaf1 = new Leaf("襯衣");
            Leaf leaf2 = new Leaf("夾克");
            Leaf leaf3 = new Leaf("裙子");
            Leaf leaf4 = new Leaf("套裝");
            
            root.addComposite(c1);
            root.addComposite(c2);
            
            c1.addLeaf(leaf1);
            c1.addLeaf(leaf2);
            
            c2.addLeaf(leaf3);
            c2.addLeaf(leaf4);
            
            root.printStruct("");
        }

    }

    public class Leaf {
        private string name = "";

        public Leaf(string name) {
            this.name = name;
        }

        public void printStruct(string preStr) {
            Console.WriteLine(preStr + "-" + name);
        }
    }

    public class Composite {
        private List<Composite> childComposite = new List<Composite>();
        private List<Leaf> childLeaf = new List<Leaf>();
        private string name = "";

        public Composite(string name) {
            this.name = name;
        }

        public void addComposite(Composite c) {
            childComposite.Add(c);
        }

        public void addLeaf(Leaf l) {
            childLeaf.Add(l);
        }

        public void printStruct(string preStr) {
            Console.WriteLine(preStr + "+" + name);

            preStr += " ";

            foreach (Leaf leaf in childLeaf) {
                leaf.printStruct(preStr);
            }

            foreach (Composite composite in childComposite) {
                composite.printStruct(preStr);
            }
        }
    }
}
View Code

    15.1.3 有何問題

Q:有何問題

A:上面的實現,雖然能實現要求的功能,可是有一個很明顯的問題:那就是必須區分組合對象和葉子對象,並進行有區別的對待,好比在Composite和Client裏面,都須要去區別對待這兩種對象

 區別對待組合對象和葉子對象,不只讓程序變得複雜,還對功能的擴展也帶來不便.實際上,大多數狀況下用戶並不想要去區別它們,而是認爲它們是同樣的,這樣他們操做起來最簡單

  15.2 解決方案

    15.2.1 使用組合模式來解決問題

Q:組合模式的定義

A:將對象組合成樹型結構以表示"部分-總體"的層次結構.組合模式使得用戶對單個對象和組合對象的使用具備一致性

仔細分析上面不用模式的例子,要區分組合對象和葉子對象的根本緣由,就在於沒有把組合對象和葉子對象統一塊兒來,也就是說,組合對象類型h和葉子對象類型是徹底不一樣的類型,這致使了操做的時候必須區分它們

組合模式經過引入一個抽象的組件對象,做爲組合對象和葉子對象的父對象,這樣就把組合對象和葉子對象統一塊兒來了,用戶使用的時候,始終是在操做組件對象,而再也不去區分是在操做組合對象仍是葉子對象

    15.2.2 組合模式的結構和說明

  [Component] 抽象的組件對象,爲組合中的對象聲明接口,讓客戶端能夠經過這個接口來訪問和管理整個對象結構,能夠在裏面爲定義的功能提供缺省的實現

  [Leaf] 葉子節點對象,定義和實現葉子對象的行爲,再也不包含其餘的子節點對象

  [Composite] 組合對象,一般會存儲子組件,定義包含子組件的那些組件的行爲,並實如今組件接口中定義的與子組件有關的操做

  [Client] 客戶端,經過組件接口來操做組合結構裏面的組件對象

    15.2.3 組合模式示例代碼

using System;
using System.Collections;
using System.Collections.Generic;

namespace Test2 {
    class Program {
        static void Main(string[] args) {
            Composite root = new Composite();
            Composite c1 = new Composite();
            Composite c2 = new Composite();

            Leaf leaf1 = new Leaf();
            Leaf leaf2 = new Leaf();
            Leaf leaf3 = new Leaf();

            root.addChild(c1);
            root.addChild(c2);
            root.addChild(leaf1);

            c1.addChild(leaf2);
            c2.addChild(leaf3);

            Component o = root.getChildren(1);
            Console.WriteLine(o);

        }
    }

    public abstract class Component {

        public virtual void addChild(Component child) {
            throw new NotImplementedException("對象不支持這個功能");
        }

        public virtual void removeChild(Component child) {
            throw new NotImplementedException("對象不支持這個功能");
        }

        public virtual Component getChildren(int index) {
            throw new NotImplementedException("對象不支持這個功能");
        }
        
        public abstract void someOperation();
    }
    
    public class Composite : Component {
        private List<Component> childComponents = null;
        
        public override void addChild(Component child) {
            if (childComponents == null) {
                childComponents = new List<Component>();
            }

            childComponents.Add(child);
        }

        public override void removeChild(Component child) {
            if (childComponents != null) {
                childComponents.Remove(child);
            }
        }

        public override Component getChildren(int index) {
            if (childComponents != null) {
                if (index >= 0 && index < childComponents.Count) {
                    return childComponents[index];
                }
            }

            return null;
        }

        public override void someOperation() {
            if (childComponents != null) {
                foreach (Component component in childComponents) {
                    component.someOperation();
                }
            }
        }
        
    }
    

    public class Leaf : Component {
        public override void someOperation() {
            
        }
    }


}
View Code

    15.2.4 使用組合模式重寫示例

using System;
using System.Collections;
using System.Collections.Generic;

namespace Test2 {
    class Program {
        static void Main(string[] args) {
            Component root = new Composite("服裝");
            Component c1 = new Composite("男裝");
            Component c2 = new Composite("女裝");
            
            Component leaf1 = new Leaf("襯衣");
            Component leaf2 = new Leaf("夾克");
            Component leaf3 = new Leaf("裙子");
            Component leaf4 = new Leaf("套裝");
            
            root.addChild(c1);
            root.addChild(c2);
            
            c1.addChild(leaf1);
            c1.addChild(leaf2);

            c2.addChild(leaf3);
            c2.addChild(leaf4);

            root.printStruct("");
        }
    }

    public abstract class Component {

        public virtual void addChild(Component child) {
            throw new NotImplementedException("對象不支持這個功能");
        }

        public virtual void removeChild(Component child) {
            throw new NotImplementedException("對象不支持這個功能");
        }

        public virtual Component getChildren(int index) {
            throw new NotImplementedException("對象不支持這個功能");
        }
        
        public abstract void printStruct(string preStr);
    }
    
    public class Composite : Component {
        private string name = "";
        private List<Component> childComponents = null;

        public Composite(string name) {
            this.name = name;
        }
        
        public override void addChild(Component child) {
            if (childComponents == null) {
                childComponents = new List<Component>();
            }

            childComponents.Add(child);
        }

        public override void printStruct(string preStr) {
            Console.WriteLine(preStr + "+" + name);
            if (childComponents != null) {
                preStr += " ";

                foreach (Component component in childComponents) {
                    component.printStruct(preStr);
                }
            }
        }
    }
    

    public class Leaf : Component {
        private string name = "";

        public Leaf(string name) {
            this.name = name;
        }

        public override void printStruct(string preStr) {
            Console.WriteLine(preStr + "-" + name);
        }
    }


}
View Code

  15.3 模式講解

    15.3.1 認識組合模式

Q:組合模式的目的

A:讓客戶端再也不區分操做的是組合對象仍是葉子對象,而是以一個統一的方式來操做

   實現這個目標的關鍵之處,是設計一個抽象的組件類,讓它能夠表明組合對象和葉子對象.這樣一來,客戶端就不用區分到底操做的是組合對象仍是葉子對象了,只須要把它們所有看成組件對象進行統一的操做就能夠了

Q:對象樹

A:一般,組合模式會組合出樹型結構來,組成這個樹型結構所使用的多個組件對象,就天然地造成了對象樹

 這也意味着,全部能夠使用對象樹來描述或操做的功能,均可以考慮使用組合模式,好比讀取XML文件,或是對語句進行語法解析等.

Q:最大化Component定義

A:前面講到了組合模式的目的,讓客戶端再也不區分操做的是組合對象仍是葉子對象,而是以一種統一的方式來操做

 因爲要統一兩種對象的操做,因此Component中的方法也主要是兩種對象對外方法的和.換句話說,有點大雜燴的意思,組件裏面既有葉子對象須要的方法,也有組合對象須要的方法

 常見的作法是在Component中爲對某些子對象沒有意義的方法提供默認的實現,或是默認拋出不支持該功能的例外,這樣一來,若是子對象須要這個功能,那就覆蓋實現它,若是不須要,那就不用管了,使用父類的默認實現就能夠了

 從另外一個層面來講,若是把葉子對象當作是一個特殊的Composite對象,也就是沒有子節點的組合對象,對於Component而言,子對象就被所有看作是組合對象,所以定義的全部方法都是有意義的了.

    15.3.2 安全性和透明性

Q:安全性和透明性

A:  安全性:從客戶使用組合模式上看是否更安全,若是是安全的,那麼就不會有發生誤操做的可能,能訪問的方法都是被支持的功能

   透明性:從客戶使用組合模式上看,是否須要區分究竟是組合對象仍是葉子對象.若是是透明的,那就不用再區分,對於客戶而言,都是組件對象,具體的類型對於客戶而言是透明的,是客戶無須關心的.

Q:透明性的實現

A:  若是把管理子組件的操做定義在Component中,那麼客戶端只須要面對Component,而無須關心具體的組件類型,這種實現方式就是透明性的實現.事實上,前面示例的實現方式都是這種實現方式

  可是透明性的實現是以安全性爲代價的,由於在Component中定義的一些方法,對於葉子對象來講是沒有意義的,好比增長,刪除子組件對象.而客戶不知道這些區別,對客戶是透明的,所以客戶可能會對葉子對象調用這種增長或刪除子組件的方法,這樣的操做是不安全的

  組合模式的透明性實現,一般的方式是:在Component中聲明管理子組件的操做,並在Component中爲這些方法提供默認的實現,若是子對象不支持的功能,默認的實現能夠是拋出一個例外,來表示不支持這個功能

Q:安全性的實現

A:若是把管理子組件的操做定義在Composite中,那麼客戶在使用葉子對象的時候,就不會發生使用添加子組件或是刪除子組件的操做了,由於壓根就沒有這樣的功能,這種實現方式是安全的

 可是這樣一來,客戶端在使用的時候,就必須區分到底使用的是Composite對象,仍是葉子對象,不一樣對象的功能是不同的.也就是說,這種實現方式,對客戶而言就不是透明的了

using System;
using System.Collections;
using System.Collections.Generic;

namespace Test2 {
    class Program {
        static void Main(string[] args) {
            Composite root = new Composite("服裝");
            Composite c1 = new Composite("男裝");
            Composite c2 = new Composite("女裝");
            
            Leaf leaf1 = new Leaf("襯衣");
            Leaf leaf2 = new Leaf("夾克");
            Leaf leaf3 = new Leaf("裙子");
            Leaf leaf4 = new Leaf("套裝");
            
            root.addChild(c1);
            root.addChild(c2);
            
            c1.addChild(leaf1);
            c1.addChild(leaf2);

            c2.addChild(leaf3);
            c2.addChild(leaf4);

            root.printStruct("");
        }
    }

    public abstract class Component {
        public abstract void printStruct(string preStr);
    }
    
    public class Composite : Component {
        private string name = "";
        private List<Component> childComponents = null;

        public Composite(string name) {
            this.name = name;
        }
        
        public void addChild(Component child) {
            if (childComponents == null) {
                childComponents = new List<Component>();
            }

            childComponents.Add(child);
        }

        public override void printStruct(string preStr) {
            Console.WriteLine(preStr + "+" + name);
            if (childComponents != null) {
                preStr += " ";

                foreach (Component component in childComponents) {
                    component.printStruct(preStr);
                }
            }
        }
    }
    

    public class Leaf : Component {
        private string name = "";

        public Leaf(string name) {
            this.name = name;
        }

        public override void printStruct(string preStr) {
            Console.WriteLine(preStr + "-" + name);
        }
    }


}
View Code

Q:兩種實現方式的選擇

A:對於組合模式而言,在安全性和透明性上,會更看重透明性,畢竟組合模式的功能就是要讓用戶對葉子對象和組合對象的使用具備一致性

 並且對於安全性的實現,須要區分是組合對象仍是葉子對象.有的時候,須要將對象進行類型轉換,卻發現類型信心丟失了,只好強行轉換,這種類型轉換必然是不夠安全的.

 對於這種狀況的處理方法是在Component中定義一個getComposite方法,用來判斷是組合對象仍是葉子對象,若是是組合對象,就返回組合對象,若是是葉子對象,就返回null,這樣就實現了先判斷,而後再強制轉換

 所以在使用組合模式的時候,建議多采用透明性的實現方式,而少用安全性的實現方式

    15.3.3 父組件引用

using System;
using System.Collections;
using System.Collections.Generic;

namespace Test2 {
    class Program {
        static void Main(string[] args) {
            Composite root = new Composite("服裝");
            Composite c1 = new Composite("男裝");
            Composite c2 = new Composite("女裝");
            
            Leaf leaf1 = new Leaf("襯衣");
            Leaf leaf2 = new Leaf("夾克");
            Leaf leaf3 = new Leaf("裙子");
            Leaf leaf4 = new Leaf("套裝");
            
            root.addChild(c1);
            root.addChild(c2);
            
            c1.addChild(leaf1);
            c1.addChild(leaf2);

            c2.addChild(leaf3);
            c2.addChild(leaf4);

            root.printStruct("");

            Console.WriteLine("------");

            root.removeChild(c1);
            root.printStruct("");
        }
    }

    public abstract class Component {
        private Component parent = null;

        public Component getParent() {
            return parent;
        }

        public void setParent(Component parent) {
            this.parent = parent;
        }

        public virtual List<Component> getChildren() {
            throw new NotImplementedException("對象不支持這個功能");
        }

        public virtual void addChild(Component child) {
            throw new NotImplementedException("對象不支持這個功能");
        }

        public virtual void removeChild(Component child) {
            throw new NotImplementedException("對象不支持這個功能");
        }

        public virtual void getChildren(int index) {
            throw new NotImplementedException("對象不支持這個功能");
        }
        
        public abstract void printStruct(string preStr);
    }
    
    public class Composite : Component {
        private string name = "";
        private List<Component> childComponents = null;

        public Composite(string name) {
            this.name = name;
        }
        
        public override void addChild(Component child) {
            if (childComponents == null) {
                childComponents = new List<Component>();
            }

            childComponents.Add(child);
            child.setParent(this);
        }

        public override void removeChild(Component child) {
            if (childComponents != null) {
                int idx = childComponents.IndexOf(child);
                if (idx != -1) {
                    foreach (Component component in child.getChildren()) {
                        component.setParent(this);
                        childComponents.Add(component);
                    }

                    childComponents.RemoveAt(idx);
                }
            }
        }

        public override List<Component> getChildren() {
            return childComponents;
        }

        public override void printStruct(string preStr) {
            Console.WriteLine(preStr + "+" + name);
            if (childComponents != null) {
                preStr += " ";

                foreach (Component component in childComponents) {
                    component.printStruct(preStr);
                }
            }
        }
    }
    

    public class Leaf : Component {
        private string name = "";

        public Leaf(string name) {
            this.name = name;
        }

        public override void printStruct(string preStr) {
            Console.WriteLine(preStr + "-" + name);
        }
    }


}
View Code

    15.3.4 環狀引用

    15.3.5 組合模式的優缺點

優勢

  1.定義了包含基本對象和組合對象的類層次結構

    在組合模式中,基本對象能夠被組合成複雜的組合對象,而組合對象又能夠組合成更復雜的組合對象,能夠不斷地遞歸組合下去,從而構成一個統一的組合對象的類層次結構

  2.統一了組合對象和葉子對象

    在組合模式中,能夠把葉子對象看成特殊的組合對象看待,爲它們定義統一的父類,從而把組合對象和葉子對象的行爲統一塊兒來.

  3.簡化了客戶端調用

    組合模式經過統一組合對象和葉子對象,使得客戶端在使用它們的時候,不須要再去區分它們,客戶不關心使用的究竟是什麼類型的對象,這就大大簡化了客戶端的使用

  4.更容易擴展

    因爲客戶端是統一地面對Component來操做,所以,新定義的Composite或Leaf子類可以很容易地與已有的結構一塊兒工做,而客戶端不須要爲增添了新的組件類而改變

缺點

  1.組合模式的缺點是很難限制組合中的組件類型

    容易增長新的組件也會帶來一些問題,好比很難限制組合中的組件類型。

    這在須要檢測組件類型的時候,使得咱們不能依靠編譯期的類型約束來完成,必須在運行期間動態檢測.   

    15.3.6 思考組合模式

Q:組合模式的本質

A:統一葉子對象和組合對象

   組合模式經過把葉子對象當成特殊的組合對象看待,從而對葉子對象和組合對象一視同仁,所有當成了Component對象,有機地統一了葉子對象和組合對象

   正是由於統一了葉子對象和組合對象,在將對象構建成樹型結構的時候,纔不須要作區分,反正是組件對象裏面包含其餘的組件對象,如此遞歸下去;也才使得對於樹形結構的操做變得簡單,無論對象類型,統一操做

Q:什麼時候選用組合模式

A:  1.若是你想表示對象的部分---總體層次結構,能夠選用組合模式,把總體和部分的操做統一塊兒來,使得層次結構實現更簡單,從外部來使用這個層次結構也容易

     2. 若是你但願統一地使用組合結構中的全部對象,能夠選用組合模式,這正是組合模式提供的主要功能.

    15.3.7 相關模式

1.組合模式和裝飾模式

  這兩個模式能夠組合使用

  裝飾模式在組裝多個裝飾器對象的時候,是一個裝飾器找下一個裝飾器,下一個裝飾器再找下一個,如此遞歸下去.其實這種結構也能夠使用組合模式來幫助構建,這樣一來,裝飾器對象就至關於組合模式的Composite對象了.

  要讓兩個模式能很好地組合使用,一般會讓它們有一個公共的父類,所以裝飾器必須支持組合模式須要的一些功能,好比,增長,刪除子組件等

2.組合模式和享元模式

  這兩個模式能夠組合使用

  若是組合模式中出現大量類似的組件對象的話,能夠考慮使用享元模式來幫助緩存組件對象,這樣能夠減小對內存的須要

  使用享元模式也是條件的,若是組件對象的可變化部分的狀態可以從組件對象中分離出去,而且組件對象自己不須要向父組件發送請求的話,就能夠採用享元模式

3.組合模式和迭代器模式

  這兩個模式能夠組合使用

  在組合模式中,一般能夠使用迭代器模式來遍歷組合對象的子對象集合,而無需關心具體存放子對象的聚合結構

4.組合模式和訪問者模式

  這兩個模式能夠組合使用

  訪問者模式可以在不修改原有對象結構的狀況下,爲對象結構中的對象增添新的功能.訪問者模式h和組合模式合用,能夠把本來分散在Composite和Leaf類中的操做和行爲都局部化

  若是在使用組合模式的時候,預計到從此可能會有增添其餘功能的可能,那麼能夠採用訪問者模式,來預留好添加新功能的方式和通道,這樣之後在添加新功能的時候,就不須要再修改已有的對象結構和已經實現的功能了

5.組合模式和職責鏈模式

  這兩個模式能夠組合使用

  職責鏈模式要解決的問題是:實現請求的發送者和接收者之間解耦,職責鏈模式的實現方式是把多個接收者組合起來,構成職責鏈,而後讓請求在這條鏈上傳遞,直到有接收者處理這個請求爲止

  能夠應用組合模式來構建這條鏈,至關因而子組件找父組件,父組件又找父組件,如此遞歸下去,構成一條處理請求的組件對象鏈

6:組合模式和命令模式

  這兩個模式能夠組合使用

  命令模式中有一個宏命令的功能,一般這個宏命令就是使用組合模式來組裝出來的.

第16章 模板方法模式(Template Method)

  16.1 場景問題

    16.1.1 登陸控制

    16.1.2 不用模式的解決方案

using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;

namespace Test2 {
    class Program {
        static void Main(string[] args) {
 
        }
    }

    public class UserModel {
        private string uuid, userId, pwd, name;

        public string getUuid() {
            return uuid;
        }

        public void setUuid(string uuid) {
            this.uuid = uuid;
        }

        public string getUserId() {
            return userId;
        }

        public void setUserId(string userId) {
            this.userId = userId;
        }

        public string getPwd() {
            return pwd;
        }

        public void setPwd(string pwd) {
            this.pwd = pwd;
        }

        public string getName() {
            return name;
        }

        public void setName(string name) {
            this.name = name;
        }
    }    
    
    public class LoginModel {
        private string userId, pwd;

        public string getUserId() {
            return userId;
        }

        public void setUserId(string userId) {
            this.userId = userId;
        }

        public string getPwd() {
            return pwd;
        }

        public void setPwd(string pwd) {
            this.pwd = pwd;
        }
    }


    
    public class NormalLogin {
        public bool login(LoginModel lm) {
            UserModel um = this.findUserByUserId(lm.getUserId());
            if (um != null) {
                if (um.getUserId().Equals(lm.getPwd()) && um.getPwd().Equals(lm.getPwd())) {
                    return true;
                }
            }

            return false;
        }

        private UserModel findUserByUserId(string userId) {
            UserModel um = new UserModel();
            um.setUserId(userId);
            um.setName("test");
            um.setPwd("test");
            um.setUserId("User0001");
            return um;
        }
    }



    public class WorkerModel {
        private string uuid, workerId, pwd, name;

        public string getUuid() {
            return uuid;
        }

        public void setUuid(string uuid) {
            this.uuid = uuid;
        }

        public string getWorkerId() {
            return workerId;
        }

        public void setWorkerId(string workerId) {
            this.workerId = workerId;
        }
        
        public string getPwd() {
            return pwd;
        }

        public void setPwd(string pwd) {
            this.pwd = pwd;
        }

        public string getName() {
            return name;
        }

        public void setName(string name) {
            this.name = name;
        }
    }
    
    
    public class LoginModel {
        private string workerId, pwd;

        public string getWorkerId() {
            return workerId;
        }

        public void setWorkerId(string workerId) {
            this.workerId = workerId;
        }

        public string getPwd() {
            return pwd;
        }

        public void setPwd(string pwd) {
            this.pwd = pwd;
        }
    }
    
    public class WorkerLogin {
        public bool login(LoginModel lm) {
            WorkerModel wm = findWorkerByWorkerId(lm.getWorkerId());

            if (wm != null) {
                string encryptPwd = this.encryptPwd(lm.getPwd());

                if (wm.getWorkerId().Equals(lm.getWorkerId()) && wm.getPwd().Equals(encryptPwd)) {
                    return true;
                }
            }

            return false;
        }

        private string encryptPwd(string pwd) {
            return pwd;
        }

        private WorkerModel findWorkerByWorkerId(string workerId) {
            WorkerModel wm = new WorkerModel();
            wm.setWorkerId(workerId);
            wm.setName("Worker1");
            wm.setPwd("worker1");
            wm.setUuid("Worker0001");
            return wm;
        }
    }

}
View Code

    16.1.3 有何問題

Q:有何問題

A:看了上面的實現示例,是否是很簡單,可是,仔細看看,總會以爲有點問題,兩種登陸的實現太類似了,如今是徹底分開,看成兩個獨立的模塊來實現的,若是從此要擴展功能,好比要添加"控制同一個編號同時只能登陸一次"的功能,那麼兩個模塊都須要修改,是很麻煩的,並且,如今的  實現中,也有不少類似的地方,顯得很重複;另外,具體的實現h和判斷的步驟混合在一塊兒,不利於從此變換功能,好比要變換加密算法等.

 總之,上面的實現,有兩個很明顯的問題:一是重複或類似代碼太多;二是擴展起來很不方便

  16.2. 解決方案

    16.2.1 使用模板方法模式來解決問題

Q:模板方法模式的定義

A:定義一個操做中的算法的骨架,而將一些步驟延遲到子類中,模板方法使得子類能夠不改變一個算法的結構便可重定義該算法的某些特定步驟

    16.2.2 模板方法模式的結構和說明

  [AbstractClass] 抽象類.用來定義算法骨架和原語操做,具體的子類經過重定義這些原語操做來實現一個算法的各個步驟.在這個類裏面,還能夠提供算法中通用的實現

  [ConcreteClass] 具體實現類,用來實現算法骨架中的某些步驟,完成與特定子類相關的功能

    16.2.3 模板方法模式示例代碼

using System;
using System.Collections;
using System.Collections.Generic;

namespace Test2 {
    class Program {
        static void Main(string[] args) {
 
        }
    }

    public abstract class AbstractClass {
        public abstract void doPrimitiveOperation1();
        public abstract void doPrimitiveOperation2();

        public void templateMethod() {
            doPrimitiveOperation1();
            doPrimitiveOperation2();
        }
    }

    public class ConcreteClass : AbstractClass {
        public override void doPrimitiveOperation1() {
            
        }
        
        public override void doPrimitiveOperation2() {
            
        }
    }

}
View Code

    16.2.4 使用模板方法模式重寫示例

using System;
using System.Collections;
using System.Collections.Generic;

namespace Test2 {
    class Program {
        static void Main(string[] args) {
            LoginModel lm = new LoginModel();
            lm.setLoginId("admin");
            lm.setPwd("workerpwd");
            
            LoginTemplate lt = new WorkerLogin();
            LoginTemplate lt2 = new NormalLogin();

            bool flag = lt.login(lm);
            Console.WriteLine("能夠登陸工做平臺 = " + flag);

            bool flag2 = lt2.login(lm);
            Console.WriteLine("能夠進行普通人員登陸 = " + flag2);
            
            
        }
    }

    public class LoginModel {
        private string loginId;
        private string pwd;

        public string getLoginId() {
            return loginId;
        }

        public void setLoginId(string loginId) {
            this.loginId = loginId;
        }

        public string getPwd() {
            return pwd;
        }

        public void setPwd(string pwd) {
            this.pwd = pwd;
        }
    }

    public abstract class LoginTemplate {
        
        public bool login(LoginModel lm) {
            LoginModel dbLm = this.findLoginUser(lm.getLoginId());

            if (dbLm != null) {
                string encryptPwd = this.encryptPwd(lm.getPwd());
                lm.setPwd(encryptPwd);
                return this.match(lm, dbLm);
            }

            return false;
        }

        public abstract LoginModel findLoginUser(string loginId);

        public virtual string encryptPwd(string pwd) {
            return pwd;
        }

        public bool match(LoginModel lm, LoginModel dbLm) {
            if (lm.getLoginId().Equals(dbLm.getLoginId()) && lm.getPwd().Equals(dbLm.getPwd())) {
                return true;
            }

            return false;
        }
    }

    public class NormalLogin : LoginTemplate {
        public override LoginModel findLoginUser(string loginId) {
            LoginModel lm = new LoginModel();
            lm.setLoginId(loginId);
            lm.setPwd("testpwd");
            return lm;
        }
    }

    public class WorkerLogin : LoginTemplate {
        public override LoginModel findLoginUser(string loginId) {
            LoginModel lm = new LoginModel();
            lm.setLoginId(loginId);
            lm.setPwd("workerpwd");
            return lm;
        }

        public override string encryptPwd(string pwd) {
            Console.WriteLine("使用MD5進行密碼加密");
            return pwd;
        }
    }
}
View Code

  16.3 模式講解

    16.3.1 認識模板方法模式

Q:模板方法模式的功能

A:模板方法模式的功能在於固定算法骨架,而讓具體算法實現可擴展

 這在實際應用中很是普遍,尤爲是在設計框架級功能的時候很是有用.框架定義好了算法的步驟,在合適的點讓開發人員進行擴展,實現具體的算法

 模板方法模式還額外提供了一個好處,就是能夠控制子類的擴展.由於在父類中定義好了算法的步驟,只是在某幾個固定的點纔會調用到被子類實現的方法,所以也就只容許在這幾個點來擴展功能.

 這些能夠被子類覆蓋以擴展功能的方法一般被稱爲"鉤子"方法

Q:爲什麼不是接口

A:何時使用抽象類

 "既要約束子類的行爲,又要爲子類提供公共功能"的時候使用抽象類

 按照這個原則來思考模板方法模式的實現,模板方法模式須要固定定義算法的骨架,這個骨架應該只有一份,算是一個公共的行爲,但其中具體的步驟的實現又多是各不相同的,剛好符合選擇抽象類的原則

 把模板實現稱爲抽象類,爲全部的子類提供了公共的功能,就是定義了具體的算法骨架;同時在模板中把須要由子類擴展的具體步驟的算法定義成爲抽象方法,要求子類去實現這些方法,這就約束了子類的行爲

 所以綜合考慮,用抽象類來實現模板是一個很好的選擇

Q:變與不變

A:程序設計的一個很重要的思考點就是"變與不變",也就是分析程序中那些功能是可變的,那些功能是不變的,而後把不變的部分抽象出來,進行公共的實現,把變化的部分分離出去,用接口來封裝隔離,或者是用抽象類來約束子類行爲.

 模板方法模式很好地體現了這一點.模板類實現的就是不變的方法和算法的骨架,而須要變化的地方,都經過抽象方法,把具體實現延遲到子類中了.並且還經過父類的定義來約束了子類的行爲,從而使系統能有更好的複用性和擴展性

Q:好萊塢法則

A:什麼是好萊塢法則呢?簡單點說,就是"不要找咱們,咱們會聯繫你"

 模板方法模式很好地體現了這一點,做爲父類的模板會在須要的時候,調用子類相應的方法,也就是由父類來找子類,而不是讓子類來找父類

 這其實也是一種反向的控制結構.按照一般的思路,是子類找父類纔對,也就是應該是子類來調用父類的方法,由於父類根本就不知道子類,而子類是知道父類的,可是在模板方法模式裏面,是父類來找子類,因此是一種反向的控制結構

    16.3.2 模板的寫法

 在實現模板的時候,到底哪些方法實如今模板上呢?模板能不能所有實現了,也就是模板不提供抽象方法呢?固然,就算沒有抽象方法,模板同樣能夠定義成爲抽象類

一般在模板裏面包含如下操做類型

模板方法:就是定義算法骨架的方法

具體的操做:在模板中直接實現某些步驟的方法.一般這些步驟的實現算法是固定的,並且是不怎麼變化的,所以能夠將其看成公共功能實如今模板中.若是不需爲子類提供訪問這些方法的話,還能夠是private的.這樣一來,子類的實現就相對簡單些

具體的AbstractClass操做:在模板中實現某些公共功能,能夠提供給子類使用,通常不是具體的算法步驟的實現,而是一些輔助的公共功能.

原語操做:就是在模板中定義的抽象操做,一般是模板方法須要調用的操做,是必須的操做,並且在父類中尚未辦法肯定下來如何實現,須要子類來真正實現的方法

鉤子操做:在模板中定義,並提供默認實現的操做.這些方法一般被視爲可擴展的點,但不是必須的,子類能夠有選擇地覆蓋這些方法,以提供新的實現來擴展功能.好比,模板方法中定義了5步操做,可是根據須要,某種具體的實現只須要其中的1,2,3,4幾個步驟,所以它就只須要覆蓋實現1,2,3這幾個步驟對應的方法.那麼4和5步驟對應的方法該怎麼辦呢,因爲有默認實現,那就不用管了.也就是說鉤子操做是能夠被擴展的點,但不是必須的

FactoryMethod:在模板方法中,若是獲得某些對象實例的話,能夠考慮經過工廠方法模式來獲取,把具體的構建對象的實現延遲到子類中去

using System;
using System.Collections;
using System.Collections.Generic;

namespace Test2 {
    class Program {
        static void Main(string[] args) {

            
        }
    }

    public abstract class AbstractTemplate {

        public void templateMethod() {
            operation1();
            operation2();
            doPrimitiveOperation1();
            doPrimitiveOperation2();
            hookOperation1();
        }

        private void operation1() {
            
        }

        private void operation2() {
            
        }

        protected void commonOperation() {
            
        }

        protected abstract void doPrimitiveOperation1();
        
        protected abstract void doPrimitiveOperation2();

        protected void hookOperation1() {
            
        }

        protected abstract object createOneObject();
    }
}
View Code

    16.3.3 Java回調與模板方法模式

    16.3.4 典型應用:排序

    16.3.5 實現通用的增刪改查

    16.3.6 模板方法模式的優缺點

優勢

  1.模板方法模式的優勢是實現代碼複用

    模板方法模式是一種實現代碼複用的很好的手段.經過把子類的公共功能提煉和抽取,把公共部分放到模板中去實現

缺點

  1.模板方法模式的缺點是算法骨架不容易升級

    模板方法模式最基本的功能就是經過模板的制定,把算法骨架徹底固定下來.事實上模板和子類是很是耦合的,若是要對模板中的算法骨架進行變動,可能就會要求全部相關的子類進行相應的變化.因此抽取算法骨架的時候要特別當心,儘可能確保是不會變化的部分才放到模板中

    16.3.7 思考模板方法模式

Q:模板方法模式的本質

A:固定算法骨架

 模板方法模式主要是經過指定模板,把算法步驟固定下來,至於誰來實現,模板能夠本身提供實現,也能夠由子類去實現,還能夠經過回調機制讓其餘類來實現

 經過固定算法骨架來約束子類的行爲,並在特定的擴展點來讓子類進行功能擴展,從而讓程序既有很好的複用性,又有較好的擴展性

Q:對設計原則的體現

A:模板方法很好地體現了開閉原則和里氏替換原則

 首先從設計上分離變與不變,而後把不變的的部分抽取出來,定義到父類中,好比算法骨架,一些公共的,固定的實現等,這些不變的部分被封閉起來,儘可能不去修改它們.想要擴展新的功能,那就使用子類來擴展,經過子類來實現可變化的步驟,對於這種新增功能的作法是開放的

 其次,可以實現統一的算法骨架,經過切換不一樣的具體實現來切換不一樣的功能,一個根本緣由就是里氏替換原則,遵循這個原則,保證全部的子類實現的是同一個算法模板,並能在使用模板的地方,根據須要切換不一樣的具體實現

Q:什麼時候選用模板方法模式

  1.須要固定定義算法骨架,實現一個算法的不變的部分,並把可變的行爲留給子類來實現的狀況

  2.各個子類中具備公共行爲,應該抽取出來,集中在一個公共類中去實現,從而避免代碼重複

  3.須要控制子類擴展的狀況.模板方法模式會在特定的點來調用子類的方法,這樣只容許在這些點進行擴展

    16.3.8 相關模式

1.模板方法模式和工廠方法模式

  這兩個模式能夠配合使用

  模板方法模式能夠經過工廠方法來獲取須要調用的對象

2.模板方法模式和策略模式

  這個模式的功能有些類似,可是是有區別的

  從表面上看,兩個模式都能實現算法的封裝,可是模板方法封裝的是算法的骨架,這個算法骨架是不變的,變化的是算法中某些步驟的具體實現;而策略模式是把某個步驟的具體實現算法封裝起來,全部封裝的算法對象是等價的,能夠相互替換

  所以,能夠在模板方法中使用策略模式,就是把那些變化的算法步驟經過使用策略模式來實現,可是具體選取哪些策略仍是要由外部來肯定,而總體的算法步驟,也就是算法骨架則由模板方法來定義了.

第17章 策略模式(Strategy)

  17.1 場景問題

    17.1.1 報價管理

    17.1.2 不用模式的解決方案

using System;
using System.Collections;
using System.Collections.Generic;

namespace Test2 {
    class Program {
        static void Main(string[] args) {

            
        }
    }

    public class Price {
        public double quote(double goodsPrice, string customerType) {
            if ("普通客戶".Equals(customerType)) {
                Console.WriteLine("對於新客戶或者是普通客戶,沒有折扣");
                return goodsPrice;
            } else if ("老客戶".Equals(customerType)) {
                Console.WriteLine("對於老客戶,統一折扣5%");
                return goodsPrice * (1 - 0.05);
            } else if ("大客戶".Equals(customerType)) {
                Console.WriteLine("對於大客戶,統一折扣10%");
                return goodsPrice * (1 - 0.1);
            }

            return goodsPrice;
        }
    }
}
View Code

    17.1.3 有何問題

Q:有何問題

A:  1.價格類包含了全部計算報價的算法,使得價格類,尤爲是報價這個方法比較龐雜,難以維護

   2.常常會有這樣的須要,在不一樣的時候,要使用不一樣的計算方式

  17.2 解決方案

    17.2.1 使用策略模式來解決問題

Q:策略模式的定義

A:定義一系列的算法,把它們一個個封裝起來,而且使它們可相互替換.本模式使得算法可獨立於使用它的客戶而變化

Q:應用策略模式來解決問題的思路

A:仔細分析上面的問題,先來把它抽象一下,各類計算報價的計算方式就比如是具體的算法,而使用這些計算方式來計算報價的程序,就至關因而使用算法的客戶

 再分析上面的實現方式,爲何會形成那些問題?根本緣由就在於算法和使用算法的客戶是耦合的,甚至是密不可分的.在上面的實現中,具體的算法和使用算法的客戶是同一個類中的不一樣方法.

 如今來解決那些問題.按照策略模式的方式,應該先把全部的計算方式獨立出來,每一個計算方式作成一個單獨的算法類,從而造成一系列的算法,而且爲這一系列算法定義一個公共的接口,這些算法實現是同一接口的不一樣實現,地位是平等的,能夠相互替換.這樣一來,要擴展新的算法就變成了增長一個新的算法實現類,要維護某個算法,也只是修改某個具體的算法實現便可,不會對其餘代碼形成影響.也就是說這樣就解決了可維護,可擴展的問題

 爲了實現讓算法能獨立於使用他的客戶,策略模式引入了一個上下文的對象,這個對象負責持有算法,可是不負責決定具體選用哪一個算法,把選擇算法的功能交給了客戶,由客戶選擇好具體的算法後,設置到上下文對象中,讓上下文對象持有客戶選擇的算法,當客戶通知上下文對象執行功能的時候,上下文對象則轉調具體的算法.這樣一來,具體的算法和直接使用算法的客戶是分離的

 具體的算法和使用他的客戶分離之後,使得算法能夠獨立於使用它的客戶而變化,而且可以動態地切換須要使用的算法,只要客戶端動態地選擇使用不一樣的算法,而後設置到上下文對象中去,在實際調用的時候,就能夠調用到不一樣的算法

    17.2.2 策略模式的結構和說明

  [Strategy] 策略接口,用來約束一系列具體的策略算法.Context使用這個接口來調用具體的策略實現定義的算法

  [ConcreteStrategy] 具體的策略實現,也就是具體的算法實現

  [Context] 上下文,負責和具體的策略類交互.一般上下文會持有一個真正的策略實現,上下文還可讓具體的策略類來獲取上下文的數據,甚至讓具體的策略類來回調上下文的方法

    17.2.3 策略模式示例代碼

using System;
using System.Collections;
using System.Collections.Generic;

namespace Test2 {
    class Program {
        static void Main(string[] args) {

            
        }
    }

    public interface Strategy {
        void algorithmInterface();
    }

    public class ConcreteStrategyA : Strategy {
        public void algorithmInterface() {
            
        }
    }

    public class ConcreteStrategyB : Strategy {
        public void algorithmInterface() {
            
        }
    }

    public class ConcreteStrategyC : Strategy {
        public void algorithmInterface() {
            
        }
    }

    public class Context {
        private Strategy strategy;

        public Context(Strategy strategy) {
            this.strategy = strategy;
        }

        public void contextInterface() {
            strategy.algorithmInterface();
        }
    }
    
}
View Code

    17.2.4 使用策略模式重寫示例

 

using System;
using System.Collections;
using System.Collections.Generic;

namespace Test2 {
    class Program {
        static void Main(string[] args) {
            Strategy strategy = new LargeCustomerStrategy();
            
            Price ctx = new Price(strategy);

            double quote = ctx.quote(1000);
            
            Console.WriteLine("向客戶報價: " + quote);

        }
    }

    public interface Strategy {
        double calcPrice(double goodsPrice);
    }

    public class NormalCustomerStrategy : Strategy {
        public double calcPrice(double goodsPrice) {
            Console.WriteLine("對於新客戶或者是普通客戶,沒有折扣");
            return goodsPrice;
        }
    }

    public class OldCustomerStrategy : Strategy {
        public double calcPrice(double goodsPrice) {
            Console.WriteLine("對於老客戶,統一折扣5%");
            return goodsPrice * (1 - 0.05);
        }
    }

    public class LargeCustomerStrategy : Strategy {
        public double calcPrice(double goodsPrice) {
            Console.WriteLine("對於大客戶,統一折扣10%");
            return goodsPrice * (1 - 0.1);
        }
    }

    public class Price {
        private Strategy strategy;

        public Price(Strategy strategy) {
            this.strategy = strategy;
        }

        public double quote(double goodsPrice) {
            return strategy.calcPrice(goodsPrice);
        }
    }
    
}
View Code

  17.3 模式講解

    17.3.1 認識策略模式

Q:策略模式的功能

A:策略模式的功能是把具體的算法實現從具體的業務處理中獨立出來,把它們實現成爲單獨的算法類,從而造成一系列的算法,並讓這些算法能夠相互替換

 策略模式的重心不是如何來實現算法,而是如何組織,調用這些算法,從而讓程序結構更靈活,具備更好的維護性和擴展性

Q:策略模式和if-else語句

A:看了前面的示例,不少朋友會發現,每一個策略算法具體實現的功能,就是原來在if-else結構中的具體實現

 沒錯,其實多個if-elseif語句表達的就是一個平等的功能結構,你要麼執行if,要麼執行else,或者是elseif,這個時候,if塊中的實現和else塊中的實現從運行地位上來說是平等的

 而策略模式就是把各個平等的具體實現封裝到單獨的策略實現類了,而後經過上下文來與具體的策略類進行交互

 所以多個if-else語句能夠考慮使用策略模式

Q:算法的平等性

A:策略模式一個很大的特色j就是各個策略算法的平等性.對於一系列具體的策略算法,你們的地位是徹底同樣的,正是由於這個平等性,才能實現算法之間能夠相互依賴

 全部的策略算法在實現上也是相互獨立的,相互之間是沒有依賴的

 因此能夠這樣描述這一系列策略算法;策略算法是相同行爲的不一樣實現

Q:誰來選擇具體的策略算法

A:在策略模式中,能夠在兩個地方來進行具體策略的選擇

 一個是在客戶端,當使用上下文的時候,由客戶端來選擇具體的策略算法,而後把這個策略算法設置給上下文

 還有一個是客戶端無論,由上下文來選擇具體的策略算法

Q:Strategy的實現方式

A:在前面的示例中,Strategy都是使用接口來定義的,這也是常見的實現方式.可是若是多個算法具備公共功能的話,能夠把Strategy實現成抽象類,而後把多個算法的公共功能實現到Strategy中

Q:運行時策略的惟一性

A:運行期間,策略模式在每個時刻只能使用一個具體的策略實現對象,雖然能夠動態地在不一樣策略實現中切換,可是同時只能使用一個

Q:增長新的策略

A:策略模式可讓你很靈活地擴展新的算法.具體的作法是,先寫一個策略算法類來實現新的要求,而後在客戶端使用的時候指定使用新的策略算法類就能夠了

    17.3.2 Context和Strategy的關係

    17.3.3 容錯恢復機制

using System;
using System.Collections;
using System.Collections.Generic;

namespace Test2 {
    class Program {
        static void Main(string[] args) {
            LogContext log = new LogContext();
            log.log("記錄日誌");
            log.log("再次記錄日誌");
        }
    }

    public interface LogStrategy {
        void log(string msg);
    }

    public class DbLog : LogStrategy {
        public void log(string msg) {
            if (msg != null && msg.Trim().Length > 5) {
                int a = 5 / 0;
            }
            Console.WriteLine("如今把 '" + msg + "' 記錄到數據庫中");
        }
    }

    public class FileLog : LogStrategy {
        public void log(string msg) {
            Console.WriteLine("如今把 '" + msg + "' 記錄到文件中");
        }
    }

    public class LogContext {
        public void log(string msg) {
            LogStrategy strategy = new DbLog();

            try {
                strategy.log(msg);
            } catch (Exception err) {
                strategy = new FileLog();
                strategy.log(msg);
            }
        }
    }
    
}
View Code

    17.3.4 策略模式結合模板方法模式

using System;
using System.Collections;
using System.Collections.Generic;

namespace Test2 {
    class Program {
        static void Main(string[] args) {
            LogContext log = new LogContext();
            log.log("記錄日誌");
            log.log("再次記錄日誌");
        }
    }

    public interface LogStrategy {
        void log(string msg);
    }

    public abstract class LogStrategyTemplate : LogStrategy {
        public void log(string msg) {
            msg = DateTime.Now.ToString() + " 內容是: " + msg;
            doLog(msg);
        }

        public abstract void doLog(string msg);
    }

    public class DbLog : LogStrategyTemplate {
        public override void doLog(string msg) {
            if (msg != null && msg.Trim().Length > 5) {
                int a = 5 / 0;
            }
            Console.WriteLine("如今把 '" + msg + "' 記錄到數據庫中");
        }
    }

    public class FileLog : LogStrategyTemplate {
        public override void doLog(string msg) {
            Console.WriteLine("如今把 '" + msg + "' 記錄到文件中");
        }
    }

    public class LogContext {
        public void log(string msg) {
            LogStrategy strategy = new DbLog();

            try {
                strategy.log(msg);
            } catch (Exception err) {
                strategy = new FileLog();
                strategy.log(msg);
            }
        }
    }
    
}
View Code

    17.3.5 策略模式的優缺點

優勢

  1.定義一系列算法

    策略模式的功能j就是定義一系列算法,實現讓這些算法能夠相互替換.因此會爲這一系列算法定義公共接口,以約束一系列算法要實現的功能.若是這一系列算法具備公共功能,能夠把策略接口實現成爲抽象類,把這些公共功能實現到父類中

  2.避免多重條件語句

  3.更好的擴展性

缺點

  1.客戶必須瞭解每種策略的不一樣

  2.增長了對象數目

  3.策略模式的一系列算法地位是平等的,是能夠相互替換的,事實上構成了一個扁平的算法結構,也就是在一個策略接口下,有多個平等的策略算法,就至關於兄弟算法.並且在運行時刻只有一個算法被使用,這就限制了算法使用的層級,使用的時候不能嵌套使用

     對於出現須要嵌套使用多個算法的狀況,好比折上折,折後返卷等業務的實現,須要組合或者是嵌套使用多個算法的狀況,能夠考慮使用裝飾模式,或是變形的職責鏈,或是AOP等方式來實現

    17.3.6 思考策略模式

Q:策略模式的本質

A:分離算法,選擇實現

 仔細思考策略模式的結構和實現的功能,會發現,若是沒有上下文,策略模式就回到了最基本的接口和實現了,只要是面向接口編程的,那麼就可以享受到接口的封裝隔離帶來的好處.也就是經過一個統一的策略接口來封裝和隔離具體的策略算法,面向接口編程的話,天然不須要關係具體的策略實現,也能夠經過使用不一樣的實現類來實例化接口,從而實現切換具體的策略

 看起來好像沒有上下文什麼事情,可是若是沒有上下文,那麼就須要客戶端來直接與具體的策略交互,尤爲是當須要提供一些公共功能,或者是相關狀態存儲的時候,會大大增長客戶端使用的難度.所以,引入上下文仍是頗有必要的,有了上下文,這些工做就由上下文來完成了,客戶端只須要與上下文交互就能夠了,這樣會讓整個設計模式更獨立,更有總體性,也讓客戶端更簡單

 但縱觀整個策略模式實現的功能和設計,它的本質仍是"分離算法,選擇實現",由於分離並封裝了算法,纔可以很容易地修改和添加算法;也能很容易地動態切換使用不一樣的算法,也就是動態選擇一個算法來實現須要的功能

Q:對設計原則的體現

 從設計原則上來看,策略模式很好地體現了開-閉原則.策略模式經過把一系列可變的算法進行封裝,並定義出合理的使用結構,使得在系統出現新算法的時候,能很容易地把新的算法加入到已有的系統中,而已有的實現不須要作任何修改.

 從設計原則上來看,策略模式還很好地體現了里氏替換原則.策略模式是一個扁平結構,一系列的實現算法實際上是兄弟關係,都是實現同一個接口或者繼承的同一個父類.這樣只要使用策略的客戶保持面向對象抽象類型編程,就可以使用不一樣策略的具體實現對象來配置它,從而實現一系列算法能夠相互代替

Q:什麼時候選用策略模式

A:  1.出現有許多相關的類,僅僅是行爲有差異的前提下,能夠使用策略模式來使用多個行爲中的一個來配置一個類的方法,實現算法動態切換

   2.出現同一個算法,有不少不一樣實現的狀況下,能夠使用策略模式來把這些"不一樣的實現"實現成爲一個算法的類層次

   3.須要封裝算法中,有與算法相關數據的狀況下,能夠使用策略模式來避免暴露這些跟算法相關的數據結構

   4.出現抽象一個定義了不少行爲的類,而且是經過多個if-else語句來選擇這些行爲的狀況下,能夠使用策略模式來代替這些條件語句

    17.3.7 相關模式

1.策略模式和狀態模式

  這兩個模式從模式結構上看是同樣的,可是實現的功能倒是不同的

  狀態模式是根據狀態的變化來選擇相應的行爲,不一樣的狀態對應不一樣的類,每一個狀態對應的類實現了該狀態對應的功能,在實現功能的同時,還會維護狀態數據的變化.這些實現狀態對應的功能的類之間是不能相互替換的.策略模式是根據須要或者是客戶端的要求來選擇相應的實現類,各個實現類是平等的,是能夠相互替換的.另外策略模式可讓客戶端來選擇須要使用的策略算法;而狀態模式通常是由上下文,或者是在狀態實現類裏面來維護具體的狀態數據,一般不禁客戶端來指定狀態

2.策略模式和模板方法模式

  這兩個模式可組合使用

  模板方法重在封裝算法骨架;而策略模式重在分離並封裝算法實現

3.策略模式和享元模式

  這兩個模式可組合使用

  策略模式分離並封裝出一系列的策略算法對象,這些對象的功能一般都比較單一,不少時候就是爲了實現某個算法的功能而存在.所以,針對這一系列的,多個細粒度的對象,能夠應用享元模式來節省資源,但前提是這些算法對象要被頻繁地使用,若是偶爾用一次,就沒有必要作成享元了

第18章 狀態模式(State)

  18.1 場景問題

    18.1.1 實如今線投票

    18.1.2 不用模式的解決方案

using System;
using System.Collections.Generic;

namespace ConsoleApplication1 {
    public class Program {
        public static void Main() {
            VoteManager vm = new VoteManager();
            for (int i = 0; i < 8; i++) {
                vm.vote("u1", "A");
            }
        }

    }

    public class VoteManager {
        private Dictionary<string,string> mapVote = new Dictionary<string, string>();
        private Dictionary<string,int> mapVoteCount = new Dictionary<string, int>();


        public void vote(string user, string voteItem) {
            int oldVoteCount = 0;

            if (!mapVoteCount.TryGetValue(user, out oldVoteCount)) {
                oldVoteCount = 0;
            }

            oldVoteCount = oldVoteCount + 1;
            mapVoteCount[user] = oldVoteCount;

            if (oldVoteCount == 1) {
                mapVote[user] = voteItem;
                Console.WriteLine("恭喜你投票成功");
            } else if (oldVoteCount > 1 && oldVoteCount < 5) {
                Console.WriteLine("請不要重複投票");
            } else if (oldVoteCount >= 5 && oldVoteCount < 8) {
                string s = "";
                if (!mapVote.TryGetValue(user,out s)) {
                    mapVote.Remove(user);
                }
                
                Console.WriteLine("你有惡意刷票行爲,取消投票資格");
            } else if (oldVoteCount >= 8) {
                Console.WriteLine("進入黑名單,將禁止登陸和使用本系統");
            }
        }
    }

}
View Code

    18.1.3 有何問題

Q:有何問題

A:看起來很簡單,是否是?辛虧這裏只是示意,不然,你想一想,在vote()方法中那麼多判斷,還有每一個判斷對應的功能處理都放在一塊兒,是否是有點太雜亂了,那簡直就是個大雜燴,若是把每一個功能都完整地實現出來,那vote()方法會很長的

 一個問題是,若是如今要修改某種投票狀況所對應的具體功能處理,那就須要在那個大雜燴中,找到相應的代碼塊,而後進行改動

 另一個問題是,若是要添加新的功能,好比投票超過8次但不足10次的,給個機會,只是禁止登陸和使用系統3天,若是再犯,才永久封掉帳號,該怎麼辦呢?那就須要改動投票管理的源代碼,在上面的if-else結構中再添加一個else if塊進行處理

 無論哪種狀況,都是在一大堆的控制代碼中找出須要的部分,而後進行修改,這不是個好方法.那麼該如何實現才能作到:既可以很容易地給vote()方法添加新的功能,又可以很方便地修改已有的功能處理呢?

  18.2 解決方案

    18.2.1 使用狀態模式來解決問題

Q:狀態模式的定義

A:容許一個對象在其內部狀態改變時改變它的行爲.對象看起來彷佛修改了它的類

    18.2.2 狀態模式的結構和說明

  [Context] 環境,也稱上下文,一般用來定義客戶感興趣的接口,同時維護一個來具體處理當前狀態的實例對象

  [State] 狀態接口,用來封裝與上下文的一個特定狀態所對應的行爲

  [ConcreteState] 具體實現狀態處理的類,每一個類實現一個跟上下文相關的狀態的具體處理

    18.2.3 狀態模式示例代碼

using System;
using System.Collections;
using System.Collections.Generic;

namespace Test2 {
    public class Program {
        static void Main(string[] args) {

        }
    }

    public interface State {
        void handle(string sampleParameter);
    }

    public class ConcreteStateA : State {
        public void handle(string sampleParameter) {
            
        }
    }

    public class ConcreteStateB : State {
        public void handle(string sampleParameter) {
            
        }
    }

    public class Context {
        private State state;

        public void setState(State state) {
            this.state = state;
        }

        public void request(string sampleParameter) {
            state.handle(sampleParameter);
        }
    }
}
View Code

    18.2.4 使用狀態模式重寫示例

 

using System;
using System.Collections;
using System.Collections.Generic;

namespace Test2 {
    public class Program {
        static void Main(string[] args) {
            VoteManager vm = new VoteManager();
            for (int i = 0; i < 8; i++) {
                vm.vote("u1","A");
            }
        }
    }

    public interface VoteState {
        void vote(string user, string voteItem, VoteManager voteManager);
    }

    public class VoteManager {
        private VoteState state = null;
        
        private Dictionary<string,string> mapVote = new Dictionary<string, string>();
        private Dictionary<string,int> mapVoteCount = new Dictionary<string, int>();

        public Dictionary<string, string> getMapVote() {
            return mapVote;
        }

        public void vote(string user, string voteItem) {
            int oldVoteCount = 0;
            if (!mapVoteCount.TryGetValue(user,out oldVoteCount)) {
                oldVoteCount = 0;
            }

            oldVoteCount = oldVoteCount + 1;

            mapVoteCount[user] = oldVoteCount;

            if (oldVoteCount == 1) {
                state = new NormalVoteState();
            } else if (oldVoteCount > 1 && oldVoteCount < 5) {
                state = new RepeatVoteState();
            } else if (oldVoteCount >= 5 && oldVoteCount < 8) {
                state = new SpiteVoteState();
            } else if (oldVoteCount >= 8) {
                state = new BlackVoteState();
            }

            state.vote(user, voteItem, this);
        }
    }

    public class NormalVoteState : VoteState {
        public void vote(string user, string voteItem, VoteManager voteManager) {
            voteManager.getMapVote()[user] = voteItem;
            Console.WriteLine("恭喜你投票成功");
        }
    }

    public class RepeatVoteState : VoteState {
        public void vote(string user, string voteItem, VoteManager voteManager) {
            Console.WriteLine("請不要重複投票");
        } 
    }

    public class SpiteVoteState : VoteState {
        public void vote(string user, string voteItem, VoteManager voteManager) {
            string s = null;
            if (voteManager.getMapVote().TryGetValue(user,out s)) {
                voteManager.getMapVote().Remove(user);
            }
            
            Console.WriteLine("你有惡意刷票行爲,取消投票資格");
        }
    }

    public class BlackVoteState : VoteState {
        public void vote(string user, string voteItem, VoteManager voteManager) {
            Console.WriteLine("進入黑名單,將禁止登陸和使用本系統");
        }        
    }
}
View Code

  18.3 模式講解

    18.3.1 認識狀態模式

Q:狀態和行爲

A:所謂對象的狀態,一般指的就是對象實例的屬性的值;而行爲大多能夠對應到方法上.

 狀態模式的功能就是分離狀態的行爲,經過維護狀態的變化,來調用不一樣狀態對應的不一樣功能

 也就是說,狀態和行爲是相關聯的,它們的關係能夠描述爲:狀態決定行爲

 因爲狀態是在運行期被改變的,所以行爲也會在運行期根據狀態的改變而改變,看起來,同一個對象,在不一樣的運行時刻,行爲是不同的,就像是類被修改了同樣

Q:行爲的平行性

A:注意是平行性而不是平等性,所謂平行性指的是各個狀態的行爲爲所處的層次是同樣的,相互是獨立的,沒有關聯的,是根據不一樣的狀態來決定到底走平行線的哪一條.行爲是不一樣的,固然對應的實現也是不一樣的,相互之間是不可替換的

 

 而平等性強調的是可替換性,你們是同一行爲的不一樣描述或實現,所以在同一個行爲發生的時候,能夠根據條件挑選任意一個實現來進行相應的處理

 你們可能會發現狀態模式的結構和策略模式的結構徹底同樣,可是它們的目的,實現,本質倒是徹底不同的.還有行爲之間的特性也是狀態模式和策略模式一個很重要的區別,狀態模式的行爲是平行性的,不可相互替換的;而策略模式的行爲是平等性的,是能夠相互替換的

Q:上下文和狀態處理對象

A:在狀態模式中,上下文是持有狀態的對象,可是上下文自身並不處理跟狀態相關的行爲,而是把處理狀態的功能委託給了狀態對應的狀態處理類來處理

 在具體的狀態處理類中常常須要獲取上下文自身的數據,甚至在必要的時候會回調上下文的方法,所以,一般將上下文自身看成一個參數傳遞給具體的狀態處理類

 客戶端通常只和上下文交互.客戶端能夠用狀態對象來配置一個上下文,一旦配置完畢,就再也不須要和狀態對象打交道了.客戶端一般不負責運行期間狀態的維護,也不負責決定後續到底使用哪個具體的狀態處理對象

    18.3.2 狀態的維護和轉換控制

所謂狀態的維護,指的是維護狀態的數據,給狀態設置不一樣的狀態值;而狀態的轉換,指的是根據狀態的變化來選擇不一樣的狀態處理對象.在狀態模式下,一般有兩個地方能夠進行狀態的維護和轉換控制

一個就是在上下文中.由於狀態自己一般被實現爲上下文對象的狀態,所以能夠在上下文中進行狀態維護,固然也就能夠控制狀態的轉換了.前面投票的示例就是採用這種方式

另一個地方就是在狀態的處理類中,當每一個狀態處理對象處理完自身狀態所對應的功能後,能夠根據須要指定後繼狀態,以便讓應用能正確處理後續的請求

using System;
using System.Collections;
using System.Collections.Generic;

namespace Test2 {
    public class Program {
        static void Main(string[] args) {
            VoteManager vm = new VoteManager();
            for (int i = 0; i < 8; i++) {
                vm.vote("u1","A");
            }
        }
    }

    public interface VoteState {
        void vote(string user, string voteItem, VoteManager voteManager);
    }

    public class VoteManager {
        private VoteState state = null;
        
        private Dictionary<string,VoteState> mapState = new Dictionary<string, VoteState>();
        private Dictionary<string,string> mapVote = new Dictionary<string, string>();
        private Dictionary<string,int> mapVoteCount = new Dictionary<string, int>();

        public Dictionary<string, VoteState> getMapState() {
            return mapState;
        }
        
        public Dictionary<string, string> getMapVote() {
            return mapVote;
        }

        public Dictionary<string, int> getMapVoteCount() {
            return mapVoteCount;
        }
        
        

        public void vote(string user, string voteItem) {
            int oldVoteCount = 0;
            if (!mapVoteCount.TryGetValue(user,out oldVoteCount)) {
                oldVoteCount = 0;
            }

            oldVoteCount = oldVoteCount + 1;

            mapVoteCount[user] = oldVoteCount;
            
            
            VoteState state;
            if (!mapState.TryGetValue(user, out state)) {
                state= new NormalVoteState();
            }

            state.vote(user, voteItem, this);
        }
    }

    public class NormalVoteState : VoteState {
        public void vote(string user, string voteItem, VoteManager voteManager) {
            voteManager.getMapVote()[user] = voteItem;
            Console.WriteLine("恭喜你投票成功");
            
            voteManager.getMapState()[user] = new RepeatVoteState();
        }
    }

    public class RepeatVoteState : VoteState {
        public void vote(string user, string voteItem, VoteManager voteManager) {
            Console.WriteLine("請不要重複投票");

            if (voteManager.getMapVoteCount()[user] >= 4) {
                voteManager.getMapState()[user] = new SpiteVoteState();
            }
        } 
    }

    public class SpiteVoteState : VoteState {
        public void vote(string user, string voteItem, VoteManager voteManager) {
            string s = null;
            if (voteManager.getMapVote().TryGetValue(user,out s)) {
                voteManager.getMapVote().Remove(user);
            }
            
            Console.WriteLine("你有惡意刷票行爲,取消投票資格");

            if (voteManager.getMapVoteCount()[user] >= 7) {
                voteManager.getMapState()[user] = new BlackVoteState();
            }
        }
    }

    public class BlackVoteState : VoteState {
        public void vote(string user, string voteItem, VoteManager voteManager) {
            Console.WriteLine("進入黑名單,將禁止登陸和使用本系統");
                        
        }        
    }
}
View Code

那麼到底如何選擇這兩種方式呢?

  1.若是狀態轉換的規則是必定的,通常不須要進行什麼擴展規則,那麼就適合在上下文中統一進行狀態的維護

  2.若是狀態的轉換取決於前一個狀態動態處理的結果,或者是依賴於外部數據,爲了加強靈活性,這種狀況下,通常是在狀態處理類中進行狀態的維護

    18.3.3 使用數據庫來維護狀態

    18.3.4 模擬工做流

    18.3.5 狀態模式的優缺點

優勢

  1.簡化應用邏輯控制

  2.更好地分離狀態和行爲

  3.更好的擴展性

  4.顯式化進行狀態轉換

缺點

  1.狀態模式也有一個很明顯的缺點,一個狀態對應一個狀態處理類,會使得程序引入太多的狀態類,這樣程序變得雜亂

    18.3.6 思考狀態模式

Q:狀態模式的本質

A:根據狀態來分離和選擇行爲

 仔細分析狀態模式的結構,若是沒有上下文,那麼就退化回到只有接口和實現了,正式經過接口,把狀態和狀態對應的行爲分開,才使得經過狀態模式設計的程序易於擴展和維護

 而上下文主要負責的是公共的狀態驅動,每當狀態發生改變的時候,一般都是回調上下文來執行狀態對應的功能.固然,上下文自身也能夠維護狀態的變化,另外,上下文一般還會做爲多個狀態處理類之間的數據載體,在多個狀態處理類之間傳遞數據

Q:什麼時候選用狀態模式

A:  1.若是一個對象的行爲取決於它的狀態,並且它必須在運行時刻根據狀態來改變它的行爲,能夠使用狀態模式,來把狀態和行爲分離開,雖然分離開了,但狀態和行爲是有對應關係的,能夠在運行期間,經過改變狀態,就可以調用到該狀態對應的狀態處理對象上去,從而改變對象的行爲

   2.若是一個操做中含有龐大的多分支語句,並且這些分支依賴於該對象的狀態,能夠使用狀態模式,把各個分支的處理分散包裝到單獨的對象處理類中,這樣,這些分支對應的對象就能夠不依賴於其餘對象而獨立變化了

    18.3.7 相關模式

1.狀態模式和策略模式

2.狀態模式和觀察者模式

  這兩個模式乍一看,功能是很類似的,可是又有區別,能夠組合使用

  這兩個模式都是在狀態發生改變的時候觸發行爲,只不過觀察者模式的行爲是固定的,那就是通知全部的觀察者,而狀態模式是根據狀態來選擇不一樣的處理.從表面來看,兩個模式功能類似,觀察者模式中的被觀察對象就比如狀態模式中的上下文,觀察者模式中當被觀察對象的狀態發生改變的時候,觸發的通知全部觀察者的方法就比如是狀態模式中,根據狀態的變化選擇對應的狀態處理

  但實際這兩個模式是不一樣的,觀察則者模式的目的是在被觀察者的狀態發生改變的時候,觸發觀察者聯動,具體如何處理觀察者模式無論;而狀態模式的主要目的在於根據狀態來分離和選擇行爲,當狀態發生改變的時候,動態地改變行爲.

  這兩個模式是能夠組合使用的,好比在觀察者模式的觀察部分,當被觀察對象的狀態發生了改變,觸發通知了全部的觀察者之後,觀察者該怎麼處理呢?這個時候就能夠使用狀態模式,根據通知過來的狀態選擇相應的處理

3.狀態模式和單例模式

  這兩個模式能夠組合使用,能夠把狀態模式中的狀態處理類實現成單例

4.狀態模式和享元模式

  因爲狀態模式把狀態對應的行爲分散到多個狀態對象中,會形成不少細粒度的狀態對象,能夠把這些狀態處理對象經過享元模式來共享,從而節省資源

第19章 備忘錄模式(Memento)

  19.1 場景問題

    19.1.1 開發仿真系統

    19.1.2 不用模式的解決方案

using System;
using System.Collections;
using System.Collections.Generic;

namespace Test2 {
    public class Program {
        static void Main(string[] args) {
            FlowAMock mock = new FlowAMock("TestFlow");
            mock.runPhaseOne();

            int tempResult = mock.getTempResult();
            string tempState = mock.getTempState();
            
            mock.schema1();
            
            mock.setTempResult(tempResult);
            mock.setTempState(tempState);
            
            mock.schema2();
        }
    }

    public class FlowAMock {
        private string flowName;
        private int tempResult;
        private string tempState;

        public FlowAMock(string flowName) {
            this.flowName = flowName;
        }

        public string getTempState() {
            return tempState;
        }

        public void setTempState(string tempState) {
            this.tempState = tempState;
        }

        public int getTempResult() {
            return tempResult;
        }

        public void setTempResult(int tempResult) {
            this.tempResult = tempResult;
        }

        public void runPhaseOne() {
            tempResult = 3;
            tempState = "PhaseOne";
        }

        public void schema1() {
            tempState += ",Schema1";
            Console.WriteLine(tempState + " : now run " + tempResult);
            tempResult += 11;
        }

        public void schema2() {
            tempState += ",Schema2";
            Console.WriteLine(tempState + " : now run " + tempResult);
            tempResult += 22;
        }                
    }

}
View Code

    19.1.3 有何問題

Q:有何問題

A:上面的實現有一個不太理想的地方,那就是數據是一個一個零散着在外部存放的,若是須要外部存放的數據多了,會顯得很雜亂.這個容易解決,只須要定義一個數據對象來封裝這些須要外部存放的數據就能夠了

 還有一個嚴重的問題,那就是,爲了把運行期間的數據放到外部存儲起來,模擬流程的對象被迫把內部數據結構開放出來,這暴露了對象的實現細節,並且也破壞了對象的封裝性.原本這些數據只是模擬流程的對象內部數據,應該是不對外的

  19.2 解決方案

    19.2.1 使用備忘錄模式來解決問題

Q:備忘錄模式的定義

A:在不破壞封裝性的前提下,捕獲一個對象的內部狀態,並在該對象以外保存這個狀態.這樣之後就可將該對象恢復到原先保存的狀態

 一個備忘錄是一個對象,它存儲另外一個對象在某個瞬間的內部狀態,後者被稱爲備忘錄的原發器

備忘錄模式引入一個存儲狀態的備忘錄對象,爲了讓外部沒法訪問這個對象的值,通常把這個對象實現成爲須要保存數據的對象的內部類,一般仍是私有的,這樣一來,除了這個須要保存數據的對象,外部沒法訪問到這個備忘錄對象的數據,這就保證了對象的封裝性不被破壞

可是這個備忘錄對象須要存儲在外部.爲了不讓外部訪問到這個對象內部的數據,備忘錄模式引入了一個備忘錄對象的窄接口,這個接口通常是空的,什麼方法都沒有,這樣外部存儲的地方,只是知道存儲了一些備忘錄接口的對象,可是因爲接口是空的,它們沒法經過接口去訪問備忘錄對象內部的數據

    19.2.2 備忘錄模式的結構和說明

  [Memento] 備忘錄.主要用來存儲原發器對象的內部狀態,可是具體須要存儲哪些數據是由原發器對象來決定的.另外備忘錄應該只能由原發器對象來訪問它內部的數據,原發器外部的對象不該該訪問到備忘錄對象的內部數據

  [Originator] 原發器,使用備忘錄來保存某個時刻原發器自身的狀態,也能夠使用備忘錄來恢復內部狀態

  [Caretaker] 備忘錄管理者,或者稱爲備忘錄負責人.主要負責保存備忘錄對象,可是不能對備忘錄對象的內容進行操做或檢查

    19.2.3 備忘錄模式示例代碼

using System;
using System.Collections.Generic;

namespace ConsoleApplication1 {
    public class Program {
        public static void Main() {

        }

    }

    public interface Memento {
        
    }

    // 原發器
    public class Originator {
        private string state = "";

        public Memento createMemento() {
            return new MementoImpl(state);
        }

        public void setMemento(Memento memento) {
            MementoImpl mementoImpl = (MementoImpl) memento;
            state = mementoImpl.getState();
        }


        private class MementoImpl : Memento {
            private string state = "";

            public MementoImpl(string state) {
                this.state = state;
            }

            public string getState() {
                return state;
            }
        }
    }

    // 負責保存備忘錄
    public class Caretaker {
        private Memento memento = null;

        public void saveMemento(Memento memento) {
            this.memento = memento;
        }

        public Memento retriveMemento() {
            return this.memento;
        }
    }

}
View Code

    19.2.4 使用備忘錄模式重寫示例

using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;

namespace ConsoleApplication1 {
    public class Program {
        public static void Main() {
            FlowAMock mock = new FlowAMock("TestFlow");
            mock.runPhaseOne();
            
            FlowAMementoCareTaker careTaker = new FlowAMementoCareTaker();

            FlowAMockMemento memento = mock.createMemento();
            careTaker.saveMemento(memento);
            
            mock.schema1();
            
            mock.setMemento(careTaker.retriveMemento());
            
            mock.schema2();
        }

    }

    public interface FlowAMockMemento {
        
    }

    public class FlowAMock {
        private string flowName;
        private int tempResult;
        private string tempState;

        public FlowAMock(string flowName) {
            this.flowName = flowName;
        }

        // 階段一
        public void runPhaseOne() {
            tempResult = 3;
            tempState = "PhaseOne";
        }

        // 階段一後的方案一
        public void schema1() {
            tempState += ",Schema1";
            Console.WriteLine(tempState + " : now run " + tempResult);
            tempResult += 11;
        }

        // 階段一後的方案二
        public void schema2() {
            tempState += ",Schema2";
            Console.WriteLine(tempState + " : now run " + tempResult);
            tempResult += 22;
        }

        public FlowAMockMemento createMemento() {
            return new MementoImpl(tempResult,tempState);
        }

        public void setMemento(FlowAMockMemento memento) {
            MementoImpl mementoImpl = (MementoImpl) memento;
            tempResult = mementoImpl.getTempResult();
            tempState = mementoImpl.getTempState();
        }
        
        // 真正的備忘錄對象,
        private class MementoImpl : FlowAMockMemento {
            private int tempResult;
            private string tempState;
            
            public MementoImpl(int tempResult,string tempState) {
                this.tempResult = tempResult;
                this.tempState = tempState;
            }

            public int getTempResult() {
                return tempResult;
            }

            public string getTempState() {
                return tempState;
            }
        }
    }
    
    public class FlowAMementoCareTaker {
        private FlowAMockMemento memento;

        public void saveMemento(FlowAMockMemento memento) {
            this.memento = memento;
        }

        public FlowAMockMemento retriveMemento() {
            return this.memento;
        }
    }
    
    

}
View Code

  19.3 模式講解

    19.3.1 認識備忘錄模式

Q:備忘錄模式的功能

A:備忘錄模式的功能,首先是在不破壞封裝性的前提下,捕獲一個對象的內部狀態.這裏要注意兩點,一個是不破壞封裝性,也就是對象不能暴露它不該該暴露的細節;另一個是捕獲的是對象的內部狀態,並且一般仍是運行期間某個時刻對象的內部狀態

 爲何要捕獲這個對象的內部狀態呢?捕獲這個內部狀態有什麼用呢?

 是爲了在之後的某個時候,將該對象的狀態恢復到備忘錄所保存的狀態,這纔是備忘錄真正的目的.前面保存狀態就是爲了後面恢復,雖然不是必定要恢復,可是目的是爲了恢復,這也是不少人理解備忘錄模式的時候,忽視掉的地方,它們太關注備忘,而忽視了恢復,這是不全面的理解

 捕獲的狀態存放在哪裏

 備忘錄模式中,捕獲的內部狀態存儲在備忘錄對象中,而備忘錄對象一般會被存儲在原發器對象以外,也就是被保存狀態的對象的外部,一般是存放在管理者對象那裏

 備忘錄對象

 在備忘錄模式中,備忘錄對象一般就是用來記錄原發器須要保存的狀態的對象,簡單點的實現,也就是封裝數據的對象

 可是備忘錄對象和普通的封裝數據的對象仍是有區別的,主要是備忘錄對象通常只讓原發器對象來操做,而不是像普通的封裝數據的對象那樣,誰均可以使用.爲了保證這一點,一般會把備忘錄對象做爲原發器對象的內部類來實現,並且實現成私有的,這就斷絕了外部來訪問這個備忘錄對象的途徑

 備忘錄對象須要保存在原發器對象以外,爲了與外部交互,一般備忘錄對象都會實現一個窄接口,來標識對象的類型.

 原發器對象

 原發器對象就是須要被保存狀態的對象,也是有可能須要恢復狀態的對象,原發器通常會包含備忘錄對象的實現

 一般原發器對象應該提供捕獲某個時刻對象內部狀態的方法,在這個方法中,原發器對象會建立備忘錄對象,把須要保存的狀態數據設置到備忘錄對象中,而後把備忘錄對象提供給管理者對象來保存

 固然,原發器對象也應該提供這樣的方法:按照外部要求來恢復內部狀態到某個備忘錄對象記錄的狀態

 管理者對象

   在備忘錄模式中,管理者對象主要是負責保存備忘錄對象

    1.並不必定要特別的作出一個管理者對象來,廣義地說,調用原發器得到備忘錄對象後,備忘錄對象放在哪裏,哪一個對象就能夠算是管理者對象

    2.管理者對象並非只能管理一個備忘錄對象,一個管理者對象能夠管理不少的備忘錄對象,

    3.狹義的管理者對象只是管理同一類的備忘錄對象,但廣義的管理者對象是能夠管理不一樣類型的備忘錄對象的

    4.管理者對象須要實現的基本功能主要是:存入備忘錄對象,保存備忘錄對象和獲取備忘錄對象.若是從功能上看,就是一個緩存功能的實現,或者是一個簡單的對象實例池的實現

    5.管理者雖然能存取備忘錄對象,可是不能訪問備忘錄對象內部的數據

  窄接口和寬接口

    1.窄接口:管理者只能看到備忘錄的窄接口,窄接口的實現中一般沒有任何的方法,只是一個類型標識,窄接口使得管理者只能將備忘錄傳遞給其餘對象

    2.寬接口:原發器可以看到一個寬接口,容許它訪問所需的全部數據,來返回到先前的狀態.理想狀況是:只容許生成備忘錄的原發器來訪問該備忘錄的內部狀態,一般實現成爲原發器內的一個私有內部類

    19.3.2 結合原型模式

    19.3.3 離線存儲

    19.3.4 再次實現可撤銷操做

    19.3.5 備忘錄模式的優缺點

優勢

  1.更好的封裝性

  2.簡化了原發器

  3.窄接口和寬接口

缺點

  1.可能會致使高開銷

    19.3.6 思考備忘錄模式

Q:備忘錄模式的本質

A:保存和恢復內部狀態

 根據備忘錄模式的本質,從廣義上講,進行數據庫存取操做;或者是Web應用中的request,session,servletContext等的attribugte數據存取;更進一步,大多數基於緩存功能的數據操做均可以視爲廣義的備忘錄模式.不過廣義到這個地步,還提備忘錄模式已經沒有什麼意義了.因此對於備忘錄模式仍是多從狹義上來講

Q:什麼時候選用備忘錄模式

A:  1.若是必須保存一個對象在某一個時刻的所有或者部分狀態,方便在之後須要的時候,能夠把該對象恢復到先前的狀態,能夠使用備忘錄模式.使用備忘錄對象來封裝和保存須要保存的內部狀態,而後把備忘錄對象保存到管理者對象中,在須要的時候,再從管理者對象中獲取備忘錄對象,來恢復對象的狀態

   2.若是須要保存一個對象的內部狀態,可是若是用接口來讓其餘對象直接獲得這些須要保存的狀態,將會暴露對象的實現細節並破壞對象的封裝性,這時能夠使用備忘錄模式,把備忘錄對象實現成爲原發器對象的內部類,並且仍是私有的,從而保證只有原發器對象才能訪問該備忘錄對象.這樣既保存了須要保存的狀態,又不會暴露原發器對象的內部實現細節

    19.3.7 相關模式

1.備忘錄模式和命令模式

  這兩個模式能夠組合使用

  命令模式實現中,在實現命令的撤銷和重作的時候,能夠使用備忘錄模式,在命令操做的時候記錄下操做先後的狀態,而後在命令撤銷和重作的時候,直接使用相應的備忘錄對象來恢復狀態就能夠了

  在這種撤銷的執行順序和重作的執行順序可控的狀況下,備忘錄對象還能夠採用增量式記錄的方式,有效減小緩存的數據量

2.備忘錄模式和原型模式

  這兩個模式能夠組合使用

  在原發器對象建立備忘錄對象的時候,若是原發器對象中所有或者大部分的狀態都須要保存,一個簡潔的方式就是直接克隆一個原發器對象.也就是說,這個時候備忘錄對象裏面存放的是一個原發器對象的實例

第20章 享元模式(Flyweight)

  20.1 場景問題

    20.1.1 加入權限控制

幾乎全部的權限系統都分紅兩個部分,一個是受權部分,一個是驗證部分,爲了理解它們,首先解釋兩個基本的名詞

  1.安全實體:就是被權限系統檢測的對象,好比工資數據

  2.權限:就是須要被校驗的權限對象,好比查看,修改等

安全實體和權限一般要一塊兒描述纔有意義,好比有這樣一個描述,"如今要檢測登陸人員對工資數據是否有查看的權限","工資數據"就是安全實體,"查看"就是權限

  1.受權:是指把對某些安全實體的某些權限分配給某些人員的過程

  2.驗證:是指判斷某我的員對某個安全實體是否擁有某個或某些權限的過程

權限的另外兩個特徵

  1.權限的繼承性:若是多個安全實體存在包含關係,而某個安全實體沒有相應的權限限制,那麼它會繼承包含它的安全實體的相應權限

  2.權限的最近匹配原則:若是多個安全實體存在包含關係,而某個安全實體沒有相應的權限限制,那麼它會向上尋找並匹配相應的權限限制,直到找到一個離這個安全實體最近的擁有相應權限限制的安全實體爲止.若是把整個層次結構都尋找完了仍沒有匹配到相應權限限制的話,那就說明全部人對這個安全實體都擁有這個相應的權限限制.

    20.1.2 不使用模式的解決方案

using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Threading;


namespace ConsoleApplication1 {
    public class Program {
        public static void Main() {
            SecurityMgr mgr = SecurityMgr.getInstance();
            mgr.login("張三");
            mgr.login("李四");

            bool f1 = mgr.hasPermit("張三", "薪資數據", "查看");
            bool f2 = mgr.hasPermit("李四", "薪資數據", "查看");

            Console.WriteLine("f1 == " + f1);
            Console.WriteLine("f2 == " + f2);

            for (int i = 0; i < 3; i++) {
                mgr.login("張三" + i);
                mgr.hasPermit("張三" + i, "薪資數據", "查看");
            }
        }

    }

    public class TestDB {
        public static List<string> colDB = new List<string>();

        static TestDB() {
            colDB.Add("張三,人員列表,查看");
            colDB.Add("李四,人員列表,查看");
            colDB.Add("李四,薪資數據,查看");
            colDB.Add("李四,薪資數據,修改");

            for (int i = 0; i < 3; i++) {
                colDB.Add("張三" + i + ",人員列表,查看");
            }
        }
    }

    public class AuthorizationModel {
        private string user;
        private string securityEntity;
        private string permit;

        public string getUser() {
            return user;
        }

        public void setUser(string user) {
            this.user = user;
        }

        public string getSecurityEntity() {
            return securityEntity;
        }

        public void setSecurityEntity(string securityEntity) {
            this.securityEntity = securityEntity;
        }

        public string getPermit() {
            return permit;
        }

        public void setPermit(string permit) {
            this.permit = permit;
        }
    }

    public class SecurityMgr {
        private static SecurityMgr securityMgr = new SecurityMgr();
        private Dictionary<string,List<AuthorizationModel>> map = new Dictionary<string, List<AuthorizationModel>>();
        
        private SecurityMgr() {
            
        }

        public static SecurityMgr getInstance() {
            return securityMgr;
        }

        public void login(string user) {
            List<AuthorizationModel> col = queryByUser(user);
            map.Add(user, col);
        }

        public bool hasPermit(string user, string securityEntity, string permit) {
            List<AuthorizationModel> col;
            if (!map.TryGetValue(user, out col) || col.Count == 0) {
                Console.WriteLine(user + "沒有登陸或是沒有被分配任何權限");
                return false;
            }

            foreach (AuthorizationModel authorizationModel in col) {
                Console.WriteLine("am == " + authorizationModel);
                if (authorizationModel.getSecurityEntity() == securityEntity && authorizationModel.getPermit() == permit) {
                    return true;
                }
            }

            return false;
        }


        private List<AuthorizationModel> queryByUser(string user) {
            List<AuthorizationModel> col = new List<AuthorizationModel>();

            foreach (string s in TestDB.colDB) {
                string[] ss = s.Split(',');
                if (ss[0] == user) {
                    AuthorizationModel am = new AuthorizationModel();
                    am.setUser(ss[0]);
                    am.setSecurityEntity(ss[1]);
                    am.setPermit(ss[2]);

                    col.Add(am);
                }
            }

            return col;
        }
    }
    
    

}
View Code

    20.1.3 有何問題

Q:有何問題

A:在系統當中,存在大量的細粒度對象,並且存在大量的重複數據,嚴重耗費內存,如何解決呢

  20.2 解決方案

    20.2.1 使用享元模式來解決問題

Q:享元模式的定義

A:運用共享技術有效地支持大量細粒度的對象

Q:應用享元模式來解決的思路

A:須要分離出被緩存對象實例中,那些數據是不變且重複出現的,那些數據是常常變化的,真正應該被緩存的數據是那些不變且重複出現的數據,把它們稱爲對象的內部狀態,而那些變化的數據就不緩存了,把它們稱爲對象的外部狀態

 這樣在實現的時候,把內部狀態分離出來共享,稱之爲享元,經過共享享元對象來減小對內存的佔用.把外部狀態分離出來,放到外部,讓應用在使用的時候進行維護,並在須要的時候傳遞給享元對象使用.爲了控制對內部狀態的共享,而且讓外部能簡單地使用共享數據,提供一個工廠來管理享元,把它稱爲享元工廠

    20.2.2 享元模式的結構和說明

  [Flyweight] 享元接口,經過這個接口Flyweight能夠接受並做用於外部狀態,經過這個接口傳入外部的狀態,在享元對象的方法處理中可能會使用這些外部的數據

  [ConcreteFlyweight] 具體的享元實現對象,必須是可共享的,須要封裝Flyweight的內部狀態

  [UnsharedConcreteFlyweight] 非共享的享元實現對象,並非全部的Flyweight實現對象都須要共享,非共享的享元實現對象一般是對共享享元對象的組合對象.

  [FlyweightFactory] 享元工廠,主要用來建立並管理共享的享元對象,並對外提供訪問共享享元的接口

  [Client] 享元客戶端,主要的工做是維持一個對Flyweight的引用,計算或存儲享元對象的外部狀態,固然這裏能夠訪問共享和不共享的Flyweight對象.

    20.2.3 享元模式示例代碼

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;

namespace Test2 {
    public class Program {
        static void Main(string[] args) {
            SecurityMgr mgr = SecurityMgr.getInstance();
            mgr.login("張三");
            mgr.login("李四");

            bool f1 = mgr.hasPermit("張三", "薪資數據", "查看");
            bool f2 = mgr.hasPermit("李四", "薪資數據", "查看");

            Console.WriteLine("f1 == " + f1);
            Console.WriteLine("f2 == " + f2);

            for (int i = 0; i < 3; i++) {
                mgr.login("張三" + i);
                mgr.hasPermit("張三" + i, "薪資數據", "查看");
            }
        }
    }

    public class TestDB {
        public static List<string> colDB = new List<string>();

        static TestDB() {
            colDB.Add("張三,人員列表,查看");
            colDB.Add("李四,人員列表,查看");
            colDB.Add("李四,薪資數據,查看");
            colDB.Add("李四,薪資數據,修改");

            for (int i = 0; i < 3; i++) {
                colDB.Add("張三" + i + ",人員列表,查看");
            }
        }
    }

    public class AuthorizationModel {
        private string user;
        private string securityEntity;
        private string permit;

        public string getUser() {
            return user;
        }

        public void setUser(string user) {
            this.user = user;
        }

        public string getSecurityEntity() {
            return securityEntity;
        }

        public void setSecurityEntity(string securityEntity) {
            this.securityEntity = securityEntity;
        }

        public string getPermit() {
            return permit;
        }

        public void setPermit(string permit) {
            this.permit = permit;
        }
    }

    public class SecurityMgr {
        private static SecurityMgr securityMgr = new SecurityMgr();
        private Dictionary<string,List<AuthorizationModel>> map = new Dictionary<string, List<AuthorizationModel>>();
        
        private SecurityMgr() {
            
        }

        public static SecurityMgr getInstance() {
            return securityMgr;
        }

        public void login(string user) {
            List<AuthorizationModel> col = queryByUser(user);
            map.Add(user, col);
        }

        public bool hasPermit(string user, string securityEntity, string permit) {
            List<AuthorizationModel> col;
            if (!map.TryGetValue(user, out col) || col.Count == 0) {
                Console.WriteLine(user + "沒有登陸或是沒有被分配任何權限");
                return false;
            }

            foreach (AuthorizationModel authorizationModel in col) {
                Console.WriteLine("am == " + authorizationModel + " " + authorizationModel.GetHashCode());
                if (authorizationModel.getSecurityEntity() == securityEntity && authorizationModel.getPermit() == permit) {
                    return true;
                }
            }

            return false;
        }


        private List<AuthorizationModel> queryByUser(string user) {
            List<AuthorizationModel> col = new List<AuthorizationModel>();

            foreach (string s in TestDB.colDB) {
                string[] ss = s.Split(',');
                if (ss[0] == user) {
                    AuthorizationModel am = new AuthorizationModel();
                    am.setUser(ss[0]);
                    am.setSecurityEntity(ss[1]);
                    am.setPermit(ss[2]);

                    col.Add(am);
                }
            }

            return col;
        }
    }

}
View Code

    20.2.4 使用享元模式重寫示例

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;

namespace Test2 {
    public class Program {
        static void Main(string[] args) {
            SecurityMgr mgr = SecurityMgr.getInstance();
            mgr.login("張三");
            mgr.login("李四");

            bool f1 = mgr.hasPermit("張三", "薪資數據", "查看");
            bool f2 = mgr.hasPermit("李四", "薪資數據", "查看");

            Console.WriteLine("f1 == " + f1);
            Console.WriteLine("f2 == " + f2);

            for (int i = 0; i < 3; i++) {
                mgr.login("張三" + i);
                mgr.hasPermit("張三" + i, "薪資數據", "查看");
            }
        }
    }

    public class TestDB {
        public static List<string> colDB = new List<string>();

        static TestDB() {
            colDB.Add("張三,人員列表,查看");
            colDB.Add("李四,人員列表,查看");
            colDB.Add("李四,薪資數據,查看");
            colDB.Add("李四,薪資數據,修改");

            for (int i = 0; i < 3; i++) {
                colDB.Add("張三" + i + ",人員列表,查看");
            }
        }
    }

    public interface Flyweight {
        bool match(string securityEntity, string permit);
    }

    public class AuthorizationFlyweight : Flyweight {
        private string securityEntity;
        private string permit;

        public AuthorizationFlyweight(string state) {
            string[] ss = state.Split(',');
            securityEntity = ss[0];
            permit = ss[1];
        }

        public string getSecurityEntity() {
            return securityEntity;
        }

        public string getPermit() {
            return permit;
        }

        public bool match(string securityEntity, string permit) {
            if (this.securityEntity == securityEntity && this.permit == permit) {
                return true;
            }

            return false;
        }
    }

    public class FlyweightFactory {
        private static FlyweightFactory factory = new FlyweightFactory();
        private Dictionary<string,Flyweight> fsMap = new Dictionary<string, Flyweight>();

        private FlyweightFactory() {
            
        }

        public static FlyweightFactory getInstance() {
            return factory;
        }

        public Flyweight getFlyweight(string key) {
            Flyweight f;
            if (!fsMap.TryGetValue(key, out f)) {
                f = new AuthorizationFlyweight(key);
                fsMap[key] = f;
            }

            return f;
        }
    }

    // 充當 Client 角色
    public class SecurityMgr {
        private static SecurityMgr securityMgr = new SecurityMgr();
        private Dictionary<string,List<Flyweight>> map = new Dictionary<string, List<Flyweight>>();
        
        private SecurityMgr() {
            
        }

        public static SecurityMgr getInstance() {
            return securityMgr;
        }

        public void login(string user) {
            List<Flyweight> col = queryByUser(user);
            map[user] = col;
        }

        public bool hasPermit(string user, string securityEntity, string permit) {
            List<Flyweight> col;
            if (!map.TryGetValue(user, out col) || col.Count == 0) {
                Console.WriteLine(user + "沒有登陸或是沒有被分配任何權限");
                return false;
            }

            foreach (Flyweight flyweight in col) {
                Console.WriteLine("fm == " + flyweight + " " + flyweight.GetHashCode());
                if (flyweight.match(securityEntity, permit)) {
                    return true;
                }
            }

            return false;
        }

        private List<Flyweight> queryByUser(string user) {
            List<Flyweight> col = new List<Flyweight>();
            foreach (string s in TestDB.colDB) {
                string[] ss = s.Split(',');
                if (ss[0] == user) {
                    Flyweight fm = FlyweightFactory.getInstance().getFlyweight(ss[1] + "," + ss[2]);
                    col.Add(fm);
                }
            }

            return col;
        }
    }

}
View Code

  20.3 模式講解

    20.3.1 認識享元模式

Q:變與不變

A:享元模式設計的重點就在於分離變與不變.把一個對象的狀態分紅內部狀態和外部狀態,內部狀態是不變的,外部狀態是可變的.而後經過共享不變的部分,達到減小對象數量並節約內存的目的.在享元對象須要的時候,能夠從外部傳入外部狀態給共享的對象,共享對象會在功能處理的時候,使用本身內部的狀態和這些外部的狀態

Q:內部狀態和外部狀態

A:享元模式的內部狀態,一般指的是包含在享元對象內部的,對象自己的狀態,是獨立於使用享元的場景的信息,通常建立後就再也不變化的狀態,所以能夠共享

 外部狀態指的是享元對象以外的狀態,取決於使用享元的場景,會根據使用場景而變化,所以不可共享.若是享元對象須要這些外部狀態的話,能夠從外部傳遞到享元對象中,好比經過方法的參數來傳遞

 也就是說享元模式真正緩存和共享的數據是享元的內部狀態,而外部狀態是不該該被緩存共享的

 還有一點,內部狀態和外部狀態是獨立的,外部狀態的變化不該該影響到內部狀態

Q:實例池

A:所謂實例池,指的是緩存和管理對象實例的程序,一般實例池會提供對象實例的運行環境,並控制對象實例的生命週期

    20.3.2 不須要共享的享元模式

    20.3.3 對享元對象的管理

    20.3.4 享元模式的優缺點

優勢

  1.減小對象數量,節省內存空間

缺點

  1.維護共享對象,須要額外開銷

    20.3.5 思考享元模式

Q:享元模式的本質

A:分離與共享

 分離的是對象狀態中變與不變的部分,共享的是對象中不變的部分.享元模式的關鍵之處就在於分離變與不變,把不變的部分做爲享元對象的內部狀態,而變化部分則做爲外部狀態,由外部來維護,這樣享元對象就可以被共享,從而減小對象數量,並節省大量的內存空間

 理解了這個本質後,在使用享元模式的時候,就會考慮,那些狀態須要分離?如何分離?分離後如何處理?哪些須要共享?如何管理共享的對象?外部如何使用共享的享元對象?是否須要不共享的對象?等等

 把這些問題都思考清楚,找到相應的解決方法,那麼享元模式也就應用起來了,多是標準的應用,也多是變形的應用,但萬變不離其宗

Q:什麼時候選用享元模式

A:  1.若是一個應用程序使用了大量的細粒度對象,能夠使用享元模式來減小對象數量.

   2.若是因爲使用大量的對象,形成很大的存儲開銷,能夠使用享元模式來減小對象數量,並節約內存

   3.若是對象的大多數狀態均可以轉變爲外部狀態,好比經過計算獲得,或是從外部傳入等,能夠使用享元模式l來實現內部狀態和外部狀態的分離

   4.若是不考慮對象的外部狀態,能夠用相對較少的共享對象取代不少組合對象,能夠使用享元模式來共享對象,而後組合對象來使用這些共享對象

    20.3.6 相關模式

1.享元模式與單例模式

  這兩個模式能夠組合使用

  一般狀況下,享元模式中的享元工廠能夠實現成爲單例.另外,享元工廠中緩存的享元對象,都是單實例的, 能夠當作是單例模式的一種變形控制,在享元工廠中來單例享元模式

2.享元模式與組合模式

  這兩個模式能夠組合使用

  在享元模式中,存在不須要共享的享元實現,這些不須要共享的享元一般是對共享的享元對象的組合對象.也就是說,享元模式一般會和組合模式組合使用,來實現更復雜的對象層次結構

3.享元模式與狀態模式

  這兩個模式能夠組合使用

  能夠使用享元模式來共享狀態模式中的狀態對象.一般在狀態模式中,會存在數量很大的,細粒度的狀態對象,並且它們基本上都是能夠重複使用的,都是用來處理某一個固定的狀態的,它們須要的數據一般都是由上下文傳入,也就是變化部分都分離出去了,因此能夠用享元模式來實現這些狀態對象

4.享元模式與策略模式

  這兩個模式能夠組合使用

  能夠使用享元模式來實現策略模式中的策略對象.和狀態模式同樣,在策略模式中也存在大量細粒度的策略對象,它們須要的數據一樣是從上下文傳入的,因此能夠使用享元模式來實現這些策略對象

第21章 解釋器模式(Interpreter)

  21.1 場景問題

    21.1.1 讀取配置文件

    21.1.2 不用模式的解決方案

<?xml version="1.0" encoding="utf-8"?>
<root>
    <jdbc>
        <driver-class>驅動類名</driver-class>
        <url>鏈接數據庫的URL</url>
        <user>鏈接數據庫的用戶名</user>
        <password>鏈接數據庫的密碼</password>
    </jdbc>
    <application-xml>缺省讀取的Spring配置的文件名稱</application-xml>
</root>
View Code
using System;
using System.Collections;
using System.Collections.Generic;
using System.Xml;

namespace ConsoleApplication1 {
    public class Program {
        public static void Main() {
            ReadAppXml rax = new ReadAppXml();;
            rax.read(@"test1.xml");
        }

    }

    public class ReadAppXml {
        public void read(string filePathName) {
            XmlDocument doc = new XmlDocument();
            doc.Load(filePathName);

            XmlNode root = doc.SelectSingleNode("root");
            XmlNode jdbc = root.SelectSingleNode("jdbc");
            
            XmlNode driverClass = jdbc.SelectSingleNode("driver-class");
            Console.WriteLine("driverClass == " + driverClass.InnerText);
            
            XmlNode urlNode = jdbc.SelectSingleNode("url");
            Console.WriteLine("url == " + urlNode.InnerText);
            
            XmlNode userNode = jdbc.SelectSingleNode("user");
            Console.WriteLine("user == " + userNode.InnerText);
            
            XmlNode passwordNode = jdbc.SelectSingleNode("password");
            Console.WriteLine("password == " + passwordNode.InnerText);
            
            XmlNode applicationXmlNode = root.SelectSingleNode("application-xml");
            Console.WriteLine("applicationXml == " + applicationXmlNode.InnerText);
        }
    }

}
View Code

    21.1.3 有何問題

Q:有何問題

A:當xml的結構發生改變後,可以很方便地獲取相應元素或者是屬性的值,而不用再去修改解析xml的程序

  21.2 解決方案

    21.2.1 使用解釋器模式來解決問題

Q:解釋器模式的定義

A:給定一個語言,定義它的文法的一種表示,並定義一個解釋器,這個解釋器使用該表示來解釋語言中的句子

Q:應用解釋器模式來解決問題的思路

A:想要解決當xml的結構發生改變後,不用修改解析部分的代碼,一個天然的思路就是要把解析部分的代碼寫成公共的,並且還要是通用的,可以知足各類xml取值的須要.

 要解決這些問題,其中的一個解決方案就是解釋器模式.在描述這個模式的解決思路以前,先解釋兩個概念,一個是解析器(不是指xml的解析器),另外一個是解釋器

 1.這裏的解析器,指的是把描述客戶端調用要求的表達式,通過解析,造成一個抽象語法樹的程序,不是指xml的解析器

 2.這裏的解釋器,指的是解釋抽象語法樹,並執行每一個節點對應的功能的程序

 要解決通用解析xml的問題,第一步:須要先設計一個簡單的表達式語言,在客戶端調用解析程序的時候,傳入用這個表達式語言描述的一個表達式,而後把這個表達式經過解析器的解析,造成一個抽象的語法樹

 第二步:解析完成後,自動調用解釋器來解釋抽象語法樹,並執行每一個節點所對應的功能,從而完成通用的xml解析

 這樣一來,每次當xml結構發生了更改,也就是在客戶端調用的時候,傳入不一樣的表達式便可,整個解析xml過程的代碼都不須要再修改了

    21.2.2 解釋器模式的結構和說明

  [AbstractExpression] 定義解釋器的接口,約定解釋器的解釋操做

  [TerminalExpression] 終結符解釋器,用來實現語法規則中和終結符相關的操做,再也不包含其餘的解釋器,若是用組合模式來構建抽象語法樹的話,就至關於組合模式中的葉子對象,能夠有多種終結符解釋器

  [NonterminalExpression] 非終結符解釋器,用來實現語法規則中非終結符相關的操做,一般一個解釋器對應一個語法規則,能夠包含其餘的解釋器,若是用組合模式來構建抽象語法樹的話,就至關於組合模式中的組合對象.能夠有多種非終結符解釋器

  [Context] 上下文,一般包含各個解釋器須要的數據或是公共的功能

  [Client] 客戶端,指的是使用解釋器的客戶端,一般在這裏將按照語言的語法作的表達轉換成爲使用解釋器對象描述的抽象語法樹,而後調用解釋操做

    21.2.3 解釋器模式示例代碼

using System;
using System.Collections;
using System.Collections.Generic;


namespace ConsoleApplication1 {
    public class Program {
        public static void Main() {

        }

    }

    public class Context {
        
    }
    
    public abstract class AbstractExpression {
        public abstract void interpret(Context ctx);
    }

    public class TerminalExpression : AbstractExpression {
        public override void interpret(Context ctx) {
            
        }
    }

    public class NonterminalExpression : AbstractExpression {
        public override void interpret(Context ctx) {
            
        }
    }

    public class Client {
        
    }
    
}
View Code

    21.2.4 使用解釋器模式重寫代碼

using System;
using System.Collections;
using System.Collections.Generic;
using System.Xml;

namespace Test2 {
    public class Program {
        static void Main(string[] args) {
            Context c = new Context("test1.xml");

            ElementExpression root = new ElementExpression("root");
            ElementExpression aEle = new ElementExpression("a");
            ElementExpression bEle = new ElementExpression("b");
            ElementTerminalExpression cEle = new ElementTerminalExpression("c");

            root.addEle(aEle);
            aEle.addEle(bEle);
            bEle.addEle(cEle);

            string[] ss = root.interpret(c);
            Console.WriteLine("c的值是 = " + ss[0]);
        }
    }

    
    
    public abstract class ReadXmlExpression {
        public abstract string[] interpret(Context c);
    }

    public class ElementExpression : ReadXmlExpression {
        private List<ReadXmlExpression> eles = new List<ReadXmlExpression>();
        private string eleName = "";

        public ElementExpression(string eleName) {
            this.eleName = eleName;
        }

        public bool addEle(ReadXmlExpression ele) {
            this.eles.Add(ele);
            return true;
        }

        public bool removeEle(ReadXmlExpression ele) {
            this.eles.Remove(ele);
            return true;
        }

        public override string[] interpret(Context c) {
            XmlElement pEle = c.getPreEle();
            if (pEle == null) {
                c.setPreEle(c.getDocument().DocumentElement);
            } else {
                XmlElement nowEle = c.getNowEle(pEle, eleName);
                c.setPreEle(nowEle);
            }

            string[] ss = null;
            foreach (ReadXmlExpression readXmlExpression in eles) {
                ss = readXmlExpression.interpret(c);
            }

            return ss;
        }
    }

    public class ElementTerminalExpression : ReadXmlExpression {
        private string eleName = "";

        public ElementTerminalExpression(string name) {
            this.eleName = name;
        }

        public override string[] interpret(Context c) {
            XmlElement pEle = c.getPreEle();
            XmlElement ele = null;
            if (pEle == null) {
                ele = c.getDocument().DocumentElement;
            } else {
                ele = c.getNowEle(pEle, eleName);
                c.setPreEle(ele);
            }

            string[] ss = new string[1];
            ss[0] = ele.FirstChild.Value;
            return ss;
        }
    }

    public class PropertyTerminalExpression : ReadXmlExpression {
        private string propName;

        public PropertyTerminalExpression(string propName) {
            this.propName = propName;
        }

        public override string[] interpret(Context c) {
            string[] ss = new string[1];
            ss[0] = c.getPreEle().GetAttribute(this.propName);
            return ss;
        }
    }

    public class Context {
        private XmlElement preEle;
        private XmlDocument document;

        public Context(string filePathName) {
            this.document = XmlUtil.getRoot(filePathName);
        }

        public void reInit() {
            preEle = null;
        }

        public XmlElement getNowEle(XmlElement pEle, string eleName) {
            XmlNodeList tempNodeList = pEle.ChildNodes;
            for (int i = 0; i < tempNodeList.Count; i++) {
                if (tempNodeList.Item(i) is XmlElement) {
                    XmlElement nowEle = (XmlElement) tempNodeList.Item(i);
                    if (nowEle.Name == eleName) {
                        return nowEle;
                    }
                }
            }
            
            return null;
        }

        public XmlElement getPreEle() {
            return preEle;
        }

        public void setPreEle(XmlElement preEle) {
            this.preEle = preEle;
        }

        public XmlDocument getDocument() {
            return document;
        }
    }

    public class XmlUtil {
        public static XmlDocument getRoot(string filePathName) {
            XmlDocument doc = new XmlDocument();
            doc.Load(filePathName);

            return doc;
        }
    }
}
View Code

  21.3 模式講解

    21.3.1 認識解釋器模式

    21.3.2 讀取多個元素或屬性的值

    21.3.3 解析器

    21.3.4 解釋器模式的優缺點

優勢

  1.易於實現語法

    在解釋器模式中,一條語法規則用一個解釋器對象來解釋執行.對於解釋器的實現來說,功能就變得比較簡單,只須要考慮這一條語法規則的實現就能夠了.其餘的都不用管

  2.易於擴展新的語法

    正是因爲採用一個解釋器對象負責一條語法規則的方式,使得擴展新的語法很是容易.擴展了新的語法,只須要建立相應的解釋器對象,在建立抽象語法樹的時候使用這個新的解釋器對象就能夠了

缺點

  1.解釋器模式的缺點是不適合複雜的語法

    若是語法特別複雜,構建解釋器模式須要的抽象語法樹的工做是很是艱鉅的,再加上有可能會須要構建多個抽象語法樹.因此解釋器模式不太適合於複雜的語法,對於複雜的語法,使用語法分析程序或編譯器生成器可能會更好一些

    21.3.5 思考解釋器模式

Q:解釋器模式的本質

A:分離實現,解釋執行

 解釋器模式經過一個解釋器對象處理一個語法規則的方式,把複雜的功能分離開,而後選擇須要被執行的功能,並把這些功能組合成爲須要被解釋執行的抽象語法樹;再按照抽象語法樹來解釋執行,實現相應的功能

 認識這個本質對於識別和變形使用解釋器模式是頗有做用的.從表面上看,解釋器模式關注的是咱們平時不太用到的自定義語法的處理;但從實質上看,解釋器模式的思路仍然是分離,封裝,簡化,和不少模式是同樣的

 好比,能夠使用解釋器模式模擬狀態模式的功能,若是把解釋器模式要處理的語法簡化到只有一個狀態標記,把解釋器當作是對狀態的處理對象,對同一個表示狀態的語法,能夠有不少不一樣的解釋器,也就是有不少不一樣的處理狀態的對象,而後在建立抽象語法樹的時候,簡化成根據狀態的標記來建立相應的解釋器,不用再構建樹了.看看這樣簡化下來,是否是能夠用解釋器模擬出狀態模式的功能呢?

 同理,解釋器模式能夠模擬實現策略模式的功能,裝飾器模式的功能等,尤爲是模擬裝飾器模式的功能,構建抽象語法樹的過程,天然就對應成爲組合裝飾器的過程

Q:什麼時候選用解釋器模式

A:建議在如下狀況中選用解釋器模式

 當有一個語言須要解釋執行,而且能夠將該語言中的句子表示爲一個抽象語法樹的時候,能夠考慮使用解釋器模式

 在使用解釋器模式的時候,還有兩個特色須要考慮,一個是語法相對應該比較簡單,太複雜的語法不適合使用解釋器模式;另外一個是效率要求不是很高,對效率要求很高的狀況下,不適合使用解釋器模式

    21.3.6 相關模式

1.解釋器模式和組合模式

  這兩個模式能夠組合使用

  一般解釋器模式都會使用組合模式來實現,這樣可以方便地構建抽象語法樹.通常非終結符解釋器至關於組合模式的組合對象,終結符解釋器就至關於葉子對象

2.解釋器模式和迭代器模式

  這兩個模式能夠組合使用

  因爲解釋器模式一般使用組合模式來實現,所以在遍歷整個對象結構的時候,天然能夠使用迭代器模式

3.解釋器模式和享元模式

  這兩個模式能夠組合使用

  在使用解釋器模式的時候,可能會形成多個細粒度對象,好比,會有各類各樣的終結符解釋器,而這些終結符解釋器對不一樣的表達式來講是同樣的,是能夠共用的,所以能夠引入享元模式來共享這些對象

4.解釋器模式和訪問者模式

  這兩個模式能夠組合使用

  在解釋器模式中,語法規則和解釋器對象是有對應關係的,語法規則的變更意味着功能的變化,天然會致使使用不一樣的解釋器對象;並且一個語法規則能夠被不一樣的解釋器解釋執行

  所以在構建抽象語法樹的時候,若是每一個節點所對應的解釋器對象是固定的,這就意味着該節點對應的功能是固定的,那麼就不得不根據須要來構建不一樣的抽象語法樹

  爲了讓構建的抽象語法樹較爲通用,那就要求解釋器的功能不要那麼固定,要能很方便地改變解釋器的功能,這個時候問題就變成了如何可以很方便地更改樹形結構中節點對象的功能了,訪問者模式能夠很好地實現這個功能

第22章 裝飾模式(Decorator)

  22.1 場景問題

    22.1.1 複雜的獎金計算

    22.1.2 簡化後的獎金計算體系

    22.1.3 不用模式的解決方案

using System;
using System.Collections;
using System.Collections.Generic;
using System.Xml;

namespace Test2 {
    public class Program {
        static void Main(string[] args) {
            Prize p = new Prize();

            double zs = p.calcPrize("張三", DateTime.Now, DateTime.Now);
            Console.WriteLine("======張三應得獎金: " + zs);
            
            double ls = p.calcPrize("李四", DateTime.Now, DateTime.Now);
            Console.WriteLine("======李四應得獎金: " + ls);
            
            double ww = p.calcPrize("王五", DateTime.Now, DateTime.Now);
            Console.WriteLine("======王五應得獎金: " + ww);
        }
    }

    public class TempDB {
        public static Dictionary<string,double> mapMonthSaleMoney = new Dictionary<string, double>();
        
        private TempDB() {
            
        }

        static TempDB() {
            mapMonthSaleMoney.Add("張三", 10000.0);
            mapMonthSaleMoney.Add("李四", 20000.0);
            mapMonthSaleMoney.Add("王五", 30000.0);
        }
    }

    public class Prize {
        public double calcPrize(string user, DateTime begin, DateTime end) {
            double prize = 0.0;
            prize = this.monthPrize(user, begin, end);
            prize += this.sumPrize(user, begin, end);

            if (this.isManager(user)) {
                prize += this.groupPrize(user, begin, end);
            }

            return prize;
        }

        private double monthPrize(string user, DateTime begin, DateTime end) {
            double prize = TempDB.mapMonthSaleMoney[user] * 0.03;
            Console.WriteLine(user + "當月業務獎金" + prize);
            return prize;
        }

        public double sumPrize(string user, DateTime begin, DateTime end) {
            double prize = 1000000 * 0.001;
            Console.WriteLine(user + "累計獎金" + prize);
            return prize;
        }

        private bool isManager(string user) {
            if (user == "王五") {
                return true;
            }

            return false;
        }

        public double groupPrize(string user, DateTime begin, DateTime end) {
            double group = 0.0;
            foreach (double value in TempDB.mapMonthSaleMoney.Values) {
                group += value;
            }

            double prize = group * 0.01;
            Console.WriteLine(user + "當月團隊業務獎金" + prize);
            return prize;
        }
    }
}
View Code

    22.1.4 有何問題

Q:有何問題

A:如有一個計算獎金的對象,如今須要可以靈活地給它增長和減小功能,還須要可以動態地組合功能,每一個功能就至關於在計算獎金的某個部分.如今的問題是,如何纔可以透明地給一個對象增長功能,並實現功能的動態組合

  22.2 解決方案

    22.2.1 使用裝飾模式l來解決問題

Q:裝飾模式的定義

A:動態地給一個對象添加一些額外的職責,就增長功能來講,裝飾模式比生成子類更爲靈活

Q:應用裝飾模式來解決問題的思路

A:雖然通過簡化,業務簡單了不少,可是須要解決的問題仍很多,還須要解決:透明地給一個對象增長功能,並實現功能的動態組合

 所謂透明地給一個對象增長功能,換句話說就是要給一個對象增長功能,可是不能讓這個對象知道,也就是不能去改動這個對象.而實現了給一個對象透明地增長功能,天然就實現了功能的動態組合,好比,原來的對象有A功能,如今透明地給它增長了一個B功能,是否是就至關於動態組合了A和B功能呢

 在裝飾模式的實現中,爲了可以實現和原來使用被裝飾對象的代碼無縫結合,是經過定義一個抽象類,讓這個類實現與被裝飾對象相同的接口,而後在具體實現類中,轉調被裝飾的對象,在轉調的先後添加新的功能,這就實現了給被裝飾對象增長功能,這個思路和"對象組合"很是類似.

 在轉調的時候,若是以爲被裝飾對象的功能再也不須要了,還能夠直接替換掉,也就是再也不轉調.而是在裝飾對象中完成全新的實現

    22.2.2 裝飾模式的結構和說明

  [Component] 組件對象的接口,能夠給這些對象動態地添加職責

  [ConcreteComponent] 具體的組件對象,實現組件對象接口,一般就是被裝飾器裝飾的原始對象,也就是能夠給這個對象添加職責

  [Decorator] 全部裝飾器的抽象父類,須要定義一個與組件接口一致的接口,並持有一個Component對象,其實就是持有一個被裝飾的對象

  [ConcreteDecorator] 實際的裝飾對象,實現具體要向被裝飾對象添加的功能

    22.2.3 裝飾模式示例代碼

using System;
using System.Collections;
using System.Collections.Generic;
using System.Net;

namespace Test2 {
    public class Program {
        static void Main(string[] args) {

        }
    }

    public abstract class Component {
        public abstract void operation();
    }

    public class ConcreteComponent : Component {
        public override void operation() {
            
        }
    }

    public abstract class Decorator : Component {
        protected Component component;

        public Decorator(Component component) {
            this.component = component;
        }

        public override void operation() {
            component.operation();
        }
    }

    public class ConcreteDecoratorA : Decorator {
        private string addedState;
        
        public ConcreteDecoratorA(Component component) : base(component) {
            
        }

        public string getAddedState() {
            return addedState;
        }

        public void setAddedState(string addedState) {
            this.addedState = addedState;
        }

        public override void operation() {
            base.operation();
        }
    }

    public class ConcreteDecoratorB : Decorator {
        public ConcreteDecoratorB(Component component) : base(component) {
            
        }

        private void addedBehavior() {
            
        }

        public override void operation() {
            base.operation();
            addedBehavior();
        }
    }
}
View Code

    22.2.4 使用裝飾模式重寫示例

using System;
using System.Collections;
using System.Collections.Generic;


namespace ConsoleApplication1 {
    public class Program {
        public static void Main() {
            Component c1 = new ConcreteComponent();

            Decorator d1 = new MonthPrizeDecorator(c1);
            Decorator d2 = new SumPrizeDecorator(d1);

            double zs = d2.calcPrize("張三", DateTime.Now, DateTime.Now);
            Console.WriteLine("======張三應得獎金: " + zs);
            
            double ls = d2.calcPrize("李四", DateTime.Now, DateTime.Now);
            Console.WriteLine("======李四應得獎金: " + ls);

            Decorator d3 = new GroupPrizeDecorator(d2);
            double ww = d3.calcPrize("王五", DateTime.Now, DateTime.Now);
            Console.WriteLine("======王經理應得獎金: " + ww);
        }

    }

    public class TempDB {
        public static Dictionary<string,double> mapMonthSaleMoney = new Dictionary<string, double>();
        
        private TempDB() {
            
        }

        static TempDB() {
            mapMonthSaleMoney.Add("張三", 10000.0);
            mapMonthSaleMoney.Add("李四", 20000.0);
            mapMonthSaleMoney.Add("王五", 30000.0);
        }
    }

    

    public abstract class Component {
        public abstract double calcPrize(string user, DateTime begin, DateTime end);
    }

    public class ConcreteComponent : Component {
        public override double calcPrize(string user, DateTime begin, DateTime end) {
            return 0;
        }
    }

    public abstract class Decorator : Component {
        protected Component c;

        public Decorator(Component c) {
            this.c = c;
        }

        public override double calcPrize(string user, DateTime begin, DateTime end) {
            return c.calcPrize(user, begin, end);
        }
    }

    public class MonthPrizeDecorator : Decorator {
        public MonthPrizeDecorator(Component c) : base(c) {
            
        }

        public override double calcPrize(string user, DateTime begin, DateTime end) {
            double money = base.calcPrize(user, begin, end);
            double prize = TempDB.mapMonthSaleMoney[user] * 0.03;
            Console.WriteLine(user + "當月業務獎金" + prize);
            return money + prize;
        }
    }

    public class SumPrizeDecorator : Decorator {
        public SumPrizeDecorator(Component c) : base(c) {
            
        }

        public override double calcPrize(string user, DateTime begin, DateTime end) {
            double money = base.calcPrize(user, begin, end);
            double prize = 1000000 * 0.001;
            Console.WriteLine(user + "累計獎金" + prize);
            return money + prize;
        }
    }

    public class GroupPrizeDecorator : Decorator {
        public GroupPrizeDecorator(Component c) : base(c) {
            
        }

        public override double calcPrize(string user, DateTime begin, DateTime end) {
            double money = base.calcPrize(user, begin, end);
            double group = 0.0;

            foreach (double value in TempDB.mapMonthSaleMoney.Values) {
                group += value;
            }

            double prize = group * 0.01;
            Console.WriteLine(user + "當月團隊業務獎金" + prize);
            return money + prize;
        }
    }
}
View Code

  22.3 模式講解

    22.3.1 認識裝飾模式

Q:裝飾模式的功能

A:裝飾模式可以實現動態地爲對象添加功能,是從一個對象外部來給對象增長功能,至關因而改變了對象的外觀.當裝飾事後,從外部使用系統的角度看,就再也不是使用原始的那個對象了,而是使用被一系列的裝飾器裝飾事後的對象

 這樣就可以靈活地改變一個對象的功能,只要動態組合的裝飾器發生了改變,那麼最終所獲得的對象的功能也就發生了改變

 變相地還獲得了另一個好處,那就是裝飾器功能的複用,能夠給一個對象屢次增長同一個裝飾器,也能夠用同一個裝飾器裝飾不一樣的對象

Q:對象組合

A:前面已經講到了,一個類的功能的擴展方式,能夠是繼承,也能夠是功能更強大,更靈活的對象組合的方式

 其實,如今在面向對象的設計中,有一條基本的規則就是"儘可能使用對象組合,而不是對象繼承"來擴展和複用功能.裝飾模式的思考起點就是這個規則

 什麼是對象組合?

 倘若有一個對象A,實現了一個a1的方法,而C對象想要來擴展A的功能,給它增長一個c11的方法,那麼一個方案是繼承

using System;
using System.Collections;
using System.Collections.Generic;


namespace ConsoleApplication1 {
    public class Program {
        public static void Main() {

        }

    }

    public class A {
        public void a1() {
            Console.WriteLine("now in A.a1");
        }
    }

    public class C1 : A {
        public void c11() {
            Console.WriteLine("now in C1.c11");
        }
    }

}
View Code

 另一個方案就是使用對象組合,怎麼組合呢?就是在C1對象中再也不繼承A對象了,而是去組合使用A對象的實例,經過轉調A對象的功能來實現A對象已有的功能

using System;
using System.Collections;
using System.Collections.Generic;


namespace ConsoleApplication1 {
    public class Program {
        public static void Main() {

        }

    }

    public class A {
        public void a1() {
            Console.WriteLine("now in A.a1");
        }
    }

    public class C2 {
        private A a = new A();

        public void a1() {
            a.a1();
        }

        public void c11() {
            Console.WriteLine("now in C2.c11");
        }
    }

}
View Code

 對象組合是否是很簡單,並且更靈活了

 1.首先能夠有選擇地複用功能,不是全部A的功能都會被複用,在C2中少調用幾個A定義的功能就能夠了

 2.其次在轉調先後,能夠實現一些功能處理,並且對於A對象是透明的,也就是A對象並不知道在a1方法處理的時候被追加了功能

 3.還有一個額外的好處,就是能夠組合擁有多個對象的功能

using System;
using System.Collections;
using System.Collections.Generic;


namespace ConsoleApplication1 {
    public class Program {
        public static void Main() {

        }

    }

    public class A {
        public void a1() {
            Console.WriteLine("now in A.a1");
        }
    }

    public class B {
        public void b1() {
            Console.WriteLine("now in B.b1");
        }
    }

    public class C3 {
        private A a = new A();
        private B b = new B();

        public void a1() {
            a.a1();
        }

        public void b1() {
            b.b1();
        }

        public void c11() {
            Console.WriteLine("now in C3.c11");
        }
    }

}
View Code

 最後再說一點,就是關於對象組合中,什麼時候建立被組合對象的實例

 1.一種方案是在屬性值上直接定義並建立須要組合的對象實例

 2.另一種方案是在屬性上定義一個變量,來表示持有被組合對象的實例,具體實例從外部傳入,也能夠經過IoC/DI容器來注入

using System;
using System.Collections;
using System.Collections.Generic;


namespace ConsoleApplication1 {
    public class Program {
        public static void Main() {

        }

    }

    public class A {
        public void a1() {
            Console.WriteLine("now in A.a1");
        }
    }

    public class B {
        public void b1() {
            Console.WriteLine("now in B.b1");
        }
    }

    public class C4 {
        private A a = new A();
        private B b;

        public void setB(B b) {
            this.b = b;
        }
        
        public void a1() {
            a.a1();
        }

        public void b1() {
            b.b1();
        }

        public void c11() {
            Console.WriteLine("now in C4.c11");
        }
    }

}
View Code

Q:裝飾器

A:裝飾器實現了對被裝飾對象的某些裝飾功能,能夠在裝飾器中調用被裝飾對象的功能,獲取相應的值,這實際上是一種遞歸調用

 在裝飾器中不只僅是能夠給被裝飾對象增長功能,還能夠根據須要選擇是否調用被裝飾對象的功能,若是不調用被裝飾對象的功能,那就變成徹底從新實現了,至關於動態修改了被裝飾對象的功能    

Q:裝飾器和組件類的關係

A:裝飾器是用來裝飾組件的,裝飾器必定要實現和組件類一致的接口,保證它們是同一個類型,並具備同一個外觀,這樣組合完成的裝飾纔可以遞歸調用下去

 組件類是不知道裝飾器的存在的,裝飾器爲組件添加功能是一種透明的包裝,組件類絕不知情.須要改變的是外部使用組件類的地方,如今須要使用包裝後的類,接口是同樣的,可是具體的實現類發生了改變

Q:退化形式

A:若是僅僅只是想要添加一個功能,就沒有必要再設計裝飾器的抽象類了,直接在裝飾器中實現跟組件同樣的接口,而後實現相應的裝飾功能就能夠了.可是建議最好仍是設計上裝飾器的抽象類,這樣有利於程序的擴展

    22.3.2 Java中的裝飾模式應用

    22.3.3 裝飾模式和AOP

    22.3.4 裝飾模式的優缺點

優勢

  1.比繼承更靈活

    從爲對象添加功能的角度來看,裝飾模式比繼承更靈活,繼承是靜態的,並且一旦繼承全部子類都有同樣的功能.而裝飾模式採用把功能分離到每一個裝飾器當中,而後經過對象組合的方式,在運行時動態地組合功能,每一個被裝飾的對象最終有哪些功能,是由運行期動態組合的功能來決定的

  2.更容易複用功能

    裝飾模式把一系列複雜的功能分散到每一個裝飾器當中,通常一個裝飾器只實現一個功能,使實現裝飾器變得簡單,更重要的是這樣有利於裝飾器功能的複用,能夠給一個對象增長多個一樣的裝飾器,也能夠把一個裝飾器用來裝飾不一樣的對象,從而實現複用裝飾器的功能

  3.簡化高層定義

    裝飾模式能夠經過組合裝飾器的方式,爲對象增添任意多的功能.所以在進行高層定義的時候,不用把全部的功能都定義出來,而是定義最基本的就能夠了,能夠在須要使用的時候,組合相應的裝飾器來完成所需的功能.

缺點

  1.會產生不少細粒度對象

    前面說了,裝飾模式是把一系列複雜的功能,分散到每一個裝飾器當中,通常一個裝飾器只實現一個功能,這樣會產生不少細粒度的對象,並且功能越複雜,須要的細粒度對象越多

    22.3.5 思考裝飾模式

Q:裝飾模式的本質

A:動態組合

 動態是手段,組合纔是目的,這裏的組合有兩個意思,一個是動態功能的組合,也就是動態進行裝飾器的組合;另一個是指對象組合,經過對象組合來實現爲被裝飾對象透明地增長功能

 可是要注意,裝飾模式不只能夠增長功能,並且也能夠控制功能的訪問,徹底實現新的功能,還能夠控制裝飾的功能是在被裝飾功能以前仍是以後來運行等

 總之,裝飾模式是經過把複雜功能簡單化,分散化,而後在運行期間,根據須要來動態組合的這樣一個模式

Q:什麼時候選用裝飾模式

A:  1.若是須要在不影響其餘對象的狀況下,以動態,透明的方式給對象添加職責,能夠使用裝飾模式,這幾乎就是裝飾模式的主要功能

   2.若是不適合使用子類來進行擴展的時候,能夠考慮使用裝飾模式.由於裝飾模式是使用的"對象組合"的方式.所謂不適合用子類擴展的方式,好比,擴展功能須要的子類太多,形成子類數目呈爆炸性增加

    22.3.6 相關模式

1.裝飾模式和適配器模式

 這是兩個沒有什麼關聯的模式,放到一塊兒來講,是由於它們有一個共同的別名:Wrapper;

 這兩個模式功能上是不同的,適配器模式是用來改變接口的,而裝飾模式是用來改變對象功能的

2.裝飾模式與組合模式

 這兩個模式有類似之處,都涉及到對象的遞歸調用,從某個角度來講,能夠把裝飾看作是隻有一個組件的組合

 可是它們的目的徹底不同,裝飾模式是要動態地給對象增長功能;而組合模式是想要管理組合對象和葉子對象,爲它們提供過一個一致的操做接口給客戶端,方便客戶端的使用

3.裝飾模式和策略模式

 這兩個模式能夠組合使用

 策略模式也能夠實現動態地改變對象的功能,可是策略模式只是一層選擇,也就是根據策略選擇一下具體的實現類而已.而裝飾模式不是一層,而是遞歸調用,無數層均可以,只要組合好裝飾器的對象組合,那就能夠依次調用下去.因此裝飾模式更靈活

 並且策略模式改變的是原始對象的功能,不像裝飾模式,後面一個裝飾器,改變的是通過前一個裝飾器裝飾後的對象.也就是策略模式改變的是對象的內核,而裝飾模式改變的是對象的外殼

 這兩個模式能夠組合使用,能夠在一個具體的裝飾器中使用策略模式來選擇更具體的實現方式.好比前面計算獎金的另一個問題就是參與計算的基數不一樣,獎金的計算方式也是不一樣的.舉例來講:假設張三和李四參與同一個獎金的計算,張三的銷售總額是2萬元,而李四的銷售總額是8萬元,它們的計算公式是不同的,假設獎金的計算規則是,銷售額在5萬如下,統一3%,而5萬以上,5萬內是4%,超過部分是6%

 參與同一個獎金的計算,這就意味着能夠使用同一個裝飾器,可是在裝飾器的內部,不一樣條件下計算公式不同,那麼怎麼選擇具體的實現策略呢?天然使用策略模式就能夠了,也就是裝飾模式和策略模式組合來使用

4.裝飾模式與模板方法模式

 這是兩個功能上有點類似的模式

 模板方法模式主要應用在算法骨架固定的狀況,那麼要是算法步驟不固定呢,也就是一個相對動態的算法步驟,就能夠使用裝飾模式了,由於在使用裝飾模式的時候,進行裝飾器的組裝,其實也至關因而一個調用算法步驟的組裝,至關因而一個動態的算法骨架

 既然裝飾模式能夠實現動態的算法步驟的組裝和調用,那麼把這些算法步驟固定下來,那就是模板方法模式實現的功能了,所以裝飾模式能夠模擬實現模板方法模式的功能

第23章 職責鏈模式(Chain of Responsibility)

  23.1 場景問題

    23.1.1 申請聚餐費用

    23.1.2 不用模式的解決方案

using System;
using System.Collections;
using System.Collections.Generic;

namespace Test2 {
    public class Program {
        static void Main(string[] args) {
            FeeRequest request = new FeeRequest();

            string ret1 = request.requestToProjectManager("小李", 300);
            Console.WriteLine("the ret = " + ret1);
            string ret2 = request.requestToProjectManager("小張", 300);
            Console.WriteLine("the ret = " + ret2);

            string ret3 = request.requestToProjectManager("小李", 600);
            Console.WriteLine("the ret = " + ret3);
            string ret4 = request.requestToProjectManager("小張", 600);
            Console.WriteLine("the ret = " + ret4);

            string ret5 = request.requestToProjectManager("小李", 1200);
            Console.WriteLine("the ret = " + ret5);
            string ret6 = request.requestToProjectManager("小張", 1200);
            Console.WriteLine("the ret = " + ret6);

        }
    }

    public class FeeRequest {
        public string requestToProjectManager(string user, double fee) {
            string str = "";
            if (fee < 500) {
                str = projectHandle(user, fee);
            }
            else if (fee < 1000) {
                str = depManagerHandle(user, fee);
            }
            else if (fee >= 1000) {
                str = generalManagerHandle(user, fee);
            }

            return str;
        }

        private string projectHandle(string user, double fee) {
            string str = "";
            if (user == "小李") {
                str = "項目經理贊成" + user + "聚餐費用" + fee + "元的請求";
            }
            else {
                str = "項目經理不一樣意" + user + "聚餐費用" + fee + "元的請求";
            }

            return str;
        }

        private string depManagerHandle(string user, double fee) {
            string str = "";
            if (user == "小李") {
                str = "部門經理贊成" + user + "聚餐費用" + fee + "元的請求";
            }
            else {
                str = "部門經理不一樣意" + user + "聚餐費用" + fee + "元的請求";
            }

            return str;
        }

        private string generalManagerHandle(string user, double fee) {
            string str = "";
            if (user == "小李") {
                str = "總經理贊成" + user + "聚餐費用" + fee + "元的請求";
            }
            else {
                str = "總經理不一樣意" + user + "聚餐費用" + fee + "元的請求";
            }

            return str;
        }
    }
}
View Code

    23.1.3 有何問題

Q:有何問題

A:把問題抽象一下,客戶端發出一個請求,會有不少對象均可以來處理這個請求,並且不一樣對象的處理邏輯是不同的.對於客戶端而言,無所謂誰來處理,反正有對象處理就能夠了.並且在上述處理中,還但願處理流程是能夠靈活變更的,而處理請求的對象須要能方便地修改或者是被替換掉,以適應新的業務功能的須要.

  23.2 解決方案

    23.2.1 使用職責鏈模式來解決問題

Q:職責鏈模式的定義

A:使多個對象都有機會處理請求,從而避免請求的發送者和接收者之間的耦合關係.將這些對象連成一條鏈,並沿着這條鏈傳遞該請求,直到有一個對象處理它爲止

Q:應用職責鏈模式來解決問題的思路

A:仔細分析上面的場景,當客戶端提出一個聚餐費用的申請,後續處理這個申請的對象項目經理,部門經理和總經理,天然地造成了一個鏈,從項目經理->部門經理->總經理,客戶端的申請請求就在這個鏈中傳遞,直到有領導處理爲止.看起來,上面的功能要求很適合採用職責鏈來處理這個業務

 要相讓處理請求的流程能夠靈活地變更,一個基本的思路,那就是動態構建流程步驟,這樣隨時均可以從新組合出新的流程來.而要讓處理請求的對象也要很靈活,那就要讓它足夠簡單,最好是隻實現單一的功能,或者是有限的功能,這樣更有利於修改和複用

 職責鏈模式就很好地體現了上述的基本思路,首先職責鏈模式會定義一個全部處理請求的對象都要繼承實現的抽象類,這樣就有利於隨時切換新的實現,其次每一個處理請求對象只實現業務流程中的一步業務處理,這樣使其變得簡單;最後職責鏈模式會動態地來組合這些處理請求的對象,把它們按照流程動態地組合起來,並要求它們依次調用,這樣就動態地實現了流程

 這樣一來,若是流程發生了變化,只要從新組合就能夠了,若是某個處理的業務功能發生了變化,一個方案是修改該處理對應的處理對象,另外一個方案是直接提供一個新的實現,而後在組合流程的時候,用新的實現替換掉舊的實現就能夠了

    23.2.2 職責鏈模式的結構和說明

  [Handler] 定義職責的接口,一般在這裏定義處理請求的方法,能夠在這裏實現後繼鏈

  [ConcreteHandler] 實現職責的類,在這個類中,實現對在它職責範圍內請求的處理,若是不處理,就繼續轉發請求給後繼者

  [Client] 職責鏈的客戶,向鏈上的具體處理對象提交請求,讓職責鏈負責處理

    23.2.3 職責鏈模式示例代碼

using System;
using System.Collections;
using System.Collections.Generic;

namespace ConsoleApplication1 {
    public class Program {
        public static void Main() {
            Handler h1 = new ConcreteHandler1();
            Handler h2 = new ConcreteHandler2();

            h1.setSuccessor(h2);
            h1.handleRequest();
        }

    }

    public abstract class Handler {
        
        protected Handler successor;

        public void setSuccessor(Handler successor) {
            this.successor = successor;
        }

        public abstract void handleRequest();
    }

    public class ConcreteHandler1 : Handler {
        public override void handleRequest() {
            bool someCondition = false;

            if (someCondition) {
                Console.WriteLine("ConcreteHandler1 handle request");
            } else {
                if (this.successor != null) {
                    this.successor.handleRequest();
                }  
            }
        }
    }

    public class ConcreteHandler2 : Handler {
        public override void handleRequest() {
            bool someCondition = false;

            if (someCondition) {
                Console.WriteLine("ConcreteHandler2 handle request");
            } else {
                if (this.successor != null) {
                    this.successor.handleRequest();
                }  
            }
        }
    }
}
View Code

    22.2.4 使用職責鏈模式重寫示例

using System;
using System.Collections;
using System.Collections.Generic;

namespace Test2 {
    public class Program {
        static void Main(string[] args) {
            Handler h1 = new GeneralManager();
            Handler h2 = new DepManager();
            Handler h3 = new ProjectManager();
            
            h3.setSuccessor(h2);
            h2.setSuccessor(h1);

            string ret1 = h3.handleFeeRequest("小李", 300);
            Console.WriteLine("the ret1 = " + ret1);
            string ret2 = h3.handleFeeRequest("小張", 300);
            Console.WriteLine("the ret2 = " + ret2);
            
            string ret3 = h3.handleFeeRequest("小李", 600);
            Console.WriteLine("the ret3 = " + ret3);
            string ret4 = h3.handleFeeRequest("小張", 600);
            Console.WriteLine("the ret4 = " + ret4);
            
            string ret5 = h3.handleFeeRequest("小李", 1200);
            Console.WriteLine("the ret5 = " + ret5);
            string ret6 = h3.handleFeeRequest("小張", 1200);
            Console.WriteLine("the ret6 = " + ret6);
        }
    }

    public abstract class Handler {
        protected Handler successor;

        public void setSuccessor(Handler successor) {
            this.successor = successor;
        }

        public abstract string handleFeeRequest(string user, double fee);
    }

    public class ProjectManager : Handler {

        public override string handleFeeRequest(string user, double fee) {
            string str = "";

            if (fee < 500) {
                if (user == "小李") {
                    str = "項目經理贊成" + user + "聚餐費用" + fee + "元的請求";
                }
                else {
                    str = "項目經理不一樣意" + user + "聚餐費用" + fee + "元的請求";
                }

                return str;
            } else {
                if (this.successor != null) {
                    return successor.handleFeeRequest(user, fee);
                }
            }

            return str;
        }        
    }

    public class DepManager : Handler {
        public override string handleFeeRequest(string user, double fee) {
            string str = "";

            if (fee < 1000) {
                if (user == "小李") {
                    str = "部門經理贊成" + user + "聚餐費用" + fee + "元的請求";
                }
                else {
                    str = "部門經理不一樣意" + user + "聚餐費用" + fee + "元的請求";
                }
    
                return str;
            } else {
                if (this.successor != null) {
                    return successor.handleFeeRequest(user, fee);    
                }
            }

            return str;
        }
    }

    public class GeneralManager : Handler {
        public override string handleFeeRequest(string user, double fee) {
            string str = "";

            if (fee >= 1000) {
                if (user == "小李") {
                    str = "總經理贊成" + user + "聚餐費用" + fee + "元的請求";
                }
                else {
                    str = "總經理不一樣意" + user + "聚餐費用" + fee + "元的請求";
                }

                return str;
            } else {
                if (successor != null) {
                    return successor.handleFeeRequest(user, fee);
                }
            }

            return str;
        }
    }
}
View Code

  23.3 模式講解

    23.3.1 認識職責鏈模式

Q:職責鏈模式的功能

A:職責鏈模式主要用來處理,「客戶端發出一個請求,有多個對象都有機會來處理這一個請求,可是客戶端不知道究竟誰會來處理他的請求」這樣的狀況.也就是須要讓請求者和接收者解耦,這樣就能夠動態地切換和組合接收者了

 若是要變形使用職責鏈,就可讓這個請求繼續傳遞,每一個職責對象對這個請求進行必定的功能處理,從而造成一個處理請求的功能鏈

    23.3.2 處理多種請求

簡單方式

using System;
using System.Collections;
using System.Collections.Generic;

namespace Test2 {
    public class Program {
        static void Main(string[] args) {
            Handler h1 = new GeneralManager();
            Handler h2 = new DepManager();
            Handler h3 = new ProjectManager();
            
            h3.setSuccessor(h2);
            h2.setSuccessor(h1);

            string ret1 = h3.handleFeeRequest("小李", 300);
            Console.WriteLine("the ret1=" + ret1);
            string ret2 = h3.handleFeeRequest("小李", 600);
            Console.WriteLine("the ret2=" + ret2);
            string ret3 = h3.handleFeeRequest("小李", 1200);
            Console.WriteLine("the ret3=" + ret3);

            h3.handlePreFeeRequest("小張", 3000);
            h3.handlePreFeeRequest("小張", 6000);
            h3.handlePreFeeRequest("小張", 32000);
        }
    }

    public abstract class Handler {
        protected Handler successor;

        public void setSuccessor(Handler successor) {
            this.successor = successor;
        }

        public abstract string handleFeeRequest(string user, double fee);
        public abstract bool handlePreFeeRequest(string user, double requestFee);
    }

    public class ProjectManager : Handler {

        public override string handleFeeRequest(string user, double fee) {
            string str = "";

            if (fee < 500) {
                if (user == "小李") {
                    str = "項目經理贊成" + user + "聚餐費用" + fee + "元的請求";
                }
                else {
                    str = "項目經理不一樣意" + user + "聚餐費用" + fee + "元的請求";
                }

                return str;
            } else {
                if (this.successor != null) {
                    return successor.handleFeeRequest(user, fee);
                }
            }

            return str;
        }

        public override bool handlePreFeeRequest(string user, double requestNum) {
            if (requestNum < 5000) {
                Console.WriteLine("項目經理贊成" + user+ "預支差旅費用" + requestNum + "元的請求");
                return true;
            } else {
                if (this.successor != null) {
                    return this.successor.handlePreFeeRequest(user, requestNum);
                }  
            }

            return false;
        }
    }

    public class DepManager : Handler {
        public override string handleFeeRequest(string user, double fee) {
            string str = "";

            if (fee < 1000) {
                if (user == "小李") {
                    str = "部門經理贊成" + user + "聚餐費用" + fee + "元的請求";
                }
                else {
                    str = "部門經理不一樣意" + user + "聚餐費用" + fee + "元的請求";
                }
    
                return str;
            } else {
                if (this.successor != null) {
                    return successor.handleFeeRequest(user, fee);    
                }
            }

            return str;
        }
        
        public override bool handlePreFeeRequest(string user, double requestNum) {
            if (requestNum < 10000) {
                Console.WriteLine("部門經理贊成" + user+ "預支差旅費用" + requestNum + "元的請求");
                return true;
            } else {
                if (this.successor != null) {
                    return this.successor.handlePreFeeRequest(user, requestNum);
                }  
            }

            return false;
        }
    }

    public class GeneralManager : Handler {
        public override string handleFeeRequest(string user, double fee) {
            string str = "";

            if (fee >= 1000) {
                if (user == "小李") {
                    str = "總經理贊成" + user + "聚餐費用" + fee + "元的請求";
                }
                else {
                    str = "總經理不一樣意" + user + "聚餐費用" + fee + "元的請求";
                }

                return str;
            } else {
                if (successor != null) {
                    return successor.handleFeeRequest(user, fee);
                }
            }

            return str;
        }
        
        public override bool handlePreFeeRequest(string user, double requestNum) {
            if (requestNum < 50000) {
                Console.WriteLine("總經理贊成" + user+ "預支差旅費用" + requestNum + "元的請求");
                return true;
            } else {
                if (this.successor != null) {
                    return this.successor.handlePreFeeRequest(user, requestNum);
                }  
            }

            return false;
        }
    }
}
View Code

通用方式

using System;
using System.Collections;
using System.Collections.Generic;

namespace Test2 {
    public class Program {
        static void Main(string[] args) {
            Handler h1 = new GeneralManager();
            Handler h2 = new DepManager();
            Handler h3 = new ProjectManager();
            
            h3.setSuccessor(h2);
            h2.setSuccessor(h1);

            FeeRequestModel frm = new FeeRequestModel();
            frm.setFee(300);
            frm.setUser("小李");
            string ret1 = (string) h3.handleRequest(frm);
            Console.WriteLine("the ret1=" + ret1);

            frm.setFee(800);
            string ret2 = (string) h3.handleRequest(frm);
            Console.WriteLine("the ret2=" + ret2);

            frm.setFee(1600);
            string ret3 = (string) h3.handleRequest(frm);
            Console.WriteLine("the ret3=" + ret3);
        }
    }

    public class RequestModel {
        private string type;

        public RequestModel(string type) {
            this.type = type;
        }

        public string getType() {
            return type;
        }
    }

    public class FeeRequestModel : RequestModel {
        public static string FEE_TYPE = "fee";

        private double fee;
        private string user;
        
        public FeeRequestModel() : base(FEE_TYPE) {
            
        }

        public string getUser() {
            return user;
        }

        public void setUser(string user) {
            this.user = user;
        }

        public double getFee() {
            return fee;
        }

        public void setFee(double fee) {
            this.fee = fee;
        }
    }

    public class PreFeeRequestModel : RequestModel {
        public static string FEE_TYPE = "preFee";

        private double fee;
        private string user;
        
        public PreFeeRequestModel() : base(FEE_TYPE) {
            
        }
        
        public string getUser() {
            return user;
        }

        public void setUser(string user) {
            this.user = user;
        }

        public double getFee() {
            return fee;
        }

        public void setFee(double fee) {
            this.fee = fee;
        }
    }
    

    public abstract class Handler {
        protected Handler successor;

        public void setSuccessor(Handler successor) {
            this.successor = successor;
        }

        public virtual object handleRequest(RequestModel rm) {
            if (successor != null) {
                return this.successor.handleRequest(rm);
            } else {
                Console.WriteLine("沒有後續處理或者暫時不支持這樣的功能處理");
                return false;
            }
        }
    }

    public class ProjectManager : Handler {
        public override object handleRequest(RequestModel rm) {
            if (FeeRequestModel.FEE_TYPE == rm.getType()) {
                return handleFeeRequest(rm);
            } else {
                return base.handleRequest(rm);
            }
        }
        
        private object handleFeeRequest(RequestModel rm) {
            FeeRequestModel frm = (FeeRequestModel) rm;
            
            string str = "";

            if (frm.getFee() < 500) {
                if (frm.getUser() == "小李") {
                    str = "項目經理贊成" + frm.getUser() + "聚餐費用" + frm.getFee() + "元的請求";
                }
                else {
                    str = "項目經理不一樣意" + frm.getUser() + "聚餐費用" + frm.getFee() + "元的請求";
                }

                return str;
            } else {
                if (this.successor != null) {
                    return successor.handleRequest(rm);
                }
            }

            return str;
        }

//        private bool handlePreFeeRequest(string user, double requestNum) {
//            if (requestNum < 5000) {
//                Console.WriteLine("項目經理贊成" + user+ "預支差旅費用" + requestNum + "元的請求");
//                return true;
//            } else {
//                if (this.successor != null) {
//                    return this.successor.handlePreFeeRequest(user, requestNum);
//                }  
//            }
//
//            return false;
//        }
    }

    public class DepManager : Handler {
        public override object handleRequest(RequestModel rm) {
            if (FeeRequestModel.FEE_TYPE == rm.getType()) {
                return handleFeeRequest(rm);
            } else {
                return base.handleRequest(rm);
            }
        }
        
        private object handleFeeRequest(RequestModel rm) {
            FeeRequestModel frm = (FeeRequestModel)rm;
            
            string str = "";

            if (frm.getFee() < 1000) {
                if (frm.getUser() == "小李") {
                    str = "部門經理贊成" + frm.getUser() + "聚餐費用" + frm.getFee() + "元的請求";
                }
                else {
                    str = "部門經理不一樣意" + frm.getUser() + "聚餐費用" + frm.getFee() + "元的請求";
                }
    
                return str;
            } else {
                if (this.successor != null) {
                    return successor.handleRequest(rm);
                }
            }

            return str;
        }
        
//        public override bool handlePreFeeRequest(string user, double requestNum) {
//            if (requestNum < 10000) {
//                Console.WriteLine("部門經理贊成" + user+ "預支差旅費用" + requestNum + "元的請求");
//                return true;
//            } else {
//                if (this.successor != null) {
//                    return this.successor.handlePreFeeRequest(user, requestNum);
//                }  
//            }
//
//            return false;
//        }
    }

    public class GeneralManager : Handler {
        public override object handleRequest(RequestModel rm) {
            if (FeeRequestModel.FEE_TYPE == rm.getType()) {
                return handleFeeRequest(rm);
            } else {
                return base.handleRequest(rm);
            }
        }
        
        private object handleFeeRequest(RequestModel rm) {
            FeeRequestModel frm = (FeeRequestModel)rm;
            
            string str = "";

            if (frm.getFee() >= 1000) {
                if (frm.getUser() == "小李") {
                    str = "總經理贊成" + frm.getUser() + "聚餐費用" + frm.getFee() + "元的請求";
                }
                else {
                    str = "總經理不一樣意" + frm.getUser() + "聚餐費用" + frm.getFee() + "元的請求";
                }

                return str;
            } else {
                if (successor != null) {
                    return successor.handleRequest(rm);
                }
            }

            return str;
        }
        
//        public override bool handlePreFeeRequest(string user, double requestNum) {
//            if (requestNum < 50000) {
//                Console.WriteLine("總經理贊成" + user+ "預支差旅費用" + requestNum + "元的請求");
//                return true;
//            } else {
//                if (this.successor != null) {
//                    return this.successor.handlePreFeeRequest(user, requestNum);
//                }  
//            }
//
//            return false;
//        }
    }
}
View Code

  23.3.3 功能鏈

using System;
using System.Collections;
using System.Collections.Generic;

namespace Test2 {
    public class Program {
        static void Main(string[] args) {
            GoodsSaleEbo ebo = new GoodsSaleEbo();
            
            SaleModel saleModel = new SaleModel();
            saleModel.setGoods("張學友懷舊經典");
            saleModel.setSaleNum(10);

            ebo.sale("小李", "張三", saleModel);
            ebo.sale("小張", "李四", saleModel);
        }
    }

    public class SaleModel {
        private string goods;
        private int saleNum;

        public string getGoods() {
            return goods;
        }

        public void setGoods(string goods) {
            this.goods = goods;
        }

        public int getSaleNum() {
            return saleNum;
        }

        public void setSaleNum(int saleNum) {
            this.saleNum = saleNum;
        }

        public override string ToString() {
            return "商品名稱=" + goods + ",銷售數量=" + saleNum;
        }
    }

    public abstract class SaleHandler {
        protected SaleHandler successor;

        public void setSuccessor(SaleHandler successor) {
            this.successor = successor;
        }

        public abstract bool sale(string user, string customer, SaleModel saleModel);
    }

    public class SaleSecurityCheck : SaleHandler {
        public override bool sale(string user, string customer, SaleModel saleModel) {
            if (user == "小李") {
                return this.successor.sale(user, customer, saleModel);
            } else {
                Console.WriteLine("對不起" + user + ",你沒有保存銷售信息的權限");
                return false;
            }
        }
    }

    public class SaleDataCheck : SaleHandler {
        public override bool sale(string user, string customer, SaleModel saleModel) {
            if (user == null || user.Trim().Length == 0) {
                Console.WriteLine("申請人不能爲空");
                return false;
            }
            
            if (customer == null || customer.Trim().Length == 0) {
                Console.WriteLine("客戶不能爲空");
                return false;
            }

            if (saleModel == null) {
                Console.WriteLine("銷售商品的數據不能爲空");
                return false;
            }

            if (saleModel.getGoods() == null || saleModel.getGoods().Trim().Length == 0) {
                Console.WriteLine("銷售的商品不能爲空");
                return false;
            }

            if (saleModel.getSaleNum() == 0) {
                Console.WriteLine("銷售商品的數量不能爲0");
                return false;
            }

            return this.successor.sale(user, customer, saleModel);
        }
    }

    public class SaleLogicCheck : SaleHandler {
        public override bool sale(string user, string customer, SaleModel saleModel) {
            return this.successor.sale(user, customer, saleModel);
        }
    }

    public class SaleMgr : SaleHandler {
        public override bool sale(string user, string customer, SaleModel saleModel) {
            Console.WriteLine(user + "保存了" + customer + "購買" + saleModel + " 的銷售收據");
            return true;
        }
    }
    
    public class GoodsSaleEbo {
        public bool sale(string user, string customer, SaleModel saleModel) {
            SaleSecurityCheck ssc = new SaleSecurityCheck();
            SaleDataCheck sdc = new SaleDataCheck();
            SaleLogicCheck slc = new SaleLogicCheck();
            SaleMgr sd = new SaleMgr();

            ssc.setSuccessor(sdc);
            sdc.setSuccessor(slc);
            slc.setSuccessor(sd);

            return ssc.sale(user, customer, saleModel);
        }
    }
}
View Code

    23.3.4 職責鏈模式的優缺點

優勢

  1.請求者和接收者鬆散耦合

    在職責鏈模式中,請求者並不知道接收者是誰,也不知道具體如何處理,請求者只是負責向職責鏈發出請求就能夠了,而每一個職責對象也不用管請求者或者是其餘的職責對象,只負責處理本身的部分,其餘的就交給其餘的職責對象去處理.也就是說,請求者和接收者是徹底解耦的

  2.動態組合職責

    職責鏈模式會把功能處理分散到單獨的職責對象中,而後在使用的時候,能夠動態組合職責造成職責鏈,從而能夠靈活地給對象分配職責,也能夠靈活地實現和改變對象的職責

缺點

  1.產生不少細粒度對象

    職責鏈模式會把功能處理分散到單獨的職責對象中,也就是每一個職責對象只處理一個方面的功能,要把整個業務處理完,須要不少職責對象的組合,這樣會產生大量的細粒度職責對象

  2.不必定能被處理

    職責鏈模式的每一個職責對象只負責本身處理的那一部分,所以可能會出現某個請求,把整個鏈傳完了,都沒有職責對象處理它,這就須要在使用職責鏈模式的時候,須要提供默認的處理,而且注意構建的鏈的有效性

    23.3.5 思考職責鏈模式

Q:職責鏈模式的本質

A:分離職責,動態組合

 分離職責是前提,只有先把複雜的功能分開,拆分紅不少的步驟和小的功能處理,而後才能合理規劃和定義職責類.能夠有不少的職責類來負責處理某一個功能,讓每一個職責類負責處理功能的某一個方面,在運行期間進行動態組合,造成一個處理的鏈,把這個鏈運行完,功能也就處理完了

 動態組合纔是職責鏈模式的精華所在,由於要實現請求對象和處理對象的解耦,請求對象不知道誰纔是真正的處理對象,所以要動態地把可能的處理對象組合起來.因爲組合的方式是動態的,這就意味着能夠很方便地修改和添加新的處理對象,從而讓系統更加靈活和具備更好的擴展性

Q:什麼時候選用職責鏈模式

A:  1.若是有多個對象能夠處理同一個請求,可是具體由哪一個對象來處理該請求,是運行時刻動態肯定的,這種狀況能夠使用職責鏈模式,把處理請求的對象實現成爲職責對象,而後把它們構成一個職責鏈,當請求在這個鏈中傳遞的時候,具體由哪一個職責對象來處理,會在運行時動態判斷

   2.若是你想在不明確指定接收者的狀況下,向多個對象中的其中一個提交請求的話,能夠使用職責鏈模式.職責鏈模式實現了請求者和接收者之間的解耦,請求者不須要知道到底是哪個接收者對象來處理請求

   3.若是想要動態指定處理一個請求的對象集合,能夠使用職責鏈模式.職責鏈模式能動態地構建職責鏈,也就是動態地來決定到底哪些職責對象來參與處處理請求中來,至關因而動態地指定了處理一個請求的職責對象集合

    23.3.6 相關模式

1.職責鏈模式和組合模式

  這兩個模式能夠組合使用

  能夠把職責對象經過組合模式來組合,這樣能夠經過組合對象自動遞歸地向上調用,由父組件做爲子組件的後繼,從而造成鏈

  這也就是前面提到過的使用外部已有的連接,這種狀況在客戶端使用的時候,就不用再構造鏈了,雖然不構造鏈,可是須要構造組合對象樹,是同樣的

2.職責鏈模式和裝飾模式

  這兩個模式類似,從某個角度講,能夠相互模擬實現對方的功能

  裝飾模式可以動態地給被裝飾對象添加功能,要求裝飾器對象和被裝飾的對象實現相同的接口.而職責鏈模式能夠實現動態的職責組合,標準的功能是有一個對象處理就結束,可是若是處理完本職責不急於結束,而是繼續向下傳遞請求,那麼其功能就和裝飾模式的功能差很少了,每一個職責對象就相似於裝飾器,能夠實現某種功能

  並且兩個模式的本質也類似,都須要在運行期間動態組合,裝飾模式是動態組合裝飾器,而職責鏈是動態組合處理請求的職責對象的鏈

  可是從標準的設計模式上來說,這兩個模式仍是有較大區別的,這點要注意.

  首先是目的不一樣,裝飾模式是要實現透明的爲對象添加功能,而職責鏈模式是要實現發送者和接收者解耦,另一個,裝飾模式是無限遞歸調用的,能夠有任意多個對象來裝飾功能,可是職責鏈模式是有一個處理就結束

3.職責鏈模式和策略模式

  這兩個模式能夠組合使用

  這兩個模式有類似之處,若是把職責鏈簡化到直接就能選擇到相應的處理對象,那就跟策略模式的選擇差很少,所以能夠用職責鏈來模擬策略模式的功能.只是若是把職責鏈簡化到這個地步,也就不存在鏈了,也就稱不上是職責鏈了.兩個模式能夠組合使用,能夠在職責鏈模式的某個職責實現的時候,使用策略模式來選擇具體的實現,一樣也能夠在策略模式的某個策略實現中,使用職責鏈模式來實現功能處理

  同理職責鏈模式也能夠和狀態模式組合使用

第24章 橋接模式(Bridge)

  24.1 場景問題

    24.1.1 發送提示消息

    24.1.2 不用模式的解決方案

實現簡化版本

using System;
using System.Collections;
using System.Collections.Generic;

namespace Test2 {
    public class Program {
        static void Main(string[] args) {

        }
    }

    public interface Message {
        void send(string message, string toUser);
    }

    public class CommonMessageSMS : Message {
        public void send(string message, string toUser) {
            Console.WriteLine("使用站內短消息的方式,發送消息'" + message + "'給" + toUser);
        }
    }

    public class CommonMessageEmail : Message {
        public void send(string message, string toUser) {
            Console.WriteLine("使用E-mail的方式,發送消息'" + message + "'給" + toUser);
        }
    }
}
View Code

實現發送加急消息

using System;
using System.Collections;
using System.Collections.Generic;

namespace Test2 {
    public class Program {
        static void Main(string[] args) {

        }
    }

    public interface Message {
        void send(string message, string toUser);
    }

    public class CommonMessageSMS : Message {
        public void send(string message, string toUser) {
            Console.WriteLine("使用站內短消息的方式,發送消息'" + message + "'給" + toUser);
        }
    }

    public class CommonMessageEmail : Message {
        public void send(string message, string toUser) {
            Console.WriteLine("使用E-mail的方式,發送消息'" + message + "'給" + toUser);
        }
    }

    public interface UrgencyMessage : Message {
        object watch(string messageId);
    }

    public class UrgencyMessageSMS : UrgencyMessage {
        public void send(string message, string toUser) {
            message = "加急: " + message;
            Console.WriteLine("使用站內短消息的方式,發送消息'" + message + "'給" + toUser);
        }

        public object watch(string messageId) {
            return null;
        }
    }

    public class UrgencyMessageEmail : UrgencyMessage {
        public void send(string message, string toUser) {
            message = "加急:" + message;
            Console.WriteLine("使用E-mail的方式,發送消息'" + message + "'給" + toUser);
        }

        public object watch(string messageId) {
            return null;
        }
    }
}
View Code

  24.1.3 有何問題

繼續添加特急消息的處理

繼續添加發送手機消息的處理方式

Q:有何問題

A:採用經過繼承來擴展的實現方式,有個明顯的缺點,擴展消息的種類不太容易.不一樣種類的消息具備不一樣的業務,也就是有不一樣的實現,在這種狀況下,每一個種類的消息,須要實現全部不一樣的消息發送方式

 更可怕的是,若是要新加入一種消息的發送方式,那麼會要求全部的消息種類都要加入這種新的發送方式的實現

 要是考慮業務功能上再擴展一下呢?好比,要求實現羣發消息,也就是一次能夠發送多條消息,這就意味着不少地方都得修改,太恐怖了

 那麼究竟該如何實現才能既實現功能,又能靈活地擴展呢?

  24.2 解決方案

    24.2.1 使用橋接模式來解決問題

Q:橋接模式的定義

A:將抽象部分與它的實現部分分離,使它們均可以獨立地變化

Q:應用橋接模式來解決問題的思路

A:仔細分析上面的示例,根據示例的功能要求,示例的變化具備兩個維度,一個緯度是抽象的消息這邊,包括普通消息,加急消息和特急消息,這幾個抽象的消息自己就具備必定的關係,加急消息和特急消息會擴展普通消息;另外一個維度是在具體的消息發送方式上,包括站內短消息,E-mail和手機短信息,這幾個方式是平等的,可被切換的方式.這兩個維度一共能夠組合出9種不一樣的可能性來

 想要解決這問題,就必需要把這兩個維度分開,也就是將抽象部分和實現部分分開,讓它們相互獨立,這樣就能夠實現獨立的變化,使擴展變得簡單

 橋接模式經過引入實現的接口,把實現部分從系統種分離出去.那麼,抽象這邊如何使用具體的實現呢?確定是用面向實現的接口來編程了,爲了讓抽象這邊可以很方便地與實現結合起來,把頂層的抽象接口改爲抽象類,在其中持有一個具體的實現部分的示例

 這樣一來,對於須要發送消息的客戶端而言,就只須要建立相應的消息對象,而後調用整個消息對象的方法就能夠了,這個消息對象會調用持有的真正的消息發送方式來把消息發送出去.也就是說客戶端只是想要發送消息而已,並不想關心具體如何發送.

    24.2.2 橋接模式的結構和說明

  [Abstraction] 抽象部分的接口,一般在這個對象中,要維護一個實現部分的對象引用,抽象對象裏面的部分,須要調用實現部分的對象來完成.這個對象中的方法,一般都是和具體的業務相關的方法

  [RefinedAbstraction] 擴展抽象部分的接口,一般在這些對象中,定義跟實際業務相關的方法,這些方法的實現一般會使用Abstraction中定義的方法,也可能須要調用實現部分的對象來完成

  [Implementor] 定義實現部分的接口,這個接口不用和Abstraction中的方法一致,一般是由Implementor接口提供基本的操做,而Abstraction中定義的是基於這些基本操做的業務方法,也就是說Abstraction定義了基於這些基本操做的較高層次的操做

  [ConcreteImplementor] 真正實現Implementor接口的對象

    24.2.3 橋接模式示例代碼

using System;
using System.Collections;
using System.Collections.Generic;

namespace Test2 {
    public class Program {
        static void Main(string[] args) {

        }
    }

    public interface Implementor {
        void operationImpl();
    }

    public abstract class Abstraction {
        protected Implementor impl;

        public Abstraction(Implementor impl) {
            this.impl = impl;
        }

        public virtual void operation() {
            impl.operationImpl();
        }
    }

    public class ConcreteImplementorA : Implementor {
        public void operationImpl() {
            
        }
    }

    public class ConcreteImplementorB : Implementor {
        public void operationImpl() {
            
        }
    }

    public class RefinedAbstraction : Abstraction {
        public RefinedAbstraction(Implementor impl) : base(impl) {
            
        }

        public void otherOperation() {
            
        } 
    }
}
View Code

    24.2.4 使用橋接模式重寫示例

using System;
using System.Collections;
using System.Collections.Generic;

namespace Test2 {
    public class Program {
        static void Main(string[] args) {
            MessageImplementor impl = new MessageSMS();
            
            AbstractMessage m = new CommonMessage(impl);
            m.sendMessage("請喝一杯茶","小李");
            
            m = new UrgencyMessage(impl);
            m.sendMessage("請喝一杯茶","小李");
            
            m = new SpecialUrgencyMessage(impl);
            m.sendMessage("請喝一杯茶","小李");
            
            
            
            impl = new MessageMobile();
            
            m = new CommonMessage(impl);
            m.sendMessage("請喝一杯茶","小李");
            
            m = new UrgencyMessage(impl);
            m.sendMessage("請喝一杯茶","小李");

            m = new SpecialUrgencyMessage(impl);
            m.sendMessage("請喝一杯茶","小李");
            
        }
    }

    public interface MessageImplementor {
        void send(string message, string toUser);
    }

    public abstract class AbstractMessage {
        protected MessageImplementor impl;

        public AbstractMessage(MessageImplementor impl) {
            this.impl = impl;
        }

        public virtual void sendMessage(string message, string toUser) {
            impl.send(message,toUser);
        }
    }

    public class MessageSMS : MessageImplementor {
        public void send(string message, string toUser) {
            Console.WriteLine("使用站內短消息的方式,發送消息'" + message + "'給" + toUser);
        }
    }

    public class MessageEmail : MessageImplementor {
        public void send(string message, string toUser) {
            Console.WriteLine("使用E-mail的方式,發送消息'" + message + "'給" + toUser);
        }
    }

    public class MessageMobile : MessageImplementor {
        public void send(string message, string toUser) {
            Console.WriteLine("使用手機短消息的方式,發送消息'" + message + "'給" + toUser);
        }
    }

    public class CommonMessage : AbstractMessage {
        public CommonMessage(MessageImplementor impl) : base(impl) {
            
        }

        public override void sendMessage(string message, string toUser) {
            base.sendMessage(message,toUser);
        }
    }

    public class UrgencyMessage : AbstractMessage {
        public UrgencyMessage(MessageImplementor impl) : base(impl) {
            
        }

        public override void sendMessage(string message, string toUser) {
            message = "加急: " + message;
            base.sendMessage(message,toUser);
        }

        public object watch(string messageId) {
            return null;
        }
    }

    public class SpecialUrgencyMessage : AbstractMessage {
        public SpecialUrgencyMessage(MessageImplementor impl) : base(impl) {
            
        }

        public override void sendMessage(string message, string toUser) {
            message = "特急: " + message;
            base.sendMessage(message,toUser);
        }
        
        public void hurry(string messageId) {
            
        }
    }
}
View Code

  24.3 模式講解

    24.3.1 認識橋接模式

Q:什麼是橋接

A:在橋接模式中,不太好理解的就是橋接概念,什麼是橋接?爲什麼須要橋接?如何橋接?把這些問題搞清楚了,也就基本明白橋接的含義了

 一個一個來,先看看什麼是橋接?所謂橋接,通俗點說就是在不一樣的東西之間搭一個橋,讓它們可以鏈接起來,能夠相互通信和使用.那麼在橋接模式中究竟是給什麼東西來搭橋呢?就是爲了被分離了的抽象部分和實現部分來搭橋,好比前面示例中在抽象的消息和具體消息發送之間搭個橋

 可是這裏要注意一個問題,在橋接模式中的橋接是單向的,也就是隻能是抽象部分的對象去使用具體實現部分的對象,而不能反過來,也就是個單向橋

Q:爲什麼須要橋接

A:爲了達到讓抽象部分和實現部�

相關文章
相關標籤/搜索