物件導向不好用

更新於 發佈於 閱讀時間約 5 分鐘

讓我在這篇文章總結一下前面對物件導向設計的討論,我們討論了物件導向的四個特性:繼承、抽象、多型、封裝,分析了它們的問題,並跟函數式編程的思維做比較。我們引入了與之相對應的特性:泛型、特性系統、模組化,有些特性雖然跟那四個特性很像,但在一些細微的地方有不同的詮釋,使得整體思考方式很不一樣。


「繼承」是物件導向最重要的特性,它提供一種方法建立子類關係,並將父類別的成員、方法與介面的實作繼承下來。這種繼承機制讓物件成員與方法得以增加,並讓方法變得可以被改寫。並不是所有方法都適合被繼承,例如物件的複製方法應該要以實體類型為回傳類型,但這個方法會因為繼承變得不符合規則,實際上它的方法實作也不適合透過繼承被重用。允許子類的類型讓物件的實體類型可以和變數類型不一樣,因而造成類型資訊的丟失,因此物件導向很依賴動態類型檢查。這種子類關係是物件導向的抽象化的根基。物件導向的「抽象」是向外的抽象,它把相同的功能抽取出來成為父類,並把其他不同的部分當成它的延伸。它透過子類轉換將不重要的部分抹除,因而形成抽象的概念。這種基於抹除的抽象化讓類型資訊遺失,使得延伸的部分在一些情況變得難以掌握,尤其是定義容器類的資料結構時。相反的,函數式編程使用向內的抽象,它將不同的部分抽換成類型參數,因此可以保證未知類型的同調性,還可以透過類型約束限制延伸的方向。泛型可以取代大部分透過繼承能做到的事情,但在解決問題上會很不一樣,因為物件導向鼓勵開發者使用繼承為問題建模,而只使用泛型就必須以組合建構模型。物件導向的類別只專注於已知的部分,這很容易會讓我們忽略掉對子類做了哪些假設,使得未來做延伸時常常打破約束。而泛型的類型變數描述的是未知的部分,因此可以明確的知道你不知道什麼,透過了解知與未知的邊界更能有效控制抽象化的模型。


物件導向的「多型」指的是子類多型,它透過繼承類別/實作介面讓相同的方法根據物件的實體類型擁有不同的行為,從而實現類別的延展性。延展性讓函式或物件有辦法與未知的程式碼合作,只要未知的部分透過介面表明它符合某些特性。介面用來描述一個類別是否符合某些特性,它是物件導向多型的基礎,在這裡類別也可以看作一種帶有預設實作的介面。因為介面的方法是屬於物件的,一個變數的介面實作是由它的實體類型決定而非變數類型,因此我們很難保證兩個相同類型的變數是否具有相同的特性,例如比較兩個變數時使用哪個物件的比較方法會影響結果。特性本來就不應該被繼承,子類轉換會使特性的意義改變。rust沒有類似繼承的子類關係,物件的實體類型基本上就是變數的類型,因此一個變數的特性可以靜態地決定,使我們更容易控制未知的程式碼。trait的語法雖然看起來像是介面,但它實際上更像Haskell的typeclass,特性是獨立於資料類型的存在,這使得我們可以在某種程度上附加任意特性給已有的類型。


模組可藉由存取權控制依賴關係的範圍,這讓我們得以將危險的函式或複雜的實作細節隱藏起來,而不會受到外部引用。這種隱藏細節的概念稱作封裝。合適的依賴關係範圍非常重要,它決定了我們對這個函式、類型的看法,如果它只能被很小範圍的函式引用,那我們就不用太擔心它的情境。物件導向的「封裝」把這個概念放到類別上,讓我們可以把類別的實作細節封裝在類別的定義範圍內,還增加一種只讓子類存取的控制權限。這種存取權限讓類別具有感染性,使得很多方法只能放在類別內定義,因而破壞單一職責原則。


物件導向的類別結合了定義資料結構、定義抽象行為、封裝複雜情境與實作細節三種功能。物件導向的繼承做了三件事:增加資料的成員變數、繼承抽象行為的實作、延伸抽象行為。除了定義資料結構,抽象行為應交由介面定義描述,而封裝應是模組的職責。比起擴充資料結構成員變數,組合才是組成複雜結構的最好方法,要延伸抽象行為應使用帶有約束的介面建立依賴關係。類別的每個特性在大部分情況都有其他機制可以取代,甚至可以做的更好。物件導向看起來好用只是因為它把各種功能加到一個語法上,讓我們照著某種範式設計程式時比較方便。如果把這些看似新潮的功能從類別上拔掉,只留下最基礎的機制,大部分物件導向的問題都將不復存在,而golang就是這麼做的成果。從這個結果來看,物件導向的機制真的好用嗎,還是只是你沒有理解為何要這麼設計。


trait/typeclass等特性系統依賴於靜態的類型推論,因此只有存在語言上的支援才有辦法實現。而物件導向的介面是基於子類關係的特性,只要物件本身能帶有方法就能實現,因此它很適合應用在動態類型系統上。有人說「物件導向是一種態度」,只要我們有心就可以在任何程式語言使用物件導向編程,但事實上每個程式語言都有最合適的思考方式。我認為認識物件導向的機制與函數式編程的機制之間的不同非常重要,因為語言特性能形塑我們的思考方式。我相信程式語言有一個最好的典範模式,物件導向這種直覺的思考方式在某種程度上抓到了它的部分精髓,但在很多地方搞砸了。物件導向的程式語言把物件導向的概念作為基礎機制,讓我們必須以物件導向的方式思考,但也使得我們難以意識到它在哪裡搞砸了。這種以直覺想法作為根基的機制本來就不穩固,它的機制大多依賴於某種特定的使用方式,而非提供一種能力讓我們可以做到什麼事,例如繼承機制本質上是不需要的,我們可以用原型鏈達到一樣的效果。程式語言應該根據能做到什麼事而非想要怎麼做而加入機制,如此才能抓到程式語言的典範模式,而函數式編程就是它的實現。我把它稱呼為「函數式編程」而非「函數式導向」就是因為它不是因為想怎麼做而發明出來的(至少我是這麼認為的),而是透過最大化語言特性的用處所得到的編程範式。因此語言特性的邊界特別重要,這種邊界才是形塑核心概念的關鍵,前面的文章一直在討論這些細微的差異就是這個原因。


留言
avatar-img
留言分享你的想法!
avatar-img
have bear的沙龍
4會員
28內容數
這不是教你如何從物件導向到函數式編程的入門教程。我會深入探討物件導向與函數式編程的差異,並討論為什麼你應該使用函數式編程並徹底放棄物件導向。
have bear的沙龍的其他內容
2024/02/15
這個系列的文章主要專注於物件導向到函數式編程的差異與分析,並針對概念與機制上的不同進行比較。很多人說物件導向和函數式編程沒有哪個比較好的問題,只有哪個比較適合的問題,然而我並不這麼認為,我透過這一系列的文章從各個角度討論它們之間的優缺點就是為了闡述我的觀點。物件導向錯在沒有理論基礎,但它贏在熟悉性,
2024/02/15
這個系列的文章主要專注於物件導向到函數式編程的差異與分析,並針對概念與機制上的不同進行比較。很多人說物件導向和函數式編程沒有哪個比較好的問題,只有哪個比較適合的問題,然而我並不這麼認為,我透過這一系列的文章從各個角度討論它們之間的優缺點就是為了闡述我的觀點。物件導向錯在沒有理論基礎,但它贏在熟悉性,
2024/02/08
前一篇文章中所提的函數式的三個機制明確說明了它關注的規則與能力具體是什麼。然而這套對於函數式編程的定義主要基於特定的類型系統,作為一個編程範式來說過於狹隘(物件導向的定義也是這樣)。更廣義的,我認為函數式編程主要依循三個原則,它們可以應用於任何程式語言,就算沒有靜態類型系統的支援也可以。例如在不管類
2024/02/08
前一篇文章中所提的函數式的三個機制明確說明了它關注的規則與能力具體是什麼。然而這套對於函數式編程的定義主要基於特定的類型系統,作為一個編程範式來說過於狹隘(物件導向的定義也是這樣)。更廣義的,我認為函數式編程主要依循三個原則,它們可以應用於任何程式語言,就算沒有靜態類型系統的支援也可以。例如在不管類
2024/02/04
前面談了那麽多函數式編程與物件導向的差異,但我們還沒定義函數式編程。就像物件導向,函數式編程沒有明確的定義,每個人對於什麼是函數式編程都有不同的看法。在這裡我會總結前面的討論,給出我對於函數式編程的觀點。 物件導向注重封裝與延展性,因此一般基於三個機制:繼承、多型、封裝,它們代表了物件導向所重
2024/02/04
前面談了那麽多函數式編程與物件導向的差異,但我們還沒定義函數式編程。就像物件導向,函數式編程沒有明確的定義,每個人對於什麼是函數式編程都有不同的看法。在這裡我會總結前面的討論,給出我對於函數式編程的觀點。 物件導向注重封裝與延展性,因此一般基於三個機制:繼承、多型、封裝,它們代表了物件導向所重
看更多
你可能也想看
Thumbnail
常常被朋友問「哪裡買的?」嗎?透過蝦皮分潤計畫,把日常購物的分享多加一個步驟,就能轉換成現金回饋。門檻低、申請簡單,特別適合學生與上班族,讓零碎時間也能創造小確幸。
Thumbnail
常常被朋友問「哪裡買的?」嗎?透過蝦皮分潤計畫,把日常購物的分享多加一個步驟,就能轉換成現金回饋。門檻低、申請簡單,特別適合學生與上班族,讓零碎時間也能創造小確幸。
Thumbnail
嗨!歡迎來到 vocus vocus 方格子是台灣最大的內容創作與知識變現平台,並且計畫持續拓展東南亞等等國際市場。我們致力於打造讓創作者能夠自由發表、累積影響力並獲得實質收益的創作生態圈!「創作至上」是我們的核心價值,我們致力於透過平台功能與服務,賦予創作者更多的可能。 vocus 平台匯聚了
Thumbnail
嗨!歡迎來到 vocus vocus 方格子是台灣最大的內容創作與知識變現平台,並且計畫持續拓展東南亞等等國際市場。我們致力於打造讓創作者能夠自由發表、累積影響力並獲得實質收益的創作生態圈!「創作至上」是我們的核心價值,我們致力於透過平台功能與服務,賦予創作者更多的可能。 vocus 平台匯聚了
Thumbnail
這個單元我一直很想學習,物件導向 Object Oriented Programming 以前一直以為是一種程式碼,其實是設計程式的觀念,文中我分享了程式碼還有自己想的比喻讓讀者更好理解。除了物件導向,我還介紹其他四種風格的程式碼設計,跟大家一起學習。
Thumbnail
這個單元我一直很想學習,物件導向 Object Oriented Programming 以前一直以為是一種程式碼,其實是設計程式的觀念,文中我分享了程式碼還有自己想的比喻讓讀者更好理解。除了物件導向,我還介紹其他四種風格的程式碼設計,跟大家一起學習。
Thumbnail
這一章節旨在介紹 PHP 中的物件導向編程(OOP)概念。通過詳細講解類別、建構子、訪問修飾符(公開、私有、受保護)、繼承、多型、封裝、介面、抽象類別、靜態類別、列舉、委派、Lambda 表達式、泛型和反射等概念,使讀者能夠理解和應用這些 OOP 技術來編寫更具結構性和可維護性的 PHP 代碼。
Thumbnail
這一章節旨在介紹 PHP 中的物件導向編程(OOP)概念。通過詳細講解類別、建構子、訪問修飾符(公開、私有、受保護)、繼承、多型、封裝、介面、抽象類別、靜態類別、列舉、委派、Lambda 表達式、泛型和反射等概念,使讀者能夠理解和應用這些 OOP 技術來編寫更具結構性和可維護性的 PHP 代碼。
Thumbnail
本章節是Java入門的第八天,主要介紹物件導向的概念。這包括了類別、建構子、存取修飾子、繼承、多型、封裝、介面、抽象類別、靜態類別、列舉、Lambda表達式、泛型和反射等主題。每個主題都配有相關的程式碼範例,以協助讀者更好地理解這些概念。
Thumbnail
本章節是Java入門的第八天,主要介紹物件導向的概念。這包括了類別、建構子、存取修飾子、繼承、多型、封裝、介面、抽象類別、靜態類別、列舉、Lambda表達式、泛型和反射等主題。每個主題都配有相關的程式碼範例,以協助讀者更好地理解這些概念。
Thumbnail
本章節是一個初級的 TypeScript 教學,主要介紹了 TypeScript 中物件導向程式設計的各種核心概念,包括類別、建構子、存取修飾子、繼承、多型、封裝、介面、抽象類別、靜態類別、列舉、委派、Lambda 表達式、泛型和反射等。每個概念都通過詳細的解釋和實例代碼來進行深入的介紹。
Thumbnail
本章節是一個初級的 TypeScript 教學,主要介紹了 TypeScript 中物件導向程式設計的各種核心概念,包括類別、建構子、存取修飾子、繼承、多型、封裝、介面、抽象類別、靜態類別、列舉、委派、Lambda 表達式、泛型和反射等。每個概念都通過詳細的解釋和實例代碼來進行深入的介紹。
Thumbnail
※ OPP第一大核心-封裝 封裝的精神在於將「方法」、「屬性」和「邏輯」包裝在類別裡面,透過類別的實例來實現。這樣外部物件不需要了解內部的實現細節,只需要知道如何使用該類別提供的接口即可。換句話說,封裝是將內部細節隱藏起來,只暴露必要的部分給使用者。 封裝的核心概念是,使用者如果想要接觸資料,只
Thumbnail
※ OPP第一大核心-封裝 封裝的精神在於將「方法」、「屬性」和「邏輯」包裝在類別裡面,透過類別的實例來實現。這樣外部物件不需要了解內部的實現細節,只需要知道如何使用該類別提供的接口即可。換句話說,封裝是將內部細節隱藏起來,只暴露必要的部分給使用者。 封裝的核心概念是,使用者如果想要接觸資料,只
Thumbnail
本章節的目的是讓讀者瞭解C#的物件導向特性,包括類別、繼承、多型、封裝等基本概念,以及介面、抽象類別、靜態類別等進階主題。此外,本章節也將介紹如何使用列舉、委派、Lambda表達式、泛型及反射,這些都是C#中常見的強大功能。
Thumbnail
本章節的目的是讓讀者瞭解C#的物件導向特性,包括類別、繼承、多型、封裝等基本概念,以及介面、抽象類別、靜態類別等進階主題。此外,本章節也將介紹如何使用列舉、委派、Lambda表達式、泛型及反射,這些都是C#中常見的強大功能。
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News