一文總結設計模式


前言

  • 看了很多寓教於學寫設計模式的,看的有點頭疼,注意力全都在故事上了,滿腦子都是鴨子,餐廳之類,還有一堆和設計模式不相關的話,翻書都翻的挺累的。
  • 這里我整理了下23種設計模式,沒什么多余的話,代碼演示,簡單粗暴,借鑒的地方都附上了參考鏈接(做個優秀的搬運工),沒附上的是自己總結的。
  • 借鑒的例子代碼,基本都做了一些精簡,如果相關例子寫的有什么不准確,麻煩在評論里面指出來,最好附上代碼,我會盡快修改文章中的相關實例
  • 23種設計模式,一文呈現,方便大家和自己查詢,也方便自己隨時修改,請配合文章旁邊的大綱食用。

總述

7種面向對象設計原則

設計原則名稱 定 義
單一職責原則(Single Responsibility Principle, SRP) 一個類只負責一個功能領域中的相應職責
開閉原則(Open-Closed Principle, OCP) 軟件實體應對擴展開放,而對修改關閉
里氏代換原則(Liskov Substitution Principle, LSP) 所有引用基類對象的地方能夠透明地使用其子類的對象
迪米特法則(Law of Demeter, LoD) 一個軟件實體應當盡可能少地與其他實體發生相互作用
接口隔離原則(Interface Segregation Principle, ISP) 使用多個專門的接口,而不使用單一的總接口
依賴倒轉原則(Dependence Inversion Principle, DIP) 抽象不應該依賴於細節,細節應該依賴於抽象
合成復用原則(Composite Reuse Principle, CRP) 盡量使用對象組合,而不是繼承來達到復用的目的
  • 設計模式可分為創建型(Creational),結構型(Structural)和行為型(Behavioral)三種

    • 創建型模式主要用於描述如何創建對象(5種)
    • 結構型模式主要用於描述如何實現類或對象的組合(7種)
    • 行為型模式主要用於描述類或對象怎樣交互以及怎樣分配職責(11種)
  • 圖解:花了我蠻多精力畫的,有需要到的,可以參照參照

    • 圖片是高清圖,下載后查看,體驗更好

設計模式

原則簡述

1. 單一職責原則

定義:一個類只有一個引起它變化的原因。

理解:對功能進行分類,代碼進行解耦,一個類只管一件事

舉例:就比如一個網絡請求框架大體分為:請求類,緩存類,配置類,不能把這3個混在一起,必須分為3個類去實現不同的功能。

2.開閉原則

定義:一個實體(類、函數、模塊等)應該對外擴展開放,對內修改關閉

理解:每次發生變化時,要通過新增代碼來增強現有類型的行為,而不是修改原有代碼。

舉例:就比如在軟件的生命周期內,因為產品迭代,軟件升級維護等原因,需要對原有代碼進行修改時,可能會給原有代碼引入錯誤,也可能使得我們對整個功能不得不進行重構,並且需要對原有代碼進行重新測試,這樣的話,對開發周期影響很大,所以開閉原則剛好解決這個問題。

3. 里氏替換原則

定義:繼承必須確保父類所擁有的性質在子類中仍然成立。

理解:在繼承類時,除了擴展一些新的功能之外,盡量不要刪除或者修改對父類方法的引用,也盡量不要重寫父類的方法。

舉例:你看啊,就比如Object有個方法,叫equals,如果不遵守里氏代替原則,它的子類重寫了equals這個方法,並且返回了個null,這個子類的下一個繼承者也會返回null,那么,在不同開發人員開發時,可能考慮不到這個問題,那么就可能導致程序崩潰。

4.迪米特法則

定義:一個模塊或對象應盡量少的與其他實體之間發生相互作用,使得系統功能模塊相對獨立,這樣當一個模塊修改時,影響的模塊就會越少,擴展起來更加容易。

理解:一個對象應該對其他對象有最少的了解;一個類應該對自己需要耦合或調用的類知道得最少,類的內部如何實現、如何復雜都與調用者或者依賴者沒關系,調用者或者依賴者只需要知道他需要的方法即可,其他的一概不關心。類與類之間的關系越密切,耦合度越大,當一個類發生改變時,對另一個類的影響也越大。

舉例:一般在使用框架的時候,框架的開發者會抽出一個類供外部調用,而這個主要的類像是一個中介一樣去調用框架里面的其他類,恰恰框架里面其他類一般都是不可訪問(調用)的,這個框架就遵守了迪米特原則,其他開發人員只關心調用的方法,並不需要關心功能具體如何實現。

5.接口隔離原則

定義:使用多個專門功能的接口,而不是使用單一的總接口

理解:在定義接口方法時應該合理化,盡量追求簡單最小,避免接口臃腫

舉例:在實際開發中,往往為了節省時間,可能會將多個功能的方法抽成一個接口,其實這設計理念不正確的,這樣會使接口處於臃腫的狀態,這時就需要合理的拆分接口中的方法,另外抽取成一個獨立的接口,避免原有的接口臃腫導致代碼理解困難。

6.依賴倒置原則

定義:細節應該依賴於抽象,而抽象不應該依賴於細節

理解:高層模塊不依賴低層次模塊的細節,不依賴具體的類,而是依賴於接口

舉例:比如說我們寫一個網絡框架,為了滿足不同開發者的需求,即能使用高效的OkHttp框架,也可以使用原生的API。那么是如何進行切換的呢,這個時候需要面向接口編程思想了,把一些網絡請求的方法封裝成一個接口,然后分別創建OkHttp和原生API的接口實現類,當然也可以擴展其他網絡框架的應用。

7.合成復用原則

定義:盡量使用對象組合,而不是繼承來達到復用的目的。

理解:它要求在軟件復用時,要盡量先使用組合或者聚合等關聯關系來實現,其次才考慮使用繼承關系來實現。

舉例:相當於我們開發軟件,一個模塊的構造,就像搭積木一樣,通過組合的形式,完成整體的構建;現在聲明式UI框架,對於這種思想比較貫徹。

參考

創建型模式

單例模式

餓漢模式

public class Singleton {
    // 只會實例化一次
    private static Singleton instance = new Singleton();       
 
    // 私有構造方法,防止被實例化
    private Singleton() {}
 
    public static Singleton getInstance() {
        return instance;
    }
}
  • 在未使用該類的時候,也會實例化對象,會造成資源浪費,除非在一定會用到該種類的場景,否則不建議使用。

懶漢模式

public class Singleton {
    // 持有私有靜態實例,防止被引用;賦值為null,目的是實現延遲加載;volatile修飾是禁止重排
    private volatile static Singleton instance = null;
 
    // 私有構造方法,防止被實例化
    private Singleton() {}
 
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (instance) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}
  • 使用volatile修飾,可禁止重排,在多線程場景也能保證正常運行;只會在使用的時候,才會去實例化對象,一般場景都可以使用。

靜態內部類模式

public class Singleton {
    // 私有構造方法,防止被實例化
    private Singleton() {}
 
    // 獲取實例
    public static Singleton getInstance() {
        return Inner.instance;
    }
 
    // 使用一個內部類來維護單例,只有在該類被加載的時候,才會實例化對象
    private static class Inner {
        private static Singleton instance = new Singleton();
    }
}
  • 餓漢模式升級版,解決了資源浪費問題,同時也能保證線程安全;只有在內部類被加載的時候,才會去實例化對象;相對於懶漢模式,靜態內部類的方式避免了排隊進同步代碼塊做null的判斷,性能優於懶漢模式,推薦使用。

枚舉模式

public enum Singleton {
    // 創建一個枚舉對象,該對象天生為單例
    INSTANCE;
    public Singleton getInstance(){
        return INSTANCE;
    }
}
  • 推薦:effective java中最佳的單例實現模式就是枚舉模式,JVM來幫我們保證線程安全和單一實例,在反射和序列化的場景中,仍能保證單一實例。

參考

工廠模式

簡單工廠模式

  • 以手機為例來說明
  • 定義一個接口,開機的時候,手機界面上,會顯示該手機系統
public interface Phone {
    //實施開機操作的時候,返回手機系統
    String startUp();
}
  • 實現一個Android手機類,實現Phone接口,返回“Android”手機系統
public class AndroidPhone implements Phone {
    @Override
    public String startUp() {
        return "Android";
    }
}
  • 實現一個IOS手機類
public class IOSPhone implements Phone {
    @Override
    public String startUp() {
        return "IOS";
    }
}
  • 創建手機工廠管理類
public class PhoneFactory {
    private static Phone mPhone;

    //根據系統關鍵字獲取相應手機對象
    public static Phone createPhone(String system) {
        switch (system) {
            case "Android":
                mPhone = new AndroidPhone();
                break;
            case "IOS":
                mPhone = new IOSPhone();
                break;
        }
        return mPhone;
    }
}
  • 使用
public void test() {
    Phone android = PhoneFactory.createPhone("Android");
    Phone ios = PhoneFactory.createPhone("IOS");

    System.out.print(android.startUp() + "\n");
    System.out.print(ios.startUp() + "\n");
}
  • 結果顯示:Android IOS

工廠模式

  • 工廠模式區別與簡單工廠模式的是,工廠模式不是用一個統一的工廠類來管理所有對象,而是每一個對象都有不同的工廠相對應。
  • 繼續以手機為例說明,需要針對Android和IOS手機這倆個實現類,去構建不同的工廠,這邊用接口去規范不同的工廠類
  • 手機工廠接口
public interface PhoneFactory {
    //獲取相關手機實例
    Phone getPhone();
}
  • 創建Android手機工廠
public class AndroidPhoneFactory implements PhoneFactory {
    @Override
    public Phone getPhone() {
        return new AndroidPhone();
    }
}
  • 創建IOS手機工廠
public class IOSPhoneFactory implements PhoneFactory {
    @Override
    public Phone getPhone() {
        return new IOSPhone();
    }
}
  • 使用
public void test() {
    PhoneFactory androidMiFactory = new AndroidPhoneFactory();
    PhoneFactory iosFactory = new IOSPhoneFactory();

    System.out.print(androidFactory.getPhone().startUp() + "\n");
    System.out.print(iosFactory.getPhone().startUp() + "\n");
}
  • 結果顯示:Android IOS

抽象工廠模式

  • 抽象模式是對工廠模式的進一步優化,工廠類不單單只能創建一個對象,而是能創建一組對象。
  • 在這里,大家可能存在一個疑惑,簡單工廠不也是創建一組對象嗎?是的,在這點上,倆者是非常非常相似的,區別在於:簡單工廠模式是外部傳值進去,以獲取不同對象;抽象工廠模式直接通過方法獲取對象,不需要傳值。
  • 繼續以手機為例說明,咱們用倆種方式來看看,中間工廠類來創建一組對象

1、第一種方式

  • 定義一個接口,這個接口里面是獲取一系列的手機系統的實例
public interface PhoneSystem {
    //獲取Android手機實例
    Phone getAndroid();
    
    //獲取IOS手機實例
    Phone getIOS();
}
  • 實現抽象工廠的這個接口
public class PhoneFactory implements PhoneSystem {
    @Override
    public Phone getAndroid() {
        return new AndroidPhone();
    }

    @Override
    public Phone getIOS() {
        return new IOSPhone();
    }
}
  • 使用
public void test() {
    PhoneSystem phoneSystem = new PhoneFactory();

    Phone android = phoneSystem.getXiaoMi();
    Phone ios = phoneSystem.getHuaWei();

    System.out.print(android.startUp() + "\n");
    System.out.print(ios.startUp() + "\n");
}
  • 結果顯示:Android IOS

2、第二種方式

  • 在這里思考下,抽象工廠模式,是在工廠類里面創建一組對象,與外層交互,不是通過關鍵字去返回相應對象,而是通過某個共性方法去返回“符合條件的實例”。
  • 這里假設一個場景:我們定義的手機對象,其中的開機功能,只在對應的手機上才會起作用(Android手機想開機,只能使用Android手機類中的開機方法,IOS也是如此),在這里,假設此款手機是Android手機。
  • 此處,我們不定義接口,直接創建抽象工廠類。
public class PhoneFactory {
    private static Phone instance;
    //模擬個數據
    private String SYSTEM = "IOS";

    public static Phone getInstance() {
        if("Android".equals(SYSTEM))
        {
            return new AndroidPhone();
        }
        if("IOS".equals(SYSTEM))
        {
            return new IOSPhone();
        }
        return null;
    }
}
  • 使用
public static void test() {
    Phone phone = PhoneFactory.getInstance();
    if (phone == null) 
        return;
    System.out.print(phone.startUp() + "\n");
}
  • 結果顯示:IOS

建造者模式

說明

  1. 在Computer 中創建一個靜態內部類 Builder,然后將Computer 中的參數都復制到Builder類中。
  2. 在Computer中創建一個private的構造函數,參數為Builder類型
  3. 在Builder中創建一個public的構造函數,參數為Computer中必填的那些參數,cpu 和ram。
  4. 在Builder中創建設置函數,對Computer中那些可選參數進行賦值,返回值為Builder類型的實例
  5. 在Builder中創建一個build()方法,在其中構建Computer的實例並返回

實現

public class Computer {
    private final String cpu;//必須
    private final String ram;//必須
    private final int usbCount;//可選
    private final String keyboard;//可選
    private final String display;//可選

    private Computer(Builder builder){
        this.cpu=builder.cpu;
        this.ram=builder.ram;
        this.usbCount=builder.usbCount;
        this.keyboard=builder.keyboard;
        this.display=builder.display;
    }
    
    public static class Builder{
        private String cpu;//必須
        private String ram;//必須
        private int usbCount;//可選
        private String keyboard;//可選
        private String display;//可選

        public Builder(String cup,String ram){
            this.cpu=cup;
            this.ram=ram;
        }

        public Builder setUsbCount(int usbCount) {
            this.usbCount = usbCount;
            return this;
        }
        public Builder setKeyboard(String keyboard) {
            this.keyboard = keyboard;
            return this;
        }
        public Builder setDisplay(String display) {
            this.display = display;
            return this;
        }        
        public Computer build(){
            return new Computer(this);
        }
    }
}

使用

Computer computer = new Computer.Builder("因特爾","三星")
    .setDisplay("三星24寸")
    .setKeyboard("羅技")
    .setUsbCount(2)
    .build();

參考

原型模式

定義

  • 原型模式(Prototype Pattern):使用原型實例指定創建對象的種類,並且通過拷貝這些原型創建新的對象。原型模式是一種對象創建型模式。

實現

public class Prototype implements Cloneable, Serializable {
    private static final long serialVersionUID = 1L;
    private String string;
    private SerializableObject obj;
 
    // 淺復制
    public Object clone() throws CloneNotSupportedException {
        Prototype proto = (Prototype) super.clone();
        return proto;
    }
 
    // 深復制
    public Object deepClone() throws IOException, ClassNotFoundException {
        // 寫入當前對象的二進制流
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(this);
 
        // 讀出二進制流產生的新對象 
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        return ois.readObject();
    }
 
    public String getString() {
        return string;
    }
 
    public void setString(String string) {
        this.string = string;
    }
 
    public SerializableObject getObj() {
        return obj;
    }
 
    public void setObj(SerializableObject obj) {
        this.obj = obj;
    }
}
 
class SerializableObject implements Serializable {
    private static final long serialVersionUID = 1L;
}

參考

結構型模式

適配器模式

類適配

  • 有一個已存在的將被適配的類
public class Adaptee {
    public void adapteeRequest() {
        System.out.println("被適配者的方法");
    }
}
  • 定義一個目標接口
public interface Target {
    void request();
}
  • 怎么才可以在目標接口中的 request() 調用 Adaptee 的 adapteeRequest() 方法呢?
    • 通過一個適配器類,實現 Target 接口,同時繼承了 Adaptee 類,然后在實現的 request() 方法中調用父類的 adapteeRequest() 即可實現
public class Adapter extends Adaptee implements Target{
    @Override
    public void request() {
        System.out.println("concreteTarget目標方法");
        super.adapteeRequest();
    }
}
  • 測試一下
public class Test {
    public static void main(String[] args) {
        Target adapterTarget = new Adapter();
        adapterTarget.request();
    }
}
  • 輸出
concreteTarget目標方法
被適配者的方法

對象適配

  • 電源適配器:定義輸出交流電接口,輸出220V交流電類
public interface AC {
    int outputAC();
}

public class AC220 implements AC {
    public final int output = 220;

    @Override
    public int outputAC() {
        return output;
    }
}
  • 適配器接口,outputDC5V() 方法則用於將輸入的電壓變換為 5V 后輸出
public interface DC5Adapter {
    int outputDC5V(AC ac);
}
  • 實現電源適配器
public class PowerAdapter implements DC5Adapter {
    public static final int voltage = 220; 
    
    @Override
    public int outputDC5V(AC ac) {
        int adapterInput = ac.outputAC();
        //變壓器...
        int adapterOutput = adapterInput / 44;
        System.out.println("使用PowerAdapter變壓適配器,輸入AC:" + adapterInput + "V" 
                           + ",輸出DC:" + adapterOutput + "V");
        return adapterOutput;
    }
}
  • 使用
public class Test {
    private List<DC5Adapter> adapters = new LinkedList<DC5Adapter>();

    public static void main(String[] args) {
        AC ac = new AC220(); //實例化220v對象
        DC5Adapter adapter = new PowerAdapter(); //實例化適配器
        adapter.outputDC5V(ac); 
    }
}
  • 輸出
使用PowerAdapter變壓適配器,輸入AC:220V,輸出DC:5V

接口適配

  • 在實際開發中,經常會遇到接口中定義了太多的方法,以致於有時我們在一些實現類中並不是都需要
public interface Sourceable {
    public void method1();
    public void method2();
}
  • 抽象類Wrapper:
public abstract class Wrapper implements Sourceable{
    public void method1(){}
    public void method2(){}
}
  • 可選擇的去實現,我們想要的方法
public class SourceSub extends Wrapper {
    @Override
    public void method1(){
        System.out.println("實現方法一");
    }
}

參考

裝飾模式

定義

  • 裝飾器模式(Decorator Pattern)允許向一個現有的對象添加新的功能,同時又不改變其結構。這種類型的設計模式屬於結構型模式,它是作為現有的類的一個包裝。

實現

  • 創建一個接口
public interface Shape {
    void draw();
}
  • 創建倆個實現類
public class Rectangle implements Shape {
    @Override
    public void draw() {
        System.out.println("Shape: Rectangle");
    }
}
public class Circle implements Shape {
    @Override
    public void draw() {
        System.out.println("Shape: Circle");
    }
}
  • 創建實現了 Shape 接口的抽象裝飾類
public abstract class ShapeDecorator implements Shape {
    protected Shape decoratedShape;

    public ShapeDecorator(Shape decoratedShape){
        this.decoratedShape = decoratedShape;
    }

    @Override
    public void draw(){
        decoratedShape.draw();
        System.out.println("Border Color: Red");
    }  
}
  • 測試
public class Test {
    public static void main(String[] args) {
        Shape circle = new Circle();
        ShapeDecorator circleShape = new ShapeDecorator(new Circle());
        ShapeDecorator rectangleShape = new ShapeDecorator(new Rectangle());
        System.out.println("Circle with normal border");
        circle.draw();

        System.out.println("\nCircle of red border");
        circleShape.draw();

        System.out.println("\nRectangle of red border");
        rectangleShape.draw();
    }
}
  • 結果
Circle with normal border
Shape: Circle

Circle of red border
Shape: Circle
Border Color: Red

Rectangle of red border
Shape: Rectangle
Border Color: Red

參考

代理模式

定義

  • 在代理模式(Proxy Pattern)中,一個類代表另一個類的功能。這種類型的設計模式屬於結構型模式;在代理模式中,我們創建具有現有對象的對象,以便向外界提供功能接口。
  • 其實每個模式名稱就表明了該模式的作用,代理模式就是多一個代理類出來,替原對象進行一些操作,比如我們在租房子的時候回去找中介,為什么呢?因為你對該地區房屋的信息掌握的不夠全面,希望找一個更熟悉的人去幫你做,此處的代理就是這個意思。再如我們有的時候打官司,我們需要請律師,因為律師在法律方面有專長,可以替我們進行操作,表達我們的想法。

實現

  • 創建一個接口
public interface Shape {
    void draw();
}
  • 實現
public class Circle implements Shape {
    @Override
    public void draw() {
        System.out.println("Shape: Circle");
    }
}
  • 創建一個代理類
public class ProxyShape implements Shape {
     private Circle circle;
    
    @Override
    public void draw() {
        if(circle == null){
            circle = new Circle();
        }
        circle.draw();
    }
}
  • 測試
public class Test {
    public static void main(String[] args) {
        Shape shapeProxy = new ProxyShape();
        shape.draw();  
    }
}
  • 結果
Shape: Circle

參考

外觀模式

定義

  • 外觀模式(Facade Pattern)隱藏系統的復雜性,並向客戶端提供了一個客戶端可以訪問系統的接口。這種類型的設計模式屬於結構型模式,它向現有的系統添加一個接口,來隱藏系統的復雜性
  • 這種模式涉及到一個單一的類,該類提供了客戶端請求的簡化方法和對現有系統類方法的委托調用
  • 外觀模式就是將他們的關系放在一個Facade類中,降低了類類之間的耦合度

實現

  • 實現類
public class CPU {
    public void startup(){
        System.out.println("cpu startup!");
    }

    public void shutdown(){
        System.out.println("cpu shutdown!");
    }
}
public class Memory {
    public void startup(){
        System.out.println("memory startup!");
    }
    
    public void shutdown(){
        System.out.println("memory shutdown!");
    }
}
public class Disk {
    public void startup(){
        System.out.println("disk startup!");
    }
    
    public void shutdown(){
        System.out.println("disk shutdown!");
    }
}
  • Facade類
public class Computer {
    private CPU cpu;
    private Memory memory;
    private Disk disk;

    public Computer(){
        cpu = new CPU();
        memory = new Memory();
        disk = new Disk();
    }

    public void startup(){
        cpu.startup();
        memory.startup();
        disk.startup();
    }

    public void shutdown(){
        cpu.shutdown();
        memory.shutdown();
        disk.shutdown();
    }
}
  • 測試
public class Test {
    public static void main(String[] args) {
        Computer computer = new Computer();
        computer.startup();
        computer.shutdown();
    }
}
  • 結果
cpu startup!
memory startup!
disk startup!
    
cpu shutdown!
memory shutdown!
disk shutdown!

參考

橋接模式

定義

  • 橋接(Bridge)是用於把抽象化與實現化解耦,使得二者可以獨立變化。這種類型的設計模式屬於結構型模式,它通過提供抽象化和實現化之間的橋接結構,來實現二者的解耦。
  • 這種模式涉及到一個作為橋接的接口,使得實體類的功能獨立於接口實現類。這兩種類型的類可被結構化改變而互不影響。
  • 橋接模式就是把事物和其具體實現分開,使他們可以各自獨立的變化。橋接的用意是:將抽象化與實現化解耦,使得二者可以獨立變化,像我們常用的JDBC橋DriverManager一樣,JDBC進行連接數據庫的時候,在各個數據庫之間進行切換,基本不需要動太多的代碼,甚至絲毫不用動,原因就是JDBC提供統一接口,每個數據庫提供各自的實現,用一個叫做數據庫驅動的程序來橋接就行了。
  • 總的來說:實現接口作為一個維度,繼承抽象類作為一個維度,倆者可以隨意組合

實現

第一個維度

  • 創建橋接實現接口
public interface DrawAPI {
   public void drawColor();
}
  • 創建實現了 DrawAPI 接口的實體橋接實現類
public class RedDraw implements DrawAPI {
   @Override
   public void drawColor() {
      System.out.println("color: red");
   }
}

public class GreenDraw implements DrawAPI {
   @Override
   public void drawColor() {
      System.out.println("color: green");
   }
}

第二個維度

  • 使用 DrawAPI 接口創建抽象類 Shape。
public abstract class Shape {
    protected DrawAPI drawAPI;
    
    protected Shape(DrawAPI drawAPI){
        this.drawAPI = drawAPI;
    }
    
    public abstract void draw();  
}
  • 實現了 Shape 接口的實體類
public class Circle extends Shape {
    public Circle(DrawAPI drawAPI) {
        super(drawAPI);
    }

    public void draw() {
        System.out.println("Drawing Circle");
        drawAPI.drawColor();
    }
}

public class Rectangle extends Shape {
    public Rectangle(DrawAPI drawAPI) {
        super(drawAPI);
    }

    public void draw() {
        System.out.println("Drawing Rectangle");
        drawAPI.drawColor();
    }
}
  • 測試
public class BridgeDemo {
   public static void main(String[] args) {
      Shape redCircle = new Circle(new RedDraw());
      Shape greenRectangle = new Rectangle(new GreenDraw());
 
      redCircle.draw();
      greenRectangle.draw();
   }
}
  • 結果
Drawing Circle
color: red

Drawing Rectangle
color: green

組合模式

定義

  • 組合模式(Composite Pattern),又叫部分整體模式,是用於把一組相似的對象當作一個單一的對象。組合模式依據樹形結構來組合對象,用來表示部分以及整體層次。這種類型的設計模式屬於結構型模式,它創建了對象組的樹形結構。
  • 這種模式創建了一個包含自己對象組的類。該類提供了修改相同對象組的方式。

實現

  • 抽象構件
abstract class Component {
    public abstract void operation();
    public void add(Component c) {
        throw new UnsupportedOperationException();
    }
    public void remove(Component c) {
        throw new UnsupportedOperationException();
    }
}
  • UnsupportedOperationException 是為了 Leaf 在繼承之后不用重寫該方法,因為這些方法是 Composite 對象需要重寫的,而單個對象不需要。
  • Leaf 類:
class ConcreteComponent1 extends Component {
    public void operation() {
        System.out.println("operation of concrete component 1 ");
    }
}

class ConcreteComponent2 extends Component {
    public void operation() {
        System.out.println("operation of concrete component 2 ");
    }
}

class ConcreteComponent3 extends Component {
    public void operation() {
        System.out.println("operation of concrete component 3 ");
    }
}
  • 組合類:
class Composite extends Component {
    private ArrayList<Component> children;
    public Composite() {
        children = new ArrayList<Component>();
    }
    public void operation() {
        for(Component child: children) {
            child.operation();
        }
    }

    public void add(Component c) {
        children.add(c);
    }

    public void remove(Component c) {
        children.remove(c);
    }
}
  • 調用:
public class Client {
    public static void main(String[] args) {
        Component c1 = new ConcreteComponent1();
        Component c2 = new ConcreteComponent2();
        Component c3 = new ConcreteComponent3();

        Component c = new Composite();
        c.add(c1);
        c.add(c2);
        c.add(c3);
        c.operation();

        c.remove(c2);
        c.operation();
    }
}
  • 運行結果:

img

參考

享元模式

定義

  • 享元模式(Flyweight Pattern)主要用於減少創建對象的數量,以減少內存占用和提高性能。這種類型的設計模式屬於結構型模式,它提供了減少對象數量從而改善應用所需的對象結構的方式。
  • 享元模式嘗試重用現有的同類對象,如果未找到匹配的對象,則創建新對象。

典型享元模式

  • 典型享元類(享元:Flyweight,w不是大寫)
class Flyweight {
    //內部狀態innerState作為成員變量,同一個享元對象其內部狀態是一致的
    private String innerState;
    public Flyweight(String innerState) {
        this.innerState = innerState;
    }
    //外部狀態outerState在使用時由外部設置,不保存在享元對象中,即使是同一個對象
    public void operation(String outerState) {
        //......
    }
}
  • 典型享元工廠類
class FlyweightFactory {
    //定義一個HashMap用於存儲享元對象,實現享元池
    private HashMap flyweights = newHashMap();
    
    public Flyweight getFlyweight(String key){
        //如果對象存在,則直接從享元池獲取
        if(flyWeights.containsKey(key)){
            return (Flyweight) flyweights.get(key);
        } else {
            //如果對象不存在,先創建一個新的對象添加到享元池中,然后返回
            Flyweight fw = newConcreteFlyweight();
            flyweights.put(key,fw);
            return fw;
        }
    }
}

通用寫法

  • 解決某些場景頻繁生成實例問題;使用泛型,節省寫判斷邏輯
/**
* 經典享元模式方法
* 場景:解決某些場景頻繁生成實例問題;使用泛型,節省寫判斷邏輯
* 使用:String s = classicFlyweight(String.class);
*/
private Map<String, Object> flyweightMap;
private <T> T classicFlyweight(Class<T> clazz) {
    T t;

    if (flyweightMap == null)
        flyweightMap = new HashMap<>();
    String key = clazz.getName();
    if (flyweightMap.get(key) != null) {
        t = (T) flyweightMap.get(key);
    }else {
        try {
            t = clazz.newInstance();
            flyweightMap.put(key, t);
        } catch (Exception e) {
            t = null;
        }
    }

    return t;
}

連接池的實現

  • 數據庫連接池
  • 通過連接池的管理,實現了數據庫連接的共享,不需要每一次都重新創建連接,節省了數據庫重新創建的開銷,提升了系統的性能!
public class ConnectionPool {
    private Vector<Connection> pool;
    
    //公有屬性
    private String url = "jdbc:mysql://localhost:3306/test";
    private String username = "root";
    private String password = "root";
    private String driverClassName = "com.mysql.jdbc.Driver";
 
    private int poolSize = 100;
    private static ConnectionPool instance = null;
    Connection conn = null;
 
    //構造方法,做一些初始化工作 
    private ConnectionPool() {
        pool = new Vector<Connection>(poolSize);
        for (int i = 0; i < poolSize; i++) {
            try {
                Class.forName(driverClassName);
                conn = DriverManager.getConnection(url, username, password);
                pool.add(conn);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
 
    //返回連接到連接池
    public synchronized void release() {
        pool.add(conn);
    }
 
    //返回連接池中的一個數據庫連接
    public synchronized Connection getConnection() {
        if (pool.size() > 0) {
            Connection conn = pool.get(0);
            pool.remove(conn);
            return conn;
        } else {
            return null;
        }
    }
}

參考

行為型模式

思考

策略模式和狀態模式

相同點

  • 兩者通過將行為和狀態拆分成一系列小的組件,由條件和狀態進行功能更替,這樣符合開閉原則,便於擴展。此外均可作為if else或者分支的替換方案;支持的最大行為和狀態均有限;

不同點

  • 策略模式中,類的功能是根據當前條件主動更改;
  • 狀態模式中,類的功能是被動由當前狀態更改;
  • 策略模式中每個行為或算法之間沒有關聯;
  • 狀態模式中的狀態之間有關聯,並且狀態本身控制着狀態轉移;

解釋器模式

定義

  • 解釋器模式(Interpreter Pattern)提供了評估語言的語法或表達式的方式,它屬於行為型模式。這種模式實現了一個表達式接口,該接口解釋一個特定的上下文。這種模式被用在 SQL 解析、符號處理引擎等。

實現

  • 創建一個表達式接口。
public interface Expression {
    public boolean interpret(String context);
}
  • 創建實現了上述接口的實體類。
public class TerminalExpression implements Expression {
    private String data;

    public TerminalExpression(String data){
        this.data = data; 
    }

    @Override
    public boolean interpret(String context) {
        if(context.contains(data)){
            return true;
        }
        return false;
    }
}
public class OrExpression implements Expression {
    private Expression expr1 = null;
    private Expression expr2 = null;

    public OrExpression(Expression expr1, Expression expr2) { 
        this.expr1 = expr1;
        this.expr2 = expr2;
    }

    @Override
    public boolean interpret(String context) {      
        return expr1.interpret(context) || expr2.interpret(context);
    }
}
public class AndExpression implements Expression {
    private Expression expr1 = null;
    private Expression expr2 = null;

    public AndExpression(Expression expr1, Expression expr2) { 
        this.expr1 = expr1;
        this.expr2 = expr2;
    }

    @Override
    public boolean interpret(String context) {      
        return expr1.interpret(context) && expr2.interpret(context);
    }
}
  • Test 使用 Expression 類來創建規則,並解析它們。
public class Test {
    //規則:Robert 和 John 是男性
    public static Expression getMaleExpression(){
        Expression robert = new TerminalExpression("John");
        Expression john = new TerminalExpression("male");
        return new OrExpression(robert, john);    
    }

    //規則:Julie 是一個已婚的女性
    public static Expression getMarriedWomanExpression(){
        Expression julie = new TerminalExpression("Julie");
        Expression married = new TerminalExpression("Married");
        return new AndExpression(julie, married);    
    }

    public static void main(String[] args) {
        Expression isMale = getMaleExpression();
        Expression isMarriedWoman = getMarriedWomanExpression();

        System.out.println("John is male? " + isMale.interpret("John"));
        System.out.println("Julie is a married women? " 
                           + isMarriedWoman.interpret("Married Julie"));
    }
}
  • 結果
John is male? true
Julie is a married women? true

參考

狀態模式

定義

  • 在狀態模式(State Pattern)中,類的行為是基於它的狀態改變的;我們創建表示各種狀態的對象和一個行為隨着狀態對象改變而改變的 context 對象。

實現

  • 創建一個接口。
public interface State {
    public void doAction(Context context);
}
  • 倆個狀態實現類.
public class StartState implements State {

    public void doAction(Context context) {
        System.out.println("Player is in start state");
        context.setState(this); 
    }

    public String toString(){
        return "Start State";
    }
}
public class StopState implements State {

    public void doAction(Context context) {
        System.out.println("Player is in stop state");
        context.setState(this); 
    }

    public String toString(){
        return "Stop State";
    }
}
  • 創建 Context 類。
public class Context {
    private State state;

    public Context(){
        state = null;
    }

    public void setState(State state){
        this.state = state;     
    }

    public State getState(){
        return state;
    }
}
  • 測試
public class StatePatternDemo {
    public static void main(String[] args) {
        Context context = new Context();

        StartState startState = new StartState();
        startState.doAction(context);

        System.out.println(context.getState().toString());

        StopState stopState = new StopState();
        stopState.doAction(context);

        System.out.println(context.getState().toString());
    }
}
  • 結果
Player is in start state
Start State
Player is in stop state
Stop State

參考

策略模式

定義

  • 在策略模式(Strategy Pattern)中,一個類的行為或其算法可以在運行時更改。這種類型的設計模式屬於行為型模式。
  • 在策略模式中,我們創建表示各種策略的對象和一個行為隨着策略對象改變而改變的 context 對象。策略對象改變 context 對象的執行算法。
  • 策略模式定義了一系列算法,並將每個算法封裝起來,使他們可以相互替換,且算法的變化不會影響到使用算法的客戶.

實現

  • 策略接口,定義策略執行接口
public interface Strategy {
    int calculate(int a, int b);
}
  • 具體策略類,實現策略接口,提供具體算法
// 加法算法
public class AddStrategy implements Strategy{
    @Override
    public int calculate(int a, int b) {
        return a + b;
    }
}
// 減法算法
public class SubtractStrategy implements Strategy{
    @Override
    public int calculate(int a, int b) {
        return a - b;
    }
}
  • 測試
public class Test{
    public static void main(String[] args) {
        Strategy strategy = new AddStrategy();
        // 輸出3
        System.out.println(strategy.calculate(1, 2));
        // 動態替換算法(策略)
        strategy = new SubtractStrategy();
        // 輸出-1
        System.out.println(strategy.calculate(1, 2));
    }
}
  • 測試main方法中,我們先使用"加法策略(算法)",然后調用calculate(1,2)方法得到結果3。然后動態替換策略為"減法策略(算法)",再次調用calculate(1,2)得到結果-1。

參考

觀察者模式

定義

  • 當對象間存在一對多關系時,則使用觀察者模式(Observer Pattern)。比如,當一個對象被修改時,則會自動通知依賴它的對象。

實現

  • 抽象觀察者接口(Observer)

    • 為所有的具體觀察者定義一個接口,在得到主題通知時更新自己。
public interface Observer {
    //更新內容
    public void update(String msg);
}
  • 觀察者接口實現類

    • 實現抽象觀察者角色所要求的更新接口,以便使本身的狀態與主題狀態保持一致。
public class TestObserver implements Observer{
    private String info;

    public TestObserver(String info){
        this.info = info;
    }

    @Override
    public void update(String msg) {
        System.out.println(info + "----" + msg);
    }
}
  • 主題類

    • 將有關狀態存入具體觀察者對象;當具體主題內部狀態放生改變時,通知所有注冊過的觀察者。
public class TestSubject {
    private List<Observer> mList = new ArrayList();
    
    //添加觀察者
    public void attach(Observer observer) {
        mList.add(observer);
    }
    
    //刪除觀察者
    public void detach(Observer observer) {
        mList.remove(observer);
    }
    
    //通知更新
    public void notify(String msg) {
        for (Observer observer : mList) {
            observer.update(msg);
        }
    }
}
  • 測試
public class TestMain {
    public static void main(String[] args) {
        Subject subject = new TestSubject();

        Observer observerA = new TestObserver("A:");
        Observer observerB = new TestObserver("B:");
        subject.attach(observerA);
        subject.attach(observerB);
        subject.notify("通知One");
        subject.detach(observerA);
        subject.notify("通知Two");
    }
}
  • 日志打印

image.png

中介者模式

定義

  • 中介者模式(Mediator Pattern)是用來降低多個對象和類之間的通信復雜性。這種模式提供了一個中介類,該類通常處理不同類之間的通信,並支持松耦合,使代碼易於維護。

實現

  • 統一實現的行為
public interface Mediator {
    public void showMessage(String msg);
}
  • 創建 user 類。
public class User implements Mediator{
    @Override
    public void showMessage(String message){
        System.out.println("User: " + message);
    }
}
  • 創建中介類 - 負責傳話
public class ChatRoom implements Mediator{
    @Override
    public void showMessage(String message){
        (new User()).showMessage(message);
    }
}
  • 測試
public class Test {
   public static void main(String[] args) {
      ChatRoom John = new ChatRoom();
      John.showMessage("Hi! John!");
   }
}
  • 結果
User: Hi! John!

備忘錄模式

定義

  • 備忘錄模式(Memento Pattern)保存一個對象的某個狀態,以便在適當的時候恢復對象。

實現

  • Original類是原始類,里面有需要保存的屬性value及創建一個備忘錄類,用來保存value值。Memento類是備忘錄類,Storage類是存儲備忘錄的類,持有Memento類的實例
public class Original {
    public String value;

    public Original(String value) {
        this.value = value;
    }

    public Memento createMemento(){
        return new Memento(value);
    }

    public void restoreMemento(Memento memento){
        this.value = memento.value;
    }
}

public class Memento {
    public String value;
 
    public Memento(String value) {
        this.value = value;
    }
}

public class Storage {
    public Memento memento;
    
    public Storage(Memento memento) {
        this.memento = memento;
    }
}
  • 測試
public class Test {
    public static void main(String[] args) {
        // 創建原始類
        Original origi = new Original("egg");
        // 創建備忘錄
        Storage storage = new Storage(origi.createMemento());

        // 修改原始類的狀態
        System.out.println("初始化狀態為:" + origi.value);
        System.out.println("修改后的狀態為:" + origi.value = "niu");

        // 回復原始類的狀態
        origi.restoreMemento(storage.memento);
        System.out.println("恢復后的狀態為:" + origi.value);
    }
}
  • 結果
初始化狀態為:egg
修改后的狀態為:niu
恢復后的狀態為:egg

參考

命令模式

定義

  • 命令模式(Command Pattern)是一種數據驅動的設計模式。請求以命令的形式包裹在對象中,並傳給調用對象。調用對象尋找可以處理該命令的合適的對象,並把該命令傳給相應的對象,該對象執行命令。

實現

  • 命令接口
public interface Order {
    public void execute();
}
  • 創建執行請求命令的實體類。
public class BuyStock implements Order {
    private String name = "ABC";
   	private int quantity = 10;

    @Override
    public void execute() {
        System.out.println("Stock [ Name: "+name+", Quantity: " + quantity +" ] bought");
    }
}
  • 創建命令調用類。
public class Broker {
    private List<Order> orderList = new ArrayList<Order>(); 

    public void takeOrder(Order order){
        orderList.add(order);      
    }

    public void placeOrders(){
        for (Order order : orderList) {
            order.execute();
        }
        orderList.clear();
    }
}
  • 測試
public class CommandPatternDemo {
    public static void main(String[] args) {
        Stock abcStock = new Stock();

        BuyStock buyStockOrder = new BuyStock(abcStock);
        Broker broker = new Broker();
        broker.takeOrder(buyStockOrder);

        broker.placeOrders();
    }
}
  • 結果
Stock [ Name: ABC, Quantity: 10 ] bought

參考

責任鏈模式

定義

  • 責任鏈模式(Chain of Responsibility Pattern)為請求創建了一個接收者對象的鏈。這種模式給予請求的類型,對請求的發送者和接收者進行解耦。

    責任鏈會將特定行為轉換為被稱作處理者的獨立對象。 每個檢查步驟都可被抽取為僅有單個方法的類, 並執行檢查操作。 請求及其數據則會被作為參數傳遞給該方法。

    該模式建議你將這些處理者連成一條鏈。 鏈上的每個處理者都有一個成員變量來保存對於下一處理者的引用。 除了處理請求外, 處理者還負責沿着鏈傳遞請求。 請求會在鏈上移動, 直至所有處理者都有機會對其進行處理。

    最重要的是: 處理者可以決定不再沿着鏈傳遞請求, 這可高效地取消所有后續處理步驟。

實現

  • 創建抽象的記錄器類
public abstract class AbstractLogger {
    public static int INFO = 1;
    public static int DEBUG = 2;
    public static int ERROR = 3;
    protected int level;

    //責任鏈中的下一個元素
    protected AbstractLogger nextLogger;

    public void setNextLogger(AbstractLogger nextLogger){
        this.nextLogger = nextLogger;
    }

    public void logMessage(int level, String message){
        if(this.level <= level){
            write(message);
        }
        if(nextLogger != null){
            nextLogger.logMessage(level, message);
        }
    }

    abstract protected void write(String message);
}
  • 創建擴展了該記錄器類的實體類
public class ConsoleLogger extends AbstractLogger {
    public ConsoleLogger(int level){
        this.level = level;
    }

    @Override
    protected void write(String message) {    
        System.out.println("Standard Console::Logger: " + message);
    }
}

public class ErrorLogger extends AbstractLogger {
    public ErrorLogger(int level){
        this.level = level;
    }

    @Override
    protected void write(String message) {    
        System.out.println("Error Console::Logger: " + message);
    }
}

public class FileLogger extends AbstractLogger {
    public FileLogger(int level){
        this.level = level;
    }

    @Override
    protected void write(String message) {    
        System.out.println("File::Logger: " + message);
    }
}
  • 測試
public class Test {
    private static AbstractLogger getChainOfLoggers(){
        AbstractLogger errorLogger = new ErrorLogger(AbstractLogger.ERROR);
        AbstractLogger fileLogger = new FileLogger(AbstractLogger.DEBUG);
        AbstractLogger consoleLogger = new ConsoleLogger(AbstractLogger.INFO);

        errorLogger.setNextLogger(fileLogger);
        fileLogger.setNextLogger(consoleLogger);
        return errorLogger;  
    }

    public static void main(String[] args) {
        AbstractLogger loggerChain = getChainOfLoggers();

        loggerChain.logMessage(AbstractLogger.INFO, "This is an information.");
        loggerChain.logMessage(AbstractLogger.DEBUG, "This is a debug level information.");
        loggerChain.logMessage(AbstractLogger.ERROR, "This is an error information.");
    }
}
  • 結果
Standard Console::Logger: This is an information.
File::Logger: This is a debug level information.
Standard Console::Logger: This is a debug level information.
Error Console::Logger: This is an error information.
File::Logger: This is an error information.
Standard Console::Logger: This is an error information.

參考

訪問者模式

定義

  • 在訪問者模式(Visitor Pattern)中,我們使用了一個訪問者類,它改變了元素類的執行算法。通過這種方式,元素的執行算法可以隨着訪問者改變而改變。這種類型的設計模式屬於行為型模式。根據模式,元素對象已接受訪問者對象,這樣訪問者對象就可以處理元素對象上的操作。

實現

  • Visitor類,存放要訪問的對象
public interface Visitor {
    public void visit(Subject sub);
}

public class MyVisitor implements Visitor {
    @Override
    public void visit(Subject sub) {
        System.out.println("visit the subject:"+sub.getSubject());
    }
}
  • Subject類,accept方法,接受將要訪問它的對象,getSubject()獲取將要被訪問的屬性
public interface Subject {
    public void accept(Visitor visitor);
    public String getSubject();
}

public class MySubject implements Subject {
    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }

    @Override
    public String getSubject() {
        return "love";
    }
}
  • 測試
public class Test {
    public static void main(String[] args) {
        Visitor visitor = new MyVisitor();
        Subject sub = new MySubject();
        sub.accept(visitor);    
    }
}
  • 結果:visit the subject:love

參考

迭代器模式

定義

  • 迭代器模式(Iterator Pattern)是 Java 和 .Net 編程環境中非常常用的設計模式。這種模式用於順序訪問集合對象的元素,不需要知道集合對象的底層表示。

實現

  • 創建接口
public interface Iterator {
    public boolean hasNext();
    public Object next();
}
public interface Container {
    public Iterator getIterator();
}
  • 創建實現了 Container 接口的實體類。該類有實現了 Iterator 接口的內部類 NameIterator。
public class NameRepository implements Container {
    public String names[] = {"Robert" , "John" ,"Julie" , "Lora"};

    @Override
    public Iterator getIterator() {
        return new NameIterator();
    }

    private class NameIterator implements Iterator {

        int index;

        @Override
        public boolean hasNext() {
            if(index < names.length){
                return true;
            }
            return false;
        }

        @Override
        public Object next() {
            if(this.hasNext()){
                return names[index++];
            }
            return null;
        }     
    }
}
  • 測試
public class Test {
    public static void main(String[] args) {
        NameRepository namesRepository = new NameRepository();
        for(Iterator iter = namesRepository.getIterator(); iter.hasNext();){
            String name = (String)iter.next();
            System.out.println("Name : " + name);
        }  
    }
}
  • 結果
Name : Robert
Name : John
Name : Julie
Name : Lora

參考

模板模式

定義

  • 在模板模式(Template Pattern)中,一個抽象類公開定義了執行它的方法的方式/模板。它的子類可以按需要重寫方法實現,但調用將以抽象類中定義的方式進行。
  • 超類中定義了一個算法的框架, 允許子類在不修改結構的情況下重寫算法的特定步驟

實現

  • 創建一個抽象類
public abstract class Game {
    abstract void initialize();
    abstract void startPlay();
    abstract void endPlay();

    //模板
    public final void play(){
        //初始化游戲
        initialize();
        //開始游戲
        startPlay();
        //結束游戲
        endPlay();
    }
}
  • 實現類
public class Football extends Game {
    @Override
    void endPlay() {
        System.out.println("Football Game Finished!");
    }

    @Override
    void initialize() {
        System.out.println("Football Game Initialized! Start playing.");
    }

    @Override
    void startPlay() {
        System.out.println("Football Game Started. Enjoy the game!");
    }
}
  • 測試
public class Test {
    public static void main(String[] args) {
        Game game = new Football();
        game.play();      
    }
}
  • 結果
Football Game Initialized! Start playing.
Football Game Started. Enjoy the game!
Football Game Finished!

參考

最后

  • 文章地址:study-notes
    • 提供良好的搬運格式,搬運使用時,請附上引用出處


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM