設計模式(二):面向對象及其特性分析

面向對象

根據大綱,首先我們來學習一種程式碼設計與編寫的風格,即面向對象。

面向對象編程和面向對象程式語言

  • 面向對象編程是一種編程範式,通俗來說,就是將 程式碼的組織單元改成類和對象,並將 封裝、繼承、抽象、多態 作為程式碼設計和編寫的基石。
  • 面向對象程式語言代表了能簡易實現面向對象編程範式的語言,其如果能夠用現成的語法機制來實現範式,就是面向對象程式語言。

從上述的定義中,我們可以看出,面向對象編程不一定非要用面向對象程式語言來實現,同時,當我們使用面向對象程式語言來編寫程式碼時,可能寫出來的卻反而是面向過程的程式碼。

面向對象分析和面向對象設計分別是什麼

既然面向對象編程重點是類和對象的實現,那麼針對分析和設計其實就是類和對象該如何設計。

分析講的就是做什麼,而設計講的就是怎麼做。通過這兩個階段產出類的設計,包括該拆出什麼類、每個類有哪些屬性和方法、類和類之間如何交互等。

封裝

定義:封裝的定義是隱藏類中的資訊。通過暴露有限的介面,來保護一些數據,只提供一些開放的介面來完成指定的業務。

實現方式:在 Java 程式碼的實現通過 public、private 等關鍵詞來實現。通過這些關鍵字來確定訪問許可權,從而隱藏對應欄位。

意義:

  • 安全、可控,因為我們指定了哪些屬性是可暴露給外部,哪些是只能按照指定方式來修改的,因此更加安全可控;
  • 好理解,因為在程式碼的實現里封裝了多數細節,只暴露一些開放的介面,因此不需要調用者太了解全部就可以使用了。

抽象

定義:抽象的定義和封裝類似,封裝封的是類內的屬性欄位,抽象抽的是行為的具體實現方式。

實現方式:在 Java 程式碼里通過 interface 或 abstract class 來實現。但該特性並不是一個面向對象特有特性,只要語言里有函數的概念,就可以通過多種方式來實現抽象特性。

意義:

  • 因為只暴露方法的定義,因此對使用方來說,不需要關注具體的實現,只面向定義使用即可,內部如何實現以及後續如何更改,只要保證定義不變,使用方就不需要更改;

為了實現上述意義,因此在設計方法定義時,盡量保證方法的定義和具體實現無關,來保證面對需求的修改時可以從容修改。

繼承

定義:繼承好理解了,其實現的是 is-a 的語義,舉個例子來說,設計父類是哺乳動物的話,那麼子類就可以是貓、狗、人等,這些子類都屬於父類的一種。

實現方式:在 Java 程式碼里繼承是通過 extend 關鍵字來實現的。

意義:繼承最重要的就是解決程式碼復用的問題,即子類復用父類的屬性和方法,避免重複的程式碼編寫多次。

帶來的問題:

  • 多繼承問題,在 Java 里是沒有多繼承的,因為多繼承會出現菱形問題,導致語義不清,因此官方不允許使用多繼承,即一個子類只可以繼承一個父類。
  • 繼承層次過深,如果繼承的層級結構比較複雜,那麼在想要知道一個類中的某個具體行為實現,可能就需要看很多層級,從而影響程式碼的可讀性和可維護性。

image.png

在 A 中定義了一個方法 test(),類 B 和 類 C 都繼承了A,並對 test() 進行了重寫,此時有個類 D 同時繼承類 B 和類 C,那麼類 D 在調用 test() 方法的時候,該調用哪一個呢?這就是菱形問題帶來的語義混亂。

組合和繼承:
針對上述所述的問題,因此推薦使用組合的形式來代替繼承,具體的實現方式的話,在後面的文章中再詳細描述。

多態

定義:簡單來說,就是父類指針可以指向子類對象,從而在程式碼實際運行過程中,根據具體是哪一個對象,來決定調用哪個對象的方法。

實現方式:文字不好解釋,直接程式碼來解釋。

// 首先定義一個介面
public interface ImageStore {
    void sayHello();
}

// 定義一個類來實現介面,並重寫其中的方法
public class AliyunImageStore implements ImageStore {
    @Override
    public void sayHello() {
        System.out.println("AliyunImageStore say Hello");
    }
}

// 再定義一個類來實現介面,並重寫其中的方法
public class TencentImageStore implements ImageStore {
    @Override
    public void sayHello() {
        System.out.println("TencentImageStore say Hello");
    }
}

// 用介面來指向具體子類,從而調用子類的方法
public class Demo {
    public static void main(String[] args) {
        ImageStore aliyun = new AliyunImageStore();
        aliyun.sayHello();

        ImageStore tencent = new TencentImageStore();
        tencent.sayHello();
    }
}

image.png

意義:
提高程式碼的可拓展性,通過父類指針來操作數據,保證在程式碼中實現一個邏輯,可以復用多次。

公眾號截圖

文章在公眾號「iceWang」第一手更新,有興趣的朋友可以關注公眾號,第一時間看到筆者分享的各項知識點,謝謝!筆芯!