1. 程式人生 > >再談面向物件中的封裝、繼承和多型

再談面向物件中的封裝、繼承和多型

  1. 封裝
    封裝說的是把資料封裝起來,對外暴露一個可以訪問的介面,不能讓外界直接訪問內部的資料。
    從上面的描述可以抽取出兩種型別:介面和類。
    從現在比較火的微服務的觀點上來看,一個類就是一個服務,一個物件就是一個服務的例項,通過這個服務暴露的介面來訪問這個服務。從這個意義上來講,面向物件其實就是一個工程化的架構設計。

  2. 繼承
    繼承是實現程式碼複用性的基礎特性。
    繼承關係其實是一種鏈式關係,使得我們可以在現有的服務的基礎上增加新的功能,而不用修改原有的服務。這其實也是符合了開閉原則:

開閉原則是java世界裡最基礎的設計原則,它指導我們如何建立一個穩定,靈活的系統。開閉原則定義如下:
Software entities like classes,modules and functions should be open for extension but closed for modifications.
一個軟體實體如類,模組和函式應該對擴充套件開放,對修改關閉。

詳細的資訊請參考博文:
六大設計原則之開閉原則

  1. 多型
    多型這個特性其實可以用盲人摸象這個寓言故事來形象地描述:

從前,有五個盲人,從來沒有見過大象,不知道大象長的什麼樣,他們就決定去摸摸大象。第一個人摸到了鼻子,他說:“大象像一條彎彎的管子。”第二個人摸到了尾巴,他說:“大象像個細細的棍子。”第三個人摸到了身體,他說:“大象像一堵牆。”第四個人摸到了腿,他說:“大象像一根粗粗的柱子。”

首先大象都是同一個大象,沒有任何變化,但是五個人理解的大象都是不一樣的,有的人認為它是管子,有的人認為是棍子。可以說,同一個大象在不同的人眼中表現出了不同的狀態。
在面向物件的世界中是怎麼體現的呢?同一個物件,我們用不同的引用去指向它,這個物件表現出的狀態就是這個引用所描述的狀態。我們其實並不知道物件的真實的狀態,我們知道的只是物件通過這個引用訪問到的狀態。可以說,多型是面向物件封裝特性的執行時(Runtime)體現。

  1. 總結
    面向物件這種設計思想其實是一種工程化的程式設計思想,它的思想的核心是對外暴露方法,而不是對外暴露資料,對這個資料的修改的操作都會放到特定的方法中。這樣,我們呼叫方法就相當於向物件傳送命令,就像現實生活中我讓某人幹某件事一樣,可以說是一種命令(Command)式的程式設計風格。
    但是,這在併發模式下會有一個問題,就是併發的修改資料會造成資料的混亂。因為我(當前執行緒)不知道資料是由我自己修改的,還是由別人修改的。如果一個執行緒拿到資料以後,別的執行緒也拿到了這個資料,這樣兩個執行緒對資料的修改就會不一致,造成邏輯混亂。怎麼解決這個問題呢?很簡單,只需要在一個執行緒拿到資料以後,別的執行緒不能再拿到這個資料,等拿到資料的執行緒把資料放回去,別的執行緒才能再拿到這個資料,這樣就不會造成併發讀寫資料的問題了。但是這樣做會把併發問題轉換為序列來解決,喪失了併發處理的高效性。
    我們可以把資料訪問的過程再細分一下,分兩種情況:1. 執行緒需要對拿到的資料做修改 2. 執行緒只需要取資料,不需要對資料進行更改。我們把只允許一個執行緒訪問資料的情況稱為對資料的鎖定,簡稱鎖。我們可以看到,只有當一個執行緒需要對資料進行修改的情況下,才會對資料進行鎖定,防止別的執行緒修改或讀取到髒資料(修改之前的資料)。這樣,我們把鎖細分為:讀鎖和寫鎖。如果資料沒有鎖或只有讀鎖,那麼可以再給資料加讀鎖,不能加寫鎖。如果想加寫鎖,必須等待資料上面的所有鎖都釋放掉才可以加,而且加上寫鎖以後,必須等寫鎖釋放掉才能加其它型別的鎖,這就是寫鎖的排他性。
    我們可以看到併發資料訪問的問題是由於資料是可修改的這種情況導致的。如果資料不可以改變,每個執行緒都在這一份原始資料上進行資料變換,那就不會有併發資料訪問的問題了,這個思路其實就是函數語言程式設計的思想。
    資料變換

函數語言程式設計的特點:無狀態、無副作用、無關時序、冪等
什麼是冪等性:在程式設計中一個冪等操作的特點是其任意多次執行所產生的影響均與一次執行的影響相同。

有關函數語言程式設計的詳細介紹,請參見以下博文:
你有必要知道的函數語言程式設計