如何學(xué)好設(shè)計(jì),做好架構(gòu)?學(xué)會(huì)六大設(shè)計(jì)原則是基礎(chǔ)
很多開發(fā)者在面對(duì)設(shè)計(jì)/架構(gòu)時(shí),常常有想學(xué)但無從下手,學(xué)了又不會(huì)用的困擾。學(xué)習(xí)設(shè)計(jì)并不是只學(xué)習(xí)設(shè)計(jì)模式,在進(jìn)行設(shè)計(jì)時(shí),我們需要底層思想來支持,這里的底層思想其實(shí)就是設(shè)計(jì)原則,而設(shè)計(jì)原則則是面向?qū)ο缶幊袒诂F(xiàn)實(shí)背景衍生出來的一套規(guī)則,用來解決開發(fā)中的痛點(diǎn)。
一個(gè)好的架構(gòu)需要反復(fù)進(jìn)行思考以及設(shè)計(jì)。
一、面向?qū)ο?/h2>
什么是面向?qū)ο? 先來看下基本定義:
面向?qū)ο笫且环N風(fēng)格,會(huì)以類作為代碼的基本單位,通過對(duì)象訪問,并擁有封裝、繼承、多態(tài)、抽象四種特性作為基石,可讓其更為智能。代表語言Java。
1. 四大特性(也有人說三種,不要糾結(jié))
(1) 封裝
封裝也被稱為信息隱藏。類通過暴露有限的訪問接口,授權(quán)外部僅能通過類提供的方式(或者叫函數(shù))來訪問內(nèi)部信息或者數(shù)據(jù)。
將用戶信息隱藏在內(nèi)部,在確實(shí)需要訪問時(shí),再通過暴露出來的唯一入口進(jìn)行,這一過程就是封裝,合理運(yùn)用封裝可以降低模塊間依賴關(guān)系(松耦合)。
(2) 繼承
“繼承”是面向?qū)ο笾械牡诙卣?,體現(xiàn)了類與類之間的“is-a”關(guān)系。當(dāng)兩個(gè)類進(jìn)行繼承關(guān)聯(lián)綁定的時(shí)候,子類自動(dòng)具備來自于父類的屬性和行為。可以提升復(fù)用性解決模板代碼問題,提升開發(fā)效率的同時(shí)也解決了錯(cuò)寫,漏寫帶來的問題。
(3) 多態(tài)
一句話概括"多態(tài)":一個(gè)對(duì)象有多種形態(tài)。
合理運(yùn)用多態(tài)可以寫出易擴(kuò)展的代碼,基于接口而非實(shí)現(xiàn)編程和開閉原則的核心
(4) 抽象
抽象的目的是為了隱藏方法的具體實(shí)現(xiàn),讓調(diào)用者只需要關(guān)心方法提供了哪些方法(功能),并不需要知道這些功能是如何實(shí)現(xiàn)的。在Java中體現(xiàn)方式是接口和抽象類
(5) 接口和抽象類的區(qū)別
- 接口更側(cè)重于功能的設(shè)計(jì),并且能將具體實(shí)現(xiàn)與調(diào)用者隔離,一般要以接口隔離原則設(shè)計(jì)接口及粒度越細(xì)越好
- 抽象類更側(cè)重于提升復(fù)用性,在原有的基礎(chǔ)上預(yù)留擴(kuò)展點(diǎn)供開發(fā)者靈活實(shí)現(xiàn)
區(qū)別:接口可以降低模塊間耦合性,抽象類可提升復(fù)用性。
相同點(diǎn):均有較好的擴(kuò)展性,符合開閉原則
2. 誕生背景
談及面向?qū)ο蟊囟x不開面向過程,畢竟它就是由面向過程衍變而來,吸收其大部分優(yōu)點(diǎn)并解決其痛點(diǎn)。那什么是面向過程呢?基本定義如下:
分析出解決問題所需要的步驟,然后用函數(shù)把這些步驟一步一步實(shí)現(xiàn),使用的時(shí)候一個(gè)一個(gè)依次調(diào)用就可以了,更側(cè)重于功能的設(shè)計(jì)。代表語言C
面向?qū)ο笤诰幊讨靶枰谒拇筇匦詫?duì)功能做建模設(shè)計(jì),可以提高代碼安全性、復(fù)用性、擴(kuò)展性,更易于維護(hù)。既然面向?qū)ο筮@么智能為什么面向過程語言還沒有被淘汰?其實(shí)面向?qū)ο笳Z言的智能是針對(duì)我們開發(fā)者的,為了能讓我們能寫出易于維護(hù)的代碼會(huì)多做一步設(shè)計(jì),雖然離開發(fā)者更近了 但離機(jī)器卻遠(yuǎn)了,畢竟機(jī)器只認(rèn)識(shí)0和1而已。C語言規(guī)則簡單易于形成機(jī)器碼,所以執(zhí)行效率高,這也是其沒有被淘汰的原因。
二、六大設(shè)計(jì)原則才是一切設(shè)計(jì)的基石
設(shè)計(jì)原則是基于面向?qū)ο笏枷胙茏兂鰜淼囊恍┮?guī)則,用來解決實(shí)際開發(fā)中的一些痛點(diǎn),是所有設(shè)計(jì)的底層思想,也是我個(gè)人認(rèn)為是設(shè)計(jì)/架構(gòu)領(lǐng)域最重要的知識(shí),所以請大家務(wù)必掌握好
(1) 單一設(shè)計(jì)原則
單一原則很好理解,指一個(gè)函數(shù)或者一個(gè)類再或者一個(gè)模塊,職責(zé)越單一復(fù)用性就越強(qiáng),同時(shí)能夠間接降低耦合性。
(2) 開閉原則
一句話概括開閉原則:對(duì)擴(kuò)展開放,修改關(guān)閉。它即充分詮釋抽象、多態(tài)特性,又是多數(shù)行為型設(shè)計(jì)模式的基礎(chǔ),遍布于各大優(yōu)秀框架之中,是最重要的一條設(shè)計(jì)原則,僅這一條原則就能把你的設(shè)計(jì)能力提高40%
(3) 迪米特法則
基本概念:不該有直接依賴關(guān)系的模塊不要有依賴。有依賴關(guān)系的模塊之間,盡量只依賴必要的接口。迪米特法則很好理解并且非常實(shí)用,它和單一設(shè)計(jì)原則很像,前者符合松耦合后者符合高內(nèi)聚。
(4) 接口隔離原則
基本概念:接口的調(diào)用者不應(yīng)該依賴它不需要的接口。乍一看與迪米特法則很相似。接口隔離原則與迪米特法則目的很相似,都可以降低模塊間依賴關(guān)系。但接口隔離更側(cè)重于設(shè)計(jì)單一接口,提升復(fù)用性并間接降低模塊間依賴關(guān)系,而迪米特法則是直接降低模塊間依賴關(guān)系。
(5) 里氏替換原則
基本概念:
設(shè)計(jì)子類的時(shí)候,要遵守父類的行為約定。父類定義了函數(shù)的行為約定,子類可以改變函數(shù)的內(nèi)部實(shí)現(xiàn)邏輯,但不能改變函數(shù)原有的行為約定。
里氏替換非常簡單并且很容易遵守,在使用繼承時(shí),允許復(fù)寫父類方法,但不要改變其功能。比如自定義View,子類的onMeasure中一定要調(diào)用setMeasureaDimission()方法(或者直接使用super),否則會(huì)影響父類方法功能(會(huì)拋異常),也既違背了里氏替換原則。
(6) 依賴倒置原則
控制反轉(zhuǎn): 提及依賴倒置便不得不提控制反轉(zhuǎn),一句話概括:將復(fù)雜的程序操作控制權(quán)由程序員交給成熟的框架處理,程序員->成熟的框架為反轉(zhuǎn),框架應(yīng)暴露出擴(kuò)展點(diǎn)由程序員實(shí)現(xiàn)。
什么是依賴倒置?
高層模塊(使用者)不應(yīng)依賴低層模塊(被使用者),它們共同依賴同一個(gè)抽象,抽象不要依賴具體實(shí)現(xiàn)細(xì)節(jié),具體實(shí)現(xiàn)細(xì)節(jié)依賴抽象。
其實(shí)核心點(diǎn)就是基于接口而非實(shí)現(xiàn)編程,高層模塊(業(yè)務(wù)層)不依賴于低層模塊(SQLiteDao/RoomDao),而是依賴于抽象(IDao),可見依賴倒置也是開閉原則擴(kuò)展而來。 區(qū)別是依賴倒置更側(cè)重于指導(dǎo)框架的設(shè)計(jì),框架層應(yīng)該盡量將更多的細(xì)節(jié)隱藏在內(nèi)部,對(duì)外只暴露抽象(抽象類/接口),指導(dǎo)框架設(shè)計(jì)這方面核心就是控制反轉(zhuǎn)。