面向對象,是編程的基礎之一.
以下是很乾很乾的乾貨
begin...........
目前主流的編程範式 有三種:
1) 面向過程
2) 面向對象
3) 函數式編程
面向對象這種編程風格是其中最爲主流的
因爲其適合處理複雜的現實邏輯
目前市面上大部分的編程語言都是支持面向對象特性的。(封裝,抽象,繼承,多態)
是很多設計模式的 編碼實現基礎
封裝:
被稱之爲信息隱藏,or 數據訪問保護。類通過暴露有限的訪問接口,讓外部通過類暴露的方式來訪問與操作內部數據與信息。它需要編程語言提供 權限訪問控制語法來支持
在java中 eg: private 、default、protected 、 public
其用意在於
- 保護數據不被隨意修改,導致業務出現問題,
- 僅僅暴露有限且有必要的接口,提高類的易用性,他人不關心類內部實現
抽象
抽象皆旨在隱藏函數or 功能的具體實現, 讓使用者值需要關係當前類有哪些功能, 當前函數有什麼功能。不用去關注他的實現,抽象可以通過 接口 or 抽象類來實現,
抽象的意義在於
- 修改實現無需改變功能定義
- 是處理複雜系統的有效手段,可以過濾掉不必要關係的實現。(畢竟,你不想發個http 請求還要關係7層網絡通訊協議之間如何通訊,併爲之編寫通訊的代碼)
繼承
繼承用來表示類之間的 is-a 關係(就是你),主要用於解決代碼複用的問題分爲兩種模式: 單繼承 與 多繼承,
-
單繼承
表示一個子類只繼承一個父類,但是一個父類被多個子類繼承
-
多繼承
表示一個子類可以繼承多個父類
爲了實現這個特性,編程語言需要提供特殊的語法支持。
多態
指子類可以替換父類,在實際的代碼運行過程中,調用子類的函數實現。多態這種特性也需要編程語言提供特使的語法機制來實現。
eg: 繼承、接口、duck-typing。
多態可以提高代碼的擴展性與複用性,是很多設計模式,設計原則,編程技巧的代碼實現基礎。
面向對象 VS 面向過程
面向對象相較於面向過程的優勢主要有三個
- 對於大規模負載程序的開發,程序的處理流程並非單一的一條主線,而是錯綜負載的網狀結構。面向對象編程比起面向過程編程更能夠因對複雜類型的程序開發
- 面向對象編程擁有更加豐富的特性(封裝,繼承,抽象,多態)。利用這些特性可以使得代碼更加易擴展,易維護,易複用。
- 面向對象更加符合人類的思維直觀,更加人性化,高級。(雖然我不這麼認爲,也沒有感受到這一點)
但是無論是面向對象風格編程還是面向過程風格編程,需要注意的是沒有絕對的銀彈,只有更加的適合。只能說如今的軟件開發業務流程都已經複雜到了面向過程風格無法滿足的地步, 所以纔會有面向對象
但是請注意
面向對象編程語言寫出來的代碼不一定是 面向對象編程風格的
同理
面向過程編程語言,也可以寫出面向對象編程風格的代碼
面向對象分析,設計,編程
面向對象分析(OOA)、面向對象設計(OOD)、面向對象編程(OOP)。是面向對象開發的三個基本環節。
面向對象分析(OOA):得出要做什麼?
面向對象設計(OOD):得出怎麼做?
面向對象編程(OOP):按照設計翻譯成代碼的過程。
與程序員的思考框架中的:
where are we now?(我們在哪裏?)
where are we going?(我們要去哪?)
how can we get there?(我們怎麼做?)
面向對象中差了一個 where are we now (我們目前在哪)?
而思考框架中 少了個 OOP
言歸正傳
面向對象分析, 就是 我在需求分析環節中 使用面向對象分析方式, 得出一個粗糙、粗粒度的基礎方案,然後慢慢優化,一步一步迭代,從第一版的粗粒度中選出一塊進行拆分,其中可以通過visio等畫圖工具進行輔助,免得分析後面忘了前面。(但是相較於面向對象分析方式,我更加熱衷與用戶故事的分析方式,把自己變成一個用戶,將當前功能的使用方式,操作步驟一步一步的record下來。最後得出所有步驟之後在進行步驟拆分,然後思考每一步該如何實現。)
將面向對象分析轉化爲面向對象設計的時候可能會有些抽象,那麼給出四個思考點,以思考點入手:
-
劃分職責
根據需求分析,把涉及的功能點一個一個羅列出來,看那些功能點相近,操作同樣的屬性,可否歸爲同一個類
-
定義類以及其屬性和方法
我們識別出需求描述中的動詞,作爲候選函數的名稱,在進一步過濾篩選出真正的函數,把功能點中的名詞作爲候選屬性,同樣過濾。
-
定義類與類之間的交互關係
UML 統一建模語言中定義了流中類之間的關係。分別是:泛化、實現、關聯、聚合、組合、依賴。從現實編程的角度保留其中的四個關係: 泛化、實現、組合、依賴。
-
將類組裝起來並提供執行入口
我們需要將所有的類、功能組裝在一起,提供一個執行入口,可以是一個api接口也就是一個函數,也可以使main()函數
面向對象設計就是 把合適的代碼放到合適的類中,至於如何劃分可以根據劃分標準“高內聚,低耦合”、單一職責、對擴展開放對修改關閉等設計原則,與思想。 目的是儘量保證 代碼的可複用、易讀、易擴展、易維護。
面向對象編程 就是 使用面向對象編程語言提供的語法特性 把設計翻譯成代碼,且儘量要保證我們的目的 可複用、易讀、易擴展、易維護。
接口vs抽象類
抽象類不允許被實例化,只能被繼承。它可以包含屬性和方法,方法既可以包含代碼實現,也可以是無實現,也可以是抽象方法。子類繼承抽象類必須實現抽象類中所有的抽象方法。
接口不能包含屬性(Java可以定義靜態常量),只能聲明方法,方法中不能包含代碼實現(Java8之後可以有默認實現)。類在實現接口的時候必須實現接口中聲明的所有方法。
抽象類是對成員變量以及方法的抽象,是一種is -a 關係,是爲了解決代碼複用的問題
接口僅僅是對方法(功能)的抽象,是一種 has-a的關係,表示具有某一組行爲特性,是爲了解決解耦問題
什麼時候該用抽象類? 什麼時候該用接口? 實際上,判斷標準很簡單:表示is-a 關係並且爲了解決代碼複用問題,就使用抽象類;如果表示 has-a關係,並且是爲了解決抽象而非代碼複用問題,那麼就使用接口。
基於接口而非實現編程
應用這條原則,可以將業務概念與實現想分離,封裝不穩定的實現,暴露穩定的接口,上游系統基於約定準則(接口)而非實現編程,不依賴不穩定的細節,如此當實現發生變化之後,上游系統代碼基本不需要改動,以此來降低耦合性,提高擴展性。
當前準則被稱之爲 “基於抽象而非實現編程”, “基於約定規則而非實現編程”(個人認爲與springBoot框架的約定大於配置理念有異曲同工之妙)。畢竟軟件開發中最大的挑戰就是需求的變化,導致實現的變化
多用組合少用繼承
爲什麼不推薦是使用繼承?
繼承是面向對象的四大特性之一,用來表示類之間的is-a關係,可以解決代碼複用問題,雖然有諸多作用但是當類的繼承層次過深, 那就是挖深坑埋自己了。在這種情況下我們應該少用,乃至不用繼承。
組合先比繼承有哪些優勢?
繼承主要有三個作用: 表示 is-a 關係,支持多態特性,代碼複用。而這三個作用都可以通過 組合、接口、委託三個技術手段來達成。除此之外,組合還可以解決層次過深,過複雜的繼承關係影響代碼維護的問題。
如何判斷該使用繼承or組合?
儘管業界鼓勵多用組合少用繼承,但是組合並非完美,繼承也不是一無是處。還是要實際場合實際分析
貧血模型 VS 充血模型
我們平時所做的WEB 項目開發,大多數都是基於 貧血模型的MVC三層架構,也就是傳統開發模式,之所以被稱之爲傳統是因爲:相對於充血模型的 DDD 開發模式而言的。
基於貧血模性的傳統開發模式,是典型的面向過程編程風格。
相反基於充血模型的DDD開發模式,是典型的面向對象編程風格
不過,DDD並非銀彈,對於業務不復雜的系統來說。基於貧血模型的三層架構完全夠用,而且快速,簡單,易上手。 基於充血模型的 DDD 反倒顯得繁複,無法發揮作用。 但是在業務複雜的系統來說,基於充血模型的DDD開發模式,因爲前期在設計上投入更多時間和經歷,來提高代碼的複用和可維護性,所以比貧血模型更有優勢。
基於充血模型的DDD 開發模式與基於貧血模型的傳統開發模式相比, Controller層和Repository層代碼基本相同,這是因爲 Repository層的 Entity聲明週期有限,Controller層面的VO只是單純作爲一種DTO。兩部分業務邏輯都不復雜,業務邏輯主要在Service。
Service在充血模型下負責與 Repository層和Controller打交道,和組織跨領域模型的業務代碼,冪等事務等工作。至於其中的業務邏輯移動到 Domain的領域模型之中,讓Service依賴Domain 來實現業務。
Service在貧血模型下負責與 Repository層和Controller打交道。和處理與實現所有的業務邏輯。
(感謝 前 Google工程師王爭在極客時間開設的設計模式之美專欄, 以上內容多數來自其專欄並以其爲基礎加入了一些個人總結。算作是學習筆記)