設計模式-建立型

image

單例模式

單例模式,顧名思義就是一個類只有一個實例。
單例主要的好處就是,1:能夠解決資源訪問衝突的問題。2:減小資源浪費。java

單例的實現方式

1:餓漢式redis

在類加載的時候就實力化對象,不支持延遲加載。編程

public class HungryDemo {
    private AtomicInteger id = new AtomicInteger(0);
    public static HungryDemo instance = new HungryDemo();

    private HungryDemo() {
    }

    public static HungryDemo getInstance() {
        return instance;
    }

    public int getId() {
        return id.incrementAndGet();
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                try {
                    Thread.sleep(new Random().nextInt(10000));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                HungryDemo instance = HungryDemo.getInstance();
                System.out.println(instance + "==>" + instance.getId());
            }).start();
        }
    }
}

2:懶漢式安全

支持延遲加載,在使用的時候才真正加載。併發

public class LazyDemo {
    private AtomicInteger id = new AtomicInteger(0);

    public static LazyDemo instance;

    private LazyDemo() {
    }

    public static synchronized LazyDemo getInstance() {
        if (Objects.isNull(instance)) {
            instance = new LazyDemo();
        }
        return instance;
    }

    public int getId() {
        return id.incrementAndGet();
    }
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                try {
                    Thread.sleep(new Random().nextInt(10000));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                LazyDemo instance = LazyDemo.getInstance();
                System.out.println(instance + "==>" + instance.getId());
            }).start();
        }
    }
}

餓漢和懶漢的區別就是一個支持延遲加載,一個不支持。懶漢模式還要加鎖,防止併發問題。從這一點來看,懶漢模式性能是不如餓漢模式,餓漢模式在類加載時就進行實力化,也能夠提早將實力化過程當中發生的資源不足等問題提早暴露,而不是等到業務訪問後才發現沒法進行初始化,引起線上事故。
但這兩種都有各自的不足,因此如今有第三種方式。dom

3:雙重檢測jvm

public class DoubleCheckDemo {
    private AtomicInteger id = new AtomicInteger(0);
    public static DoubleCheckDemo instance;

    private DoubleCheckDemo() {
    }

    public static DoubleCheckDemo getInstance() {
        if (Objects.isNull(instance)) {
            synchronized (DoubleCheckDemo.class) {
                System.out.println("加鎖操做");
                if (Objects.isNull(instance)) {
                    instance = new DoubleCheckDemo();
                }
            }
        }
        return instance;
    }

    public int getId() {
        return id.incrementAndGet();
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                try {
                    Thread.sleep(new Random().nextInt(10000));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                DoubleCheckDemo instance = DoubleCheckDemo.getInstance();
                System.out.println(instance + "==>" + instance.getId());
            }).start();
        }
    }
}

雙重檢測解決了懶漢模式的併發性能問題,同時支持懶加載。分佈式

4:靜態內部類函數

靜態內部類的實現比雙重檢測要更加簡單,同時也能作到懶加載。工具

public class InnerClassDemo {

    private AtomicInteger id = new AtomicInteger(0);

    private InnerClassDemo() {
    }

    private static class InnerClassDemoHolder {
        private static final InnerClassDemo instance = new InnerClassDemo();
    }

    public static InnerClassDemo getInstance() {
        return InnerClassDemoHolder.instance;
    }

    public int getId() {
        return id.incrementAndGet();
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                try {
                    Thread.sleep(new Random().nextInt(10000));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                InnerClassDemo instance = InnerClassDemo.getInstance();
                System.out.println(instance + "==>" + instance.getId());
            }).start();
        }
    }
}

InnerClassDemoHolder 是靜態內部類,一開始並不會加載,等到調用getInstance()方法時纔會被加載,這是一種無鎖的實現,線程安全由jvm來保證。

5:枚舉

採用枚舉的方式來實現是最簡單的方式。

public enum EnumDemo {
    INSTANCE;
    private AtomicInteger id = new AtomicInteger(0);

    public int getId() {
        return id.incrementAndGet();
    }
}
public class EnumDemoTest {
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                try {
                    Thread.sleep(new Random().nextInt(10000));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                EnumDemo instance = EnumDemo.INSTANCE;
                System.out.println(instance + "==>" + instance.getId());
            }).start();
        }
    }
}

單例模式的問題

  • 單例對OOP特性的支持不友好,單例對繼承,多態特性支持不友好,違背了面向抽象編程的思想。
  • 單例對代碼的擴展性很差。
  • 單例對代碼的測試性很差,單例每每硬編碼進業務,後續mock不方便,須要在一開始就對單例進行包裝。
  • 單例不支持有參數的構造函數,單例的設計是無狀態的,就沒法進行參數傳遞來構建實例。

單例的範圍

若是須要在線程內實現單例,可使用ThreadLocal併發工具類。

若是須要在集羣環境下實現單例,能夠藉助外部共享存儲,例如redis。當進行實例建立的時候,從外部存儲加載對象,若是沒有則建立後存儲回去,這裏須要分佈式鎖的接入,實現起來比較麻煩。

工廠模式

簡單工廠

簡單工廠能夠看做是將對象的建立抽取出來獨立的一種實現,這種實現方式比較簡單,目的就是將建立對象的負責業務代碼從業務中剝離出來,實現複用。
缺點是邏輯過於集中,不利於後續擴展。

工廠方法

工廠方法模式是簡單工廠的進一步抽象。使用面向對象的多態性,保持了簡單工廠的的優勢。將不一樣的構建邏輯分開到不一樣的實現中,避免了修改單個邏輯影響全部的構建邏輯。

抽象工廠

抽象工廠提供一個抽象,而不是具體實現。符合面向抽象編程思想,增長工廠也不影響具體業務代碼。

建造者模式

比較典型的實現是lombok的builder方法。

public class BuilderDemo {
    private String name;
    private int age;
    private String phone;
    private String email;

    private BuilderDemo(BuilderDemoBuilder builder) {
        this.name = builder.name;
        this.age = builder.age;
        this.phone = builder.phone;
        this.email = builder.email;
    }

    public static class BuilderDemoBuilder {
        private String name;
        private int age;
        private String phone;
        private String email;

        public BuilderDemo build() {
            return new BuilderDemo(this);
        }

        public BuilderDemoBuilder setName(String name) {
            if (Objects.isNull(name)) {
                throw new IllegalArgumentException("name must not null");
            }
            this.name = name;
            return this;
        }

        public BuilderDemoBuilder setAge(int age) {
            this.age = age;
            return this;
        }

        public BuilderDemoBuilder setPhone(String phone) {
            if (Objects.isNull(name)) {
                throw new IllegalArgumentException("phone must not null");
            }
            this.phone = phone;
            return this;
        }

        public BuilderDemoBuilder setEmail(String email) {
            this.email = email;
            return this;
        }
    }

    public static void main(String[] args) {
        BuilderDemo build = new BuilderDemoBuilder().setAge(18).setEmail("email").setName("name").setPhone("13812340987").build();
        System.out.println(build.name);
    }
}

原型模式

基於已有對象原型建立對象。

實現方式分爲深拷貝和淺拷貝。深拷貝須要遞歸負責,建立一個徹底獨立的對象。淺拷貝只是拷貝引用地址,適合不會變的對象。

相關文章
相關標籤/搜索