首頁 > 軟體

java大話之建立型設計模式教學範例

2023-11-01 18:00:49

前言

本文針對一些基礎的知識進行一下總結。

建立型模式相對其它兩種模式也比較簡單,用的地方也會更多,理解起來也會相對更容易,所以可以放在一起一次性講完。

1. 原型模式

沒想到吧,我不從單例開始講,當然要從最簡單的開始講,原型模式是幹嘛的呢?通俗的講就是拷貝

我已經有一個物件了,我要改這個物件的引數,但是我又不能直接改,因為其它地方也參照到了這個物件,我直接改的話其它地方的參照就有問題,那我只能建立一個一模一樣的物件,然後在修改。那怎麼做呢,總不能建立一個物件然後根據前一個物件一個一個屬性賦值吧?這時候就需要用原型模式了

原型模式在java中的實現是繼承Cloneable介面然後實現clone()方法

public class Test implements Cloneable{
    public String name;
    public int age;
    public Object testObj;
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

呼叫時

Test testA = new Test();
try {
    Test testB = (Test) testA.clone();
} catch (CloneNotSupportedException e) {
    e.printStackTrace();
}

看得出很簡單,也很容易理解,也很常用。
除此之外,它還有一個比較重要的知識點就是深拷貝和淺拷貝

有什麼區別呢?簡單來說就是內部的物件引數也是拷貝的物件,還是指向同一個地址的同一個物件。
上面的Demo就是淺拷貝,要實現深拷貝的話就要自己實現clone方法的具體操作,比如(虛擬碼,大概能看懂就行)

public class Test implements Cloneable {
    public String name;
    public int age;
    public Object testObj;
    @Override
    protected Object clone() throws CloneNotSupportedException {
        Test testB = (Test) super.clone();
        Object newObj = new Object();
        newObj.xxx = testObj.xxx;
        testB.testObj = newObj;
        return testB;
    }
}

2. 建造者模式

又稱為builder模式,主要使用場景是一個物件有多個引數的時候,而且這些引數可傳可不傳,不傳的話就用預設值。

這種情況下,如果你用構造引數來做,你不知道要寫多少個構造引數,你用set方法來做,你就要寫很多行set操作,而且不美觀。這時候就需要用到builder模式,它是基於一個鏈式呼叫的方法,寫起來很美觀。

public class Test {
    public Test(Builder builder) {
    }
    public static class Builder {
        private Context context;
        private String name = "";
        private int age = 18;
        private int sex = 0;
        private String phone = "";
        public Builder(Context context) {
            this.context = context;
        }
        public Builder setName(String name) {
            this.name = name;
            return this;
        }
        public Builder setAge(int age) {
            this.age = age;
            return this;
        }
        public Builder setSex(int sex) {
            this.sex = sex;
            return this;
        }
        public Builder setPhone(String phone) {
            this.phone = phone;
            return this;
        }
        public Test build() {
            return new Test(this);
        }
    }
}

大概這樣,然後呼叫的時候

Test testA = new Test.Builder(this)
        .setName("name")
        .setAge(16)
        .setSex(1)
        .setPhone("phone")
        .build();

這樣的鏈式呼叫看起來就比較美觀,如果你只想傳部分引數

Test testA = new Test.Builder(this)
        .setName("name")
        .build();

看得出很簡單,也很容易理解,也很常用。
實用?嗯...... 準確來說,應該是對java來說比較實用,但如果你是用kotlin的話,用data更爽。

我個人還有個習慣是如果引數少於5個,我都是用構造方法來做,如果超過4個,我才考慮用Builder

3. 工廠模式

工廠模式簡單來說就算抽象一個工廠,你建立物件的操作就交給這個工廠來處理。使用的場景也比較廣泛,我個人使用比較多的場景是和業務邏輯掛鉤的,受業務的影響導致有些物件我沒辦法複用,不同的業務分支要建立不同的業務物件,我就會用工廠來管理建立的邏輯,這樣以後要改或者怎樣的時候去改工廠這個物件就行,還是很方便的。

工廠模式又分為幾種,簡單工廠、工廠方法和抽象工廠,我這裡只講簡單工廠。為什麼呢?因為你要知道當簡單工廠的建立邏輯超複雜導致很臃腫的時候(或在用我的話來說建立的邏輯有多個維度的時候),會用到工廠方法和抽象工廠,可以簡單理解成他們是簡單工廠的升級版本。

我這裡先拿個別人的Demo來舉個例子

public interface Shape {
    void draw(); 
}
public class Rectangle implements Shape {
    @Override
    public void draw() {
        System.out.println("Inside Rectangle::draw() method.");
    }
}
public class Circle implements Shape {
    @Override
    public void draw() {
        System.out.println("Inside Circle::draw() method.");
    }
}

定義了這些物件,然後寫個工廠來管理建立的流程

public class ShapeFactory {
    public Shape getShape(String shapeType){
        if(shapeType == null){
            return null;
        }
        if(shapeType.equalsIgnoreCase("CIRCLE")){
            return new Circle();
        } else if(shapeType.equalsIgnoreCase("RECTANGLE")){
            return new Rectangle();
        }
        return null;
    }
}

重點在於ShapeFactory這個類,有這麼一個工廠類的概念,工廠是一個抽象,工廠裡面提供相對應的方法來寫建立的邏輯。

工廠基本不會複用,因為它裡面的建立邏輯都是和業務邏輯相關的,所以在複雜的情況中,不光會只寫if-else或者switch。那這裡我可以丟擲一個問題,如果這個工廠建立物件時,有些物件是同步建立,有些物件是非同步建立,你會怎麼處理?

4. 單例模式

什麼時候會使用到單例,簡單來說就是全域性共用一個物件的時候,那麼明顯可以看出它的生命週期很長,所以很容易知道它有個缺點,很容易造成記憶體洩露 ,比如你在這個單例裡面存了很多全域性變數又不釋放,那就會記憶體洩露。

單例又分為懶漢模式和俄漢模式

// 懶漢
public class Test {
    private Test(){}
    private static Test instance = null;
    public static Test getInstance(){
        if (instance == null){
            instance = new Test();
        }
        return instance;
    }
}
// 餓漢
public class Test {
    private Test(){}
    private static final Test instance = new Test();
    public static Test getInstance(){
        return instance;
    }
}

區別是餓漢在類載入的時候物件就會建立出來,懶漢是在第一次呼叫的時候物件被建立出來。餓漢是執行緒安全的,懶漢是執行緒不安全的,所以我們一般在多執行緒的環境中使用懶漢的話要通過一些方式保證執行緒安全。

這裡有兩種方法來保證執行緒安全,雙重檢鎖和靜態內部類。

// 雙重檢鎖
public class Test {
    private static volatile Test mTest = null;
    private Test() {
    }
    public static Test getInstance() {
        if (mTest == null) {
            synchronized (Test.class) {
                if (mTest == null) {
                    mTest = new Test();
                }
            }
        }
        return mTest;
    }
}

相信都能一眼看懂什麼意思,唯一注意的是要加volatile來保證有序性,因為new Test()不是一個原子性操作。

// 靜態內部類
public class Test {
    private static class LazyHolder{
        private static final Test INSTANCE = new Test();
    }
    private Test(){}
    public static final Test getInstance(){
        return LazyHolder.INSTANCE;
    }
}

這是通過使用類載入機制來保證物件只建立一次從而保證執行緒安全。 這兩種方法都可以保證執行緒安全,兩種方法都可以使用,但我更傾向於雙檢鎖,因為能省去一步類載入,雖然這個影響不大。

5. 總結

建立型模式主要是為了解決建立物件時的場景而被設計出來的,原型模式是為了處理複製物件的情況,建造者模式是為了處理建立物件時靈活傳參的情況,工廠模式是為了處理建立物件的邏輯,單例模式是為了處理全域性共用一個物件的情況。

相信看到這裡相信都能瞭解建立型模式的概念和其中各個模式的使用。好了,正文開始,我的總結主要是想做一個反推,這些東西是怎麼被設計出來的,無非就是同個場景的程式碼敲多了,然後進行總結,設計出一套東西來方便這個場景的開發。

比如想象一下沒有單例時會怎麼去實現單例的功能。(或者說怎麼使用low一點的方法來實現單例效果),如果你讓我來做,我可能會想到通過序列化來實現,當需要多個地方改同一個物件的某個屬性時(不考慮多執行緒的情況),把這個物件序列化本地,反序列化改屬性之後再序列化回去,然後把這套東西封裝起來,這也能實現單例的效果,但是這樣一比就會發現單例的解決方案比這個方便得多,所以才需要學習設計模式。

以上就是java大話之建立型設計模式教學範例的詳細內容,更多關於java建立型設計模式 的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com