經常使用設計模式

1、設計模式的分類

整體來講設計模式分爲三大類:html

建立型模式,共五種:工廠方法模式、抽象工廠模式、單例模式、建造者模式、原型模式。算法

建立型模式是用來建立對象的模式,抽象了實例化的過程,幫助一個系統獨立於其餘關聯對象的建立、組合和表示方式。全部的建立型模式都有兩個主要功能:數據庫

  1.將系統所使用的具體類的信息封裝起來編程

  2.隱藏類的實例是如何被建立和組織的。外界對於這些對象只知道他們共同的接口,而不清楚其具體的實現細節。設計模式

正由於以上兩點,建立型模式在建立什麼(what)、由誰來建立(who)、以及什麼時候建立(when)這些方面,都爲設計者提供了儘量大的靈活性。數組

結構型模式,共七種:適配器模式、裝飾器模式、代理模式、外觀模式、橋接模式、組合模式、享元模式。安全

結構型模式討論的是類和對象的結構,它採用繼承機制來組合接口或實現(類結構型模式),或者經過組合一些對象實現新的功能(對象結構型模式)。這些結構型模式在某些方面具備很大的類似性,但側重點各有不一樣。多線程

行爲型模式,共十一種:策略模式、模板方法模式、觀察者模式、迭代子模式、責任鏈模式、命令模式、備忘錄模式、狀態模式、訪問者模式、中介者模式、解釋器模式。框架

行爲型設計模式關注的是對象的行爲,用來解決對象之間的聯繫問題。ide

2、設計模式的六大原則

總原則:開閉原則(Open Close Principle)

開閉原則就是說對擴展開放,對修改關閉。在程序須要進行拓展的時候,不能去修改原有的代碼,而是要擴展原有代碼,實現一個熱插拔的效果。因此一句話歸納就是:爲了使程序的擴展性好,易於維護和升級。想要達到這樣的效果,咱們須要使用接口和抽象類等,後面的具體設計中咱們會提到這點。

一、單一職責原則

不要存在多於一個致使類變動的緣由,也就是說每一個類應該實現單一的職責,如若否則,就應該把類拆分。

 

二、里氏替換原則(Liskov Substitution Principle)

里氏代換原則(Liskov Substitution Principle LSP)面向對象設計的基本原則之一。 里氏代換原則中說,任何基類能夠出現的地方,子類必定能夠出現。 LSP是繼承複用的基石,只有當衍生類能夠替換掉基類,軟件單位的功能不受到影響時,基類才能真正被複用,而衍生類也可以在基類的基礎上增長新的行爲。里氏代換原則是對「開-閉」原則的補充。實現「開-閉」原則的關鍵步驟就是抽象化。而基類與子類的繼承關係就是抽象化的具體實現,因此里氏代換原則是對實現抽象化的具體步驟的規範。—— From Baidu 百科

歷史替換原則中,子類對父類的方法儘可能不要重寫和重載。由於父類表明了定義好的結構,經過這個規範的接口與外界交互,子類不該該隨便破壞它。

 

三、依賴倒轉原則(Dependence Inversion Principle)

這個是開閉原則的基礎,具體內容:面向接口編程,依賴於抽象而不依賴於具體。寫代碼時用到具體類時,不與具體類交互,而與具體類的上層接口交互。

 

四、接口隔離原則(Interface Segregation Principle)

這個原則的意思是:每一個接口中不存在子類用不到卻必須實現的方法,若是否則,就要將接口拆分。使用多個隔離的接口,比使用單個接口(多個接口方法集合到一個的接口)要好。

 

五、迪米特法則(最少知道原則)(Demeter Principle)

就是說:一個類對本身依賴的類知道的越少越好。也就是說不管被依賴的類多麼複雜,都應該將邏輯封裝在方法的內部,經過public方法提供給外部。這樣當被依賴的類變化時,才能最小的影響該類。

最少知道原則的另外一個表達方式是:只與直接的朋友通訊。類之間只要有耦合關係,就叫朋友關係。耦合分爲依賴、關聯、聚合、組合等。咱們稱出現爲成員變量、方法參數、方法返回值中的類爲直接朋友。局部變量、臨時變量則不是直接的朋友。咱們要求陌生的類不要做爲局部變量出如今類中。

 

六、合成複用原則(Composite Reuse Principle)

原則是儘可能首先使用合成/聚合的方式,而不是使用繼承。

3、經常使用設計模式

單例模式(Singleton)--單線程

保證一個類僅有一個實例,並提供一個訪問它的全局訪問點,避免一個全局使用的類頻繁的建立和銷燬,節省系統資源,提升程序效率。怎麼建立惟一的實例?Java是這麼建立實例的 Person p = new Person();可是這麼建立會建立多個實例,因此咱們必須把構造器設爲私有,這樣其餘類就不能使用new來實例化一個類。

//單線程實現單例模式
class Singleton {
    private static Singleton instance;
    
    public Singleton() {};
    
    public static Singleton getInstance (){
        instance = new Singleton();
        
        return instance;
    }
}
 
public class Main {
 
    public static void main(String[] args) {
        Singleton s1 = Singleton.getInstance();
        Singleton s2 = Singleton.getInstance();
        //判斷兩個實例s1 s2是否爲同一個實例
        System.out.println(s1 == s2);
    }
 
}public class Singleton {
 
    //定義一個屬性,用來保存Singleton類對象的實例
    private static Singleton instance;
 
    //私有構造器,該類不能被外部類使用new方式實例化
    private Singleton(){
 
    }
 
    //外部經過該方法獲取Singleton類的惟一實例
    public static Singleton getInstance(){
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

 

這種實現方式並非線程安全的,當有多個線程同時調用Singleton.getInstance()方法時會產生多個實例。下節咱們來學習多線下如何實現單例模式。

Java多線程程序,線程執行順序是不肯定的,因此在同時多個線程調用Singleton.getInstance()方法時,存在建立多個實例的可能,會引發程序執行錯誤。那咱們該如何實現多線程下安全的建立一個惟一的實例呢?鎖,加鎖。在線程調用Singleton.getInstance()方法時,判斷instance == null ? 是,加鎖,其餘線程這時只能等待這個線程釋放鎖,才能進入臨界區。那如何加鎖,可使用synchronized。

 public static Singleton getInstance() {
        //synchronized加鎖同步會下降效率,這裏先判斷是否爲空
        //不爲空則不須要加鎖,提升程序效率
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }

 

單例模式優勢

  • 1 在內存中只有一個對象,節省內存空間。
  • 2 避免頻繁的建立銷燬對象,能夠提升性能。
  • 3 避免對共享資源的多重佔用。
  • 4 能夠全局訪問。

適用場景

  • 1 須要頻繁實例化而後銷燬的對象。
  • 2 建立對象時耗時過多或者耗資源過多,但又常常用到的對象。
  • 3 有狀態的工具類對象。
  • 4 頻繁訪問數據庫或文件的對象。
  • 5 以及其餘我沒用過的全部要求只有一個對象的場景。

工廠方法模式(Factory Method)

定義一個用於建立對象的接口,讓子類決定實例化哪個類,工廠方法使一個類的實例化延遲到其子類。

類圖:

 

  • 1.不少工廠都有一些相同的行爲,好比汽車工廠。咱們須要抽象這些相同的行爲成接口,每一個工廠都實現這個接口。
public interface IFactory {
 
    public void createProduct();
}

 

  • 2.生產相同的產品每一個工廠所使用的方法可能不一樣,因此具體如何生產產品由具體工廠實現。
public class Factory implements IFactory {
 
    @Override
    public void createProduct() {
 
    }
}

 

工廠模式兩要點:

  •  

1.工廠接口是工廠方法模式的核心,與調用者直接交互用來提供產品。

2.工廠實現決定如何實例化產品,是實現擴展的途徑,須要有多少種產品,就須要有多少個具體的工廠實現。

  •  

適用場景:

  •  

1.在任何須要生成複雜對象的地方,均可以使用工廠方法模式。有一點須要注意的地方就是複雜對象適合使用工廠模式,而簡單對象,特別是只須要經過new就能夠完成建立的對象,無需使用工廠模式。

2.工廠模式是一種典型的解耦模式,迪米特法則在工廠模式中表現的尤其明顯。假如調用者本身組裝產品須要增長依賴關係時,能夠考慮使用工廠模式。將會大大下降對象之間的耦合度。

3.當須要系統有比較好的擴展性時,能夠考慮工廠模式,不一樣的產品用不一樣的實現工廠來組裝。

 

 

抽象工廠模式(Abstract Factory)

爲建立一組相關或相互依賴的對象提供一個接口,並且無需指定他們的具體類。

抽象工廠是工廠模式的升級版,他用來建立一組相關或者相互依賴的對象。來看下抽象工廠模式的類圖:

 

上節學習了工廠模式,類的建立依賴工廠類,程序須要擴展時,咱們必須建立新的工廠類。工廠類是用來生產產品的,那咱們也能夠把「工廠類當成咱們要生產的產品」,因此抽象工廠就是「工廠的工廠」,即生產工廠的工廠。下面經過一個例子來深刻理解。

經過一個例子,來加深對抽象工廠的理解。

//CPU工廠接口
public interface CPUFactory {
    public void createCPU();
}
//IntelCPU工廠
public class IntelCPU implements CPUFactory {
    @Override
    public void createCPU() {
        System.out.println("Intel CPU");
    }
}
//AMDCPU工廠
public class AMDCPU implements CPUFactory {
    @Override
    public void createCPU() {
        System.out.println("AMD CPU");
    }
}
//建立抽象工廠類接口
public interface Provider {
    public CPUFactory createCPUFactory();
}
public class InterCPUFactory implements Provider {
    @Override
    public CPUFactory createCPUFactory() {
        return new InterCPU();
    }
}
public class AMDCPUFactory implements Provider {
    @Override
    public CPUFactory createCPUFactory() {
        return new AMDCPU();
    }
}
public static void main(String[] args) {
        //建立一個生產CPU工廠的工廠
        Provider cpufactory = new InterCPUFactory();
        //經過CPU工廠的工廠建立一個IntelCPU工廠
        CPUFactory intelcpu = cpufactory.createCPUFactory();
        //IntelCPU工廠生產intelCPU
        intelcpu.createCPU();
}

 

抽象工廠的優勢:

抽象工廠模式除了具備工廠方法模式的優勢外,最主要的優勢就是能夠在類的內部對產品族進行約束。所謂的產品族,通常或多或少的都存在必定的關聯(例如不一樣廠商生產CPU)。

適用場景:

一個繼承體系中,若是存在着多個等級結構(即存在着多個抽象類),而且分屬各個等級結構中的實現類之間存在着必定的關聯或者約束,就可使用抽象工廠模式。

 

建造者模式(Builder)

將一個複雜的構建與其表示相分離,使得一樣的構建過程能夠建立不一樣的表示。主要解決在軟件系統中,有時候面臨着"一個複雜對象"的建立工做,因爲需求的變化,這個複雜對象的某些部分常常面臨着劇烈的變化,一些基本部件不會變。因此須要將變與不變分離。與抽象工廠的區別:在建造者模式裏,有個指導者(Director),由指導者來管理建造者,用戶是與指導者聯繫的,指導者聯繫建造者最後獲得產品。即建造者模式能夠強制實行一種分步驟進行的建造過程。

建造者類圖:

 

建造者模式四要素:

  •  

1.產品類Product:通常是一個較爲複雜的對象,也就是說建立對象的過程比較複雜,通常會有比較多的代碼量。​​​​​​​

2.抽象建造者類Builder: 將建造的具體過程交與它的子類來實現,這樣更容易擴展。​​​​​​​

3.建造者類ConcreteBuilder: 組建產品;返回組建好的產品。​​​​​​​

4.指導類Director: 負責調用適當的建造者來組建產品,指導類通常不與產品類發生依賴關係,與指導類直接交互的是建造者類。

彷佛很抽象。舉個例子:前面你建立了一個生產保時捷的工廠,生產一臺保時捷911須要的主要部件都同樣(引擎,輪子,方向盤...)和流程是不變的,變的是引擎,輪子,控制系統等等部件具體實現,這些部件的生產交由具體的builder去生產。

/抽象生產者
public interface Builder {
 
    void buildPartA();
    void buildPartB();
    void buildPartC();
 
    Product buildProduct();
}
//具體生產者
public class ConcreteBuilder implements Builder {
 
    Product product;
 
    @Override
    public void buildPartA() {
 
    }
 
    @Override
    public void buildPartB() {
 
    }
 
    @Override
    public void buildPartC() {
 
    }
 
    @Override
    public Product buildProduct() {
        return product;
    }
}
//產品由各個組件組成
public class Product {
 
    //partA
    //partB
    //partC
}
//指導者,產品生產流程規範
public class Director {
 
    Builder builder;
    //由具體的生產者來生產產品
    public Director(Builder builder) {
        this.builder = builder;
    }
    //生產流程
    public void buildProduct(){
        builder.buildPartA();
        builder.buildPartB();
        builder.buildPartC();
    }
}
public static void main(String[] args) {
        //只須要關心具體建造者,無需關心產品內部構建流程。
        //若是須要其餘的複雜產品對象,只須要選擇其餘的建造者.
        Builder builder = new ConcreteBuilder();
        //把建造者注入指導者
        Director director = new Director(builder);
        //指導者負責流程把控
        director.buildProduct();
        // 建造者返回一個組合好的複雜產品對象
        Product product = builder.buildProduct();
    }

 

建造者模式優勢:

  •  

1.建造者模式的封裝性很好。使用建造者模式能夠有效的封裝變化,在使用建造者模式的場景中,通常產品類和建造者類是比較穩定的,所以,將主要的業務邏輯封裝在指導者類中對總體而言能夠取得比較好的穩定性。​​​​​​​

2.建造者模式很容易進行擴展。若是有新的需求,經過實現一個新的建造者類就能夠完成。

 

適用場景:須要生成的對象具備複雜的內部結構;須要生成的對象內部屬性自己相互依賴。

 

模板方法模式(Template Method)

定義一個操做中算法的框架,而將一些步驟延遲到子類中,使得子類能夠不改變算法的結構便可重定義該算法中的某些特定步驟。

類圖:

 

模板方法模式是編程中常常用到的模式,其很是簡單,AbstractClass叫抽象模板,其方法分爲3類:

  •  

1.抽象方法:父類中只聲明但不加以實現,而是定義好規範,而後由它的子類去實現。

  •  
  •  

2.模版方法:由抽象類聲明並加以實現。通常來講,模版方法調用抽象方法來完成主要的邏輯功能,而且,模版方法大多會定義爲final類型,指明主要的邏輯功能在子類中不能被重寫。

  •  
  •  

3.鉤子方法:由抽象類聲明並加以實現。可是子類能夠去擴展,子類能夠經過擴展鉤子方法來影響模版方法的邏輯。

  •  

實現類用來實現細節。抽象類中的模版方法正是經過實現類擴展的方法來完成業務邏輯。

如今要實現一個對無序數組從小到大排序並打印數組的類。排序算法有不少種,打印功能固定的。定義一個AbstractClass定義抽象排序方法由子類去實現;模板類實現打印方法。

//抽象模板類
public abstract class AbstractSort {
 
    public abstract void sort(int[] array);
    //防止子類覆蓋使用final修飾
    public final void printArray(int[] array) {
        sort(array);
        for (int i = 0; i < array.length; i++) {
            System.out.println(array[i]);
        }
    }
}
//具體實現類
public class QuickSort extends AbstractSort {
    @Override
    public void sort(int[] array) {
        //使用快排算法實現
    }
}
public class MergeSort extends AbstractSort {
    @Override
    public void sort(int[] array) {
        //使用歸併排序算法實現
    }
}
public static void main(String[] args) {
        int[] arr = {3,5,2,45,243,341,111,543,24};
        //AbstractSort s = new MergeSort();
        AbstractSort s = new QuickSort();
        s.printArray(arr);
    }

 

模板方法模式優勢:

  •  

1.容易擴展。通常來講,抽象類中的模版方法是不易反生改變的部分,而抽象方法是容易反生變化的部分,所以經過增長實現類通常能夠很容易實現功能的擴展,符合開閉原則。

  •  
  •  

2.便於維護。對於模版方法模式來講,正是因爲他們的主要邏輯相同,才使用了模版方法。

  •  

適用場景:

在多個子類擁有相同的方法,而且這些方法邏輯相同時,能夠考慮使用模版方法模式。在程序的主框架相同,細節不一樣的場合下,也比較適合使用這種模式。

上節你生產了保時捷911和Cayma,如今要對其進行檢測。檢測程序順序:可否啓動start,加油門speeup,剎車brake,熄火stop。因爲檢測標準不同,請你用模板方法模式來實現。

 

適配器模式(Adapter Class/Object)

是指將一個接口轉換成客戶端但願的另一個接口,該模式使得本來不兼容的類能夠一塊兒工做。

舉個例子:macbook pro有一個HDMI接口,一條HDMI接口的數據線,如今要外接顯示器,而顯示器只有VGI接口,咱們須要一個HDMI-VGI轉換器,這個轉換器其實起到的做用就是適配器,讓兩個不兼容的接口能夠一塊兒工做。

類圖:

適配器有4種角色:

  •  

1.目標抽象角色(Target):定義客戶所期待的使用接口。(GVI接口)

  •  
  •  

2.源角色(Adaptee):須要被適配的接口。(HDMI接口)

  •  
  •  

3.適配器角色(Adapter):把源接口轉換成符合要求的目標接口的設備。(HDMI-VGI轉換器)

  •  
  •  

4.客戶端(client):例子中指的VGI接口顯示器。

  •  

繼續學習 

把HDMI接口轉換成VGI接口,使得macbook pro能夠外接顯示器。

//HDMI接口,須要被適配的接口
public interface HDMIPort {
    void workByHDMI();
}
//VGI接口,客戶端所期待的接口
public interface VGIPort {
    void workByVGI();
}
//將HDMI接口轉換爲VGI,這就是適配器
public class HDMIToVGI implements VGIPort{
 
    HDMIPort hdmiPort;
 
    public HDMIToVGI(HDMIPort hdmiPort) {
        this.hdmiPort = hdmiPort;
    }
    //將HDMI接口轉換爲VGI接口
    @Override
    public void workByVGI() {
        hdmiPort.workByHDMI();
    }
}
 public static void main(String[] args) {
        //定義一個HDMI接口
        HDMIPort hdmiPort = new HDMIPort() {
            @Override
            public void workByHDMI() {
                //hdmi接口工做方式
            }
        };
        //將HDMI接口轉換爲VGI接口
        VGIPort vgiPort = new HDMIToVGI(hdmiPort);
        //通過轉換HDMI接口變成了VGI接口
        vgiPort.workByVGI();
    }

 

適配器模式優勢:

  •  

1.可讓任何兩個沒有關聯的類一塊兒運行。​​​​​​​

2.提升了類的複用。​​​​​​​

3.增長了類的透明度。​​​​​​​

4.靈活性好。

適配器模式缺點:過多地使用適配器,會讓系統很是零亂,不易總體進行把握。

適用場景:

1.系統須要使用現有的類,而此類的接口不符合系統的須要。​​​​​​​

2.想要創建一個能夠重複使用的類,用於與一些彼此之間沒有太大關聯的一些類,包括一些可能在未來引進的類一塊兒工做,這些源類不必定有一致的接口。​​​​​​​

3.經過接口轉換,將一個類插入另外一個類系中。

  •  

外觀模式(Facade)

爲子系統中的一組接口提供一個一致的界面,Facade模式定義了一個高層接口,這個接口使得這一子系統更加容易使用。隱藏系統的複雜性,並向客戶端提供了一個客戶端能夠訪問系統的接口。下降訪問複雜系統的內部子系統時的複雜度。

類圖:

 

在客戶端和複雜系統之間再加一層,將調用順序、依賴關係等處理好。舉個例子:咱們常常用的電腦,開機實際上是個很是複雜的過程,而咱們只須要按開機按鈕就能夠了。

模擬電腦啓動,假設電腦啓動順序:啓動CPU,啓動內存,啓動硬盤,加載數據等。

public class CPU {
 
    public void startup(){
        System.out.println("啓動CPU");
    }
}
public class Memory {
 
    public void startup(){
        System.out.println("啓動內存");
    }
}
public class Disk {
 
    public void startup(){
        System.out.println("啓動硬盤");
    }
}
//facade
public class Computer {
 
    CPU cpu;
    Memory memory;
    Disk disk;
 
    public Computer(){
        cpu = new CPU();
        memory = new Memory();
        disk = new Disk();
    }
 
    public void start(){
        cpu.startup();
        memory.startup();
        disk.startup();
    }
}
public static void main(String[] args) {
        Computer computer = new Computer();
        //啓動computer是個很複雜的過程,咱們並不須要知道其啓動各個子系統的加載過程
        //只須要調用computer爲各個子系統提供統一的一個接口start()就能夠啓動computer了
        computer.start();
    }

 

外觀模式優勢:

  •  

1.減小系統相互依賴。​​​​​​​​​​​​​​

2.提升靈活性。

​​​​​​​3.提升了安全性。

適用場景:

 

1.爲複雜的模塊或子系統提供外界訪問的模塊。​​​​​​​

2.客戶程序與抽象類的實現部分之間存在着很大的依賴性。引入facade 將這個子系統與客戶以及其餘的子系統分離,能夠提升子系統的獨立性和可移植性。

  •  

裝飾器模式(Decorator)

對客戶透明的方式動態地給一個對象附加上更多的責任,同時又不改變其結構。裝飾模式能夠在不使用創造更多子類的狀況下,將對象的功能加以擴展。

類圖:

 

1.抽象構件(Component)角色:給出一個抽象接口,以規範準備接收附加責任的對象。​​​​​​​

2.具體構件(ConcreteComponent)角色:定義一個將要接收附加責任的類。​​​​​​​

3.裝飾(Decorator)角色:持有一個構件(Component)對象的實例,並定義一個與抽象構件接口一致的接口。​​​​​​​

4.具體裝飾(ConcreteDecorator)角色:負責給構件對象「貼上」附加的責任。

Java IO中就是典型的裝飾器

//InputStream提供的基本方法(Component)
public abstract class InputStream implements Closeable {
 
}
//默認目標實現類(ConcreteComponent)
public class FileInputStream extends InputStream {
 
}
/*裝飾實現類(FilterInputStream)必定是繼承或實現原始接口(InputStream)的,內部有包含一個原始接口的超類(其實就是某個默認目標實現類)*/
//Decorator
public class FilterInputStream extends InputStream {
    /**
     * The input stream to be filtered.
     */
    protected volatile InputStream in;
 
    protected FilterInputStream(InputStream in) {
        this.in = in;
    }
}
//具體裝飾類(ConcreteDecorator)
public class BufferedInputStream extends FilterInputStream {
 
    public BufferedInputStream(InputStream in) {
        this(in, DEFAULT_BUFFER_SIZE);
    }
}
//具體裝飾類(ConcreteDecorator)
public class DataInputStream extends FilterInputStream implements DataInput {
 
    public DataInputStream(InputStream in) {
        super(in);
    }
}

 

裝飾器模式優勢:

1.裝飾類和被裝飾類能夠獨立發展,不會相互耦合。​​​​​​​

2.裝飾模式是繼承的一個替代模式,裝飾模式能夠動態擴展一個實現類的功能。就增長功能來講,裝飾器模式相比生成子類更爲靈活。

適用場景:

1.擴展一個類的功能。​​​​​​​

2.動態增長功能,動態撤銷。

觀察者模式(Observer)

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

類圖:

 

 

1.抽象主題(Subject)角色:把全部對觀察者對象的引用保存在一個集合中,每一個抽象主題角色均可以有任意數量的觀察者。抽象主題提供一個接口,能夠增長和刪除觀察者角色。通常用一個抽象類和接口來實現。​​​​​​​

2.抽象觀察者(Observer)角色:爲全部具體的觀察者定義一個接口,在獲得主題的通知時更新本身。​​​​​​

3.具體主題(ConcreteSubject)角色:在具體主題內部狀態改變時,給全部登記過的觀察者發出通知。具體主題角色一般用一個子類實現。​​​​​​​

4.具體觀察者(ConcreteObserver)角色:該角色實現抽象觀察者角色所要求的更新接口,以便使自己的狀態與主題的狀態相協調。一般用一個子類實現。若是須要,具體觀察者角色能夠保存一個指向具體主題角色的引用。

控件按鈕、報警器等都是觀察者模式。

public interface Subject {
    //添加觀察者
    void attach(Observer o);
    //刪除觀察者
    void detach(Observer o);
    //通知觀察者
    void notifyObservers();
    //發生某事
    void doSomeThings()
}
//觀察者
public interface Observer {
 
    void update();
}
public class ConcreteSubject implements Subject {
 
    ArrayList<Observer> observers = new ArrayList<>();
 
    @Override
    public void attach(Observer o) {
        observers.add(o);
    }
 
    @Override
    public void detach(Observer o) {
        observers.remove(o);
    }
 
    @Override
    public void notifyObservers() {
        for (Observer o : observers) {
            o.update();
        }
    }
 
    public void doSomeThings(){
        //doSomeThings
        notifyObservers();//通知觀察者
    }
}
//具體觀察者
public class ConcreteObserver implements Observer {
    @Override
    public void update() {
        System.out.println("我觀察到subject發生了某事");
    }
}
public static void main(String[] args) {
        Subject cs = new ConcreteSubject();
        //添加觀察者
        cs.attach(new ConcreteObserver());
        //subject發生了某事,通知觀察者
        cs.doSomeThings();
    }

 

觀察者模式優勢:

  •  

1.觀察者和被觀察者是抽象耦合的。​​​​​​​

2.創建一套觸發機制。

觀察者模式缺點:

 

1.若是一個被觀察者對象有不少的直接和間接的觀察者的話,將全部的觀察者都通知到會花費不少時間。​​​​​​​

2.若是在觀察者和觀察目標之間有循環依賴的話,觀察目標會觸發它們之間進行循環調用,可能致使系統崩潰。​​​​​​​

3.觀察者模式沒有相應的機制讓觀察者知道所觀察的目標對象是怎麼發生變化的,而僅僅只是知道觀察目標發生了變化。

  •  

適用場景:

1.當一個抽象模型有兩個方面, 其中一個方面依賴於另外一方面。將這兩者封裝在獨立的對象中以使它們能夠各自獨立地改變和複用。​​​​​​​

2.當對一個對象的改變須要同時改變其它對象, 而不知道具體有多少對象有待改變。

3.當一個對象必須通知其它對象,而它又不能假定其它對象是誰。換言之, 你不但願這些對象是緊密耦合的。

策略模式(Strategy)

定義一系列的算法,把它們一個個封裝起來, 而且使它們可相互替換。本模式使得算法可獨立於使用它的客戶而變化。

類圖:

 

1.Strategy:策略接口,用來約束一系列具體的策略算法。Context使用這個接口來調用具體的策略,實現定義的策略。​​​​​​

2.ConcreteStrategy:具體的策略實現,也就是具體的算法實現。​​​​​​​

3.Context:上下午,負責與具體的策略交互,一般上下文會持有一個真正的策略實現。

策略模式是把一個類中常常改變或者未來可能改變的部分提取出來做爲一個接口,而後在類中包含這個對象的實例,這樣類的實例在運行時就能夠隨意調用實現了這個接口的類的行爲。

如今咱們要根據不一樣需求,計算兩個數的四則運算( + - * /)

//策略定義算法的接口
public interface Strategy {
    int calculate(int num1,int num2);
}
//具體算法,加法
public class OperationAdd implements Strategy {
    @Override
    public int calculate(int num1, int num2) {
        return num1 + num2;
    }
}
//具體算法,減法
public class OperationSubstract implements Strategy {
    @Override
    public int calculate(int num1, int num2) {
        return num1 - num2;
    }
}
//具體算法,乘法
public class OperationMultiply implements Strategy {
    @Override
    public int calculate(int num1, int num2) {
        return num1 * num2;
    }
}
//具體算法,除法
public class OperationDivide implements Strategy {
    @Override
    public int calculate (int num1, int num2){
        int res = 0;
        try {
            res = num1 / num2;
        }catch (Exception e) {
            e.printStackTrace();
        }
        return res;
    }
}
//上下文
public class Context {
    //持有一個具體策略對象
    private Strategy strategy;
 
    //傳入一個具體策略對象
    public Context(Strategy strategy) {
        this.strategy =strategy;
    }
 
    public int calculate(int num1,int num2){
        //調用具體策略對象進行算法運算
        return strategy.calculate(num1,num2);
    }
}
 public static void main(String[] args) {
        //計算 1 + 1
        Context context = new Context(new OperationAdd());
        System.out.println("1 + 1 = " + context.calculate(1,1));
        //計算 1 - 1
        context = new Context(new OperationSubstract());
        System.out.println("1 - 1 = " +context.calculate(1,1));
    }

 

策略模式優勢:

1.算法能夠自由切換。​​​​​​​

2.避免使用多重條件判斷。​​​​​​​

3.擴展性良好。

策略模式缺點:

1.策略類會增多。​​​​​​​

2.全部策略類都須要對外暴露。

適用場景:

1.若是在一個系統裏面有許多類,它們之間的區別僅在於它們的行爲,那麼使用策略模式能夠動態地讓一個對象在許多行爲中選擇一種行爲。​​​​​​​

2.一個系統須要動態地在幾種算法中選擇一種。​​​​​​​​​​​​​​

3.一個類定義了多種行爲, 而且這些行爲在這個類的操做中以多個條件語句的形式出現。將相關的條件分支移入它們各自的Strategy類中以代替這些條件語句。

 

轉載自:https://www.cnblogs.com/geek6/p/3951677.html  

轉載自:https://blog.csdn.net/heijunwei/article/details/82056313

相關文章
相關標籤/搜索