天天看點

深度思考:到底什麼是抽象?

到底什麼是抽象?

前言:

不要為了抽象而抽象。 ——《Tony Bai · Go 語言第一課》

1 回顧軟體設計的六大原則

一、單一職責原則(SRP: Single responsibility principle)

二、開放封閉原則(OCP: Open Closed Principle)

三、裡氏替換原則 ( LSP: Liskov Substitution Principle)

四、接口隔離原則( ISP: Interface Segregation Principle)

五、依賴倒置原則( DIP: Dependence Inversion Principle)

六、迪米特原則(Law of Demeter)

解釋:

  • 單一職責原則:

    一個類/接口/方法隻負責一項職責,并且有且隻有一個需要被改變的理由。

  • 開放封閉原則:

    一個軟體實體如類、子產品和函數應該對擴充開放,對修改關閉。

  • 裡式替換原則:

    子類可以替換父類,即子類可以擴充父類的功能,但是不能改變父類原有的功能。

  • 接口隔離原則:

    用多個專門的接口,而不使用單一的總接口,用戶端不應該依賴它不需要的接口,一個類對一個類的依賴應該建立在最小的接口上。

  • 依賴倒置原則:

    高層子產品不應該依賴底層子產品,二者都應該依賴其抽象。

  • 迪米特原則:

    一個對象應該對其他對象保持最少的了解,又叫最少知道原則。

2 抽象引入

在依賴倒置原則中有一個非常常見但又讓你了解起來很模糊的名詞——抽象。

我們看下維基百科對抽象的解釋:

抽象就是把一個問題或模型,以不同規則或方法所得出的不同的解(求解方法和解本身即抽象層),這些不同的解可以組合并還原成問題或模型的本身。

抽象的意義是可以忽略不是求解過程中必需的解。例如要用計算機程式)去模拟“人”,在描述了人的動作(飲食、思考、移動等)符合設計要求後(如可完整表達“人”在坐下時候的動作),其他“人”的細節都可以忽略,以集中設計需要的功能,并減低程式的複雜度。

在程式設計中,進階語言是對機器指令序列的抽象。進階語言的出現,使得程式的編寫得以簡化,極大提高了程式的編寫效率。随着軟體技術的發展,元件技術進一步提升了程式抽象的級别。

另一種可取的替代方法是設計一種語言機制,允許程式師在需要的時候建構自己的抽象方法。一個通用的機制是使用過程(procedure)。通過分離過程的定義和規則,程式設計語言包含了兩種重要的抽象方法:參數化抽象(abstraction by parameterization)和規範化抽象(abstraction by specification)。

程式設計中,抽象類别包括下列4類:

1.過程抽象:能夠引入一些新的操作;

2.資料抽象:能夠引入新的資料對象類型;

3.反複運算抽象:能夠反複運算周遊在集合中的元素,而不必顯示如何獲得元素的細節;

4.類型層次:能夠從多個單獨的資料類型中抽象成幾組相關的類型。

3 軟體設計中的抽象舉例

3.1 業務場景

在平時生活中我們每個人在加入一個新的群體前都會進行自我介紹,以此來讓人們認識自己,比如一名老師會這樣說:“大家好,我是一名老師,我叫XXX”,一名運動員會說:“大家好,我叫XXX,我的職業是一名運動員”等等,但是生活中總是不乏有有趣的人出現,比如突然來了一位程式員自我介紹:“Hello World,我叫XXX,來自中國,是一名Programmer”。

由上述我們可以得知,每個人的自我介紹一般都會先問好,然後介紹自己的職業,自己的姓名等等,于是我們可以将每個人的自我介紹進行提取和抽象,在大多數情況下每個人都可以依賴這個抽象,并且在自己有别出心裁的自我介紹時還可以将抽象具體化成自己的自我介紹。

是以,到這裡我們就可以總結出可以抽象的部分問好、職業,(因為每個人都會先問好,并且很多人也都可能從事同一個職業,但是名字的抽象度并不高):

  • 問好
  • 職業

3.2 代碼實作

提供抽象:
// Say 抽象接口
type Say interface {
    // SayHello 抽象方法
    SayHello() string
}

// PeopleSayHello 利用反射調用接口
func PeopleSayHello(dest interface{}) (string, error) {
    var sayHello string
    if dest == nil {
        return "dest is nil", nil
    }
    value := reflect.ValueOf(dest)
    if value.Kind() == reflect.Ptr && value.IsNil() {
        value = reflect.New(value.Type().Elem())
    }
    modelType := reflect.Indirect(value).Type()
    if modelType.Kind() == reflect.Interface {
        modelType = reflect.Indirect(reflect.ValueOf(dest)).Elem().Type()
    }
    modelValue := reflect.New(modelType)
    sayHello = DefaultSayHello(modelType.String())
    if say, ok := modelValue.Interface().(Say); ok {
        sayHello = say.SayHello()
    }
    return sayHello, nil
}

// DefaultSayHello 抽象的預設實作
func DefaultSayHello(str string) string {
    split := strings.Split(str, ".")
    return "Hello,I am a " + split[len(split)-1]
}      
使用抽象:
// Teacher 教師
type Teacher struct {
    Name string
    Age  int
}

// Student 學生
type Student struct {
    Name string
    No   string
    Age  int
}

// Sportsman 運動員
type Sportsman struct {
    Name string
    Age  int
    Like string
}


func (stu Student) SayHello() string {
    return "Hello,I am a Student and my name is Zs."
}

func (tea Teacher) SayHello() string {
    return "Hello,I am a Teacher and my name is Ls."
}      
運作:
func main() {
    teacherHello, err := PeopleSayHello(Teacher{})
    studentHello, err := PeopleSayHello(Student{})
    sportsmanHello, err := PeopleSayHello(Sportsman{})
    if err != nil {
        fmt.Println(err)
    }
    fmt.Println(teacherHello)
    fmt.Println(studentHello)
    fmt.Println(sportsmanHello)
}      
深度思考:到底什麼是抽象?
解釋:

在整個代碼中,我們使用接口定義了抽象,使用接口的方法定義了抽象方法,并且通過反射能夠讓抽象更加的通用,進而形成抽象層,将人們習慣的方式進行統一,但是通過提供抽象的具體化讓每個角色能使用自己的具體方法。

4 探究反射和抽象

"反射是架構設計的靈魂"

我們都知道架構的設計高度依賴于反射這個特性,可以說反射是架構的靈魂,究其原因,反射是因為有以下這個概念:

反射用于觀察并修改程式在運作時的行為。一個反射導向的程式元件可以監測一個範圍内的代碼執行情況,可以根據擷取的目标對象資訊及與此相關的範圍修改自身。這可通過在運作時動态配置設定程式代碼實作。

反射主要用途就是使給定的程式,動态地适應不同的運作情況。利用面向對象模組化中的多态(多态性)也可以簡化編寫分别适用于多種不同情形的功能代碼,但是反射可以解決多态(多态性)并不适用的更普遍情形,進而更大程度地避免寫死(即把代碼的細節“寫死”,缺乏靈活性)的代碼風格。

反射也是元程式設計的一個關鍵政策。

反射本身很抽象,反射讓架構更加抽象。

反射本身是抽象的,我認為反射的抽象在于他能在有條件的情況下取出運作時的對象狀态,進而作出判定或修改。

反射的最大優勢在于動态地适應不同的運作情況,進而解決多态,比如在上面的代碼中,無論執行個體化的結構體類型是Teacher、Student還是Sportsman,反射構成的方法都可以當做interface{}類型進行處理,進而能讓方法更加的抽象化,這種方式在架構設計和編碼中也非常的常見,如Java的JDBC抽象、Spring IOC抽象等等。

5 更進階的抽象

對于計算機領域中,我認為更進階的抽象是數學。

德國數學家Hermann Weyl曾說:“數學抽象中最關鍵的一步讓我們忘記這些符号所表示的對象,有許多操作可以應用于這些符号,而根本不必考慮他們到底代表着什麼。”數學離不開抽象,架構的模組化過程同樣離不開抽象。

對于數學和軟體架構的關系,因為本人目前的閱曆也是較為淺薄,是以在此先一筆帶過,待日後經驗更加豐富,思想更加有深度時再回來補充。

6 總結

文章的開頭前言就寫出了一句話:“不要為了抽象而抽象”

确實是這樣的,盲目的抽象隻會讓系統過度設計,而我們要做的就是懂得适度權衡。

為什麼要抽象?這是一個值得思考的問題,而我認為抽象最大的好處就是:

  • 能夠最大限度的減少重複
  • 基于抽象可以靈活的擴充

是以隻有當我們的設計能夠達到可抽象的要求是才适合使用抽象,而本來的簡單和精巧的設計不一定非要進行抽象。

以上就是個人對抽象的全部了解,歡迎讀者留言~

參考文章:

https://zh.wikipedia.org/wiki/%E6%8A%BD%E8%B1%A1%E5%8C%96_(%E8%A8%88%E7%AE%97%E6%A9%9F%E7%A7%91%E5%AD%B8)

上一篇: 3s

繼續閱讀