前言
不久前我寫了兩篇關(guān)于架構(gòu)的文章,真沒想到能有這么高的關(guān)注度,后續(xù)也不斷有小伙伴私信向我請(qǐng)教設(shè)計(jì)/架構(gòu)的學(xué)習(xí)方式,從他們提問得問題以及表現(xiàn)出的困惑我能感覺到 想學(xué)又無從下手,學(xué)了又不會(huì)用。
其實(shí)好多小伙伴都認(rèn)為學(xué)習(xí)設(shè)計(jì)就是學(xué)習(xí)設(shè)計(jì)模式,這是一個(gè)誤區(qū),沒有底層思想的支持寫出來的設(shè)計(jì)模式無非就是生搬硬套罷了,這里的底層思想其實(shí)就是設(shè)計(jì)原則,而設(shè)計(jì)原則則是面向?qū)ο缶幊?/code>基于現(xiàn)實(shí)背景衍生出來的一套規(guī)則,用來解決開發(fā)中的痛點(diǎn)。
好的架構(gòu)需要反復(fù)進(jìn)行思考以及設(shè)計(jì),今天我將從面向?qū)ο?/code>為出發(fā)點(diǎn) 來分享自己對(duì)設(shè)計(jì)/架構(gòu)衍變過程的理解,盡量幫你理清背景 抓住本質(zhì)
文章目錄看起來有些枯燥,但有別于其他八股文,跟著流程去學(xué)大概率可以短時(shí)間內(nèi)見成效,所以請(qǐng)耐心閱讀
目錄
-
1. 面向?qū)ο?/strong>
- 1.1 四大特性
- 1.2 誕生背景
-
2. 六大設(shè)計(jì)原則才是一切設(shè)計(jì)的基石
- 2.1 單一設(shè)計(jì)原則
- 2.2 開閉原則
- 2.3 迪米特法則
- 2.4 接口隔離原則
- 2.5 里氏替換原則
- 2.6 依賴倒置原則
-
3. 設(shè)計(jì)模式只是設(shè)計(jì)原則的產(chǎn)物而已
- 3.1 設(shè)計(jì)模式該怎么去學(xué)?
- 3.2 "鹽加少許" 只可意會(huì)
- 3.3 自創(chuàng)設(shè)計(jì)模式
- 4. 如何做好架構(gòu)?
1. 面向?qū)ο?/h2>
什么是面向?qū)ο螅?/code> 估計(jì)這個(gè)問題能難倒一大片同學(xué),相信讀完本文你心里應(yīng)該會(huì)有一個(gè)合適的答案。先來看下基本定義:
面向?qū)ο笫且环N風(fēng)格,會(huì)以類作為代碼的基本單位,通過對(duì)象訪問,并擁有封裝、繼承、多肽、抽象四種特性作為基石,可讓其更為智能。代表語言Java
1.1 四大特性(也有人說三種,不要糾結(jié))
封裝
封裝也也可稱之為信息隱藏。類通過暴露有限的訪問接口,授權(quán)外部僅能通過類提供的方式(或者叫函數(shù))來訪問內(nèi)部信息或者數(shù)據(jù)。舉個(gè)例子解釋下:
class User{
private String idNumber;
private String name;
public String getIdNumber() {
return idNumber;
}
public void setIdNumber(String idNumber) {
this.idNumber = idNumber;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
User類中包含身份證號(hào)、姓名等個(gè)人信息,這些屬性一旦暴露那外界就可以隨意修改,進(jìn)而可能產(chǎn)生安全隱患。此時(shí)可通過private修飾符將其隱藏在內(nèi)部,如果確實(shí)需要訪問只能通過暴露出來的唯一入口getter,setter方法進(jìn)行,這一過程就是封裝
合理運(yùn)用封裝可以降低模塊間依賴關(guān)系(松耦合)
繼承
“繼承”是面向?qū)ο笾械牡诙卣?,體現(xiàn)了類與類之間的“is-a”關(guān)系。當(dāng)兩個(gè)類進(jìn)行繼承關(guān)聯(lián)綁定的時(shí)候,子類自動(dòng)具備來自于的父類的屬性和行為。可以提升復(fù)用性解決模板代碼問題,提升開發(fā)效率的同時(shí)也解決了
錯(cuò)寫,漏寫帶來的問題
多肽
一句話概括"多肽":一個(gè)對(duì)象多種形態(tài)。舉個(gè)例子說明下:
interface IFruit{
String getColor();
}
class Apple implements IFruit{
@Override
public String getColor() {
return "red";
}
}
IFruit fruit = new Apple();
fruit.getColor();
通過聲明的IFruit類型可以對(duì)其實(shí)現(xiàn)類Apple進(jìn)行編程,好處就是擴(kuò)展性強(qiáng),當(dāng)需要替換具體實(shí)現(xiàn)Apple時(shí),對(duì)IFruit`的操作完全不用改
合理運(yùn)用多肽可以寫出易擴(kuò)展的代碼,基于接口而非實(shí)現(xiàn)編程和開閉原則的核心
抽象
抽象的目的是為了隱藏方法的具體實(shí)現(xiàn),讓調(diào)用者只需要關(guān)心方法提供了哪些方法(功能),并不需要知道這些功能是如何實(shí)現(xiàn)的。在
Java中體現(xiàn)方式是接口和抽象類
接口和抽象類的區(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ò)展性,符合開閉原則
tips
面向?qū)ο蟮?code>四大特性相信大家都很熟悉,本小結(jié)只是幫大家做一次簡單的回憶,關(guān)于其背景和職責(zé)下半問會(huì)詳細(xì)描述
1.2 誕生背景
談及面向?qū)ο蟊囟ゲ婚_面向過程,畢竟它就是由面向過程衍變而來,吸收其大部分優(yōu)點(diǎn)并解決其痛點(diǎn)。那什么是面向過程呢?基本定義如下:
分析出解決問題所需要的步驟,然后用函數(shù)把這些步驟一步一步實(shí)現(xiàn),使用的時(shí)候一個(gè)一個(gè)依次調(diào)用就可以了,更側(cè)重于功能的設(shè)計(jì)。代表語言C
用代碼體現(xiàn)就是下面這樣:
#java版面向過程
public class Wallet {
/**
* 余額
*/
int balance;
/**
* 存錢
*/
void saveMoney(int money){
balance += money;
}
/**
* 花錢
*/
void spendMoney(int money){
balance -= money;
}
}
無權(quán)限修飾符將內(nèi)部信息全部暴露,簡單粗暴很符合初級(jí)程序員的思維,但帶來的問題很明顯,外部可直接訪問balance修改錢包內(nèi)余額,現(xiàn)象就是"我錢包都沒掏出來但里面錢卻變少/多了"。面向過程在開發(fā)中帶來的問題遠(yuǎn)不止這些,所以在此背景下誕生了面向?qū)ο?br>
通過面向?qū)ο?code>封裝特性將面向過程代碼做個(gè)改進(jìn),如下:
#java版面向?qū)ο?
public class Wallet {
/**
* 余額
*/
private int balance;
/**
* 存錢
*/
void saveMoney(int money){
balance += money;
}
/**
* 花錢
*/
void spendMoney(int money){
balance -= money;
}
}
通過封裝特性將balance通過private修飾,這樣外部就沒有權(quán)限直接修改金額,避免誤操作帶來的未知風(fēng)險(xiǎn),滿足松耦合特性
面向過程編程偏向于功能的開發(fā),簡單粗暴難以維護(hù)。而面向?qū)ο?/code>在編程之前需要基于四大特性對(duì)功能做建模設(shè)計(jì),可以提高代碼安全性、復(fù)用性、擴(kuò)展性,更易于維護(hù)
既然面向?qū)ο?/code>這么智能為什么面向過程語言還沒有被淘汰?其實(shí)面向?qū)ο?/code>語言的智能是針對(duì)我們開發(fā)者的,為了能讓我們能寫出易于維護(hù)的代碼會(huì)多做一步設(shè)計(jì),雖然離開發(fā)者更近了 但離機(jī)器確遠(yuǎn)了,畢竟機(jī)器只認(rèn)識(shí)0和1而已。C語言規(guī)則簡單易于形成機(jī)器碼,所以執(zhí)行效率高,這也是其沒有被淘汰的原因。
小提示:
不要以為用了面向?qū)ο笳Z言寫出的就是面向?qū)ο蟠a,如果沒有利用其特性那可能還是面向過程,比如沒有利用權(quán)限修飾符、一個(gè)類一把梭等等....
2. 六大設(shè)計(jì)原則才是一切設(shè)計(jì)的基石
設(shè)計(jì)原則是基于面向?qū)ο笏枷胙茏兂鰜淼囊恍┮?guī)則,用來解決實(shí)際開發(fā)中的一些痛點(diǎn),是所有設(shè)計(jì)的底層思想,也是我個(gè)人認(rèn)為是設(shè)計(jì)/架構(gòu)領(lǐng)域最重要的知識(shí),所以請(qǐng)大家務(wù)必掌握好
2.1 單一設(shè)計(jì)原則
單一原則很好理解,指一個(gè)函數(shù)或者一個(gè)類再或者一個(gè)模塊,職責(zé)越單一復(fù)用性就越強(qiáng),同時(shí)能夠間接降低耦合性。
案例:本地獲取用戶信息,提交到網(wǎng)絡(luò)
fun post(){
//創(chuàng)建數(shù)據(jù)庫訪問對(duì)象Dao
val userDao = ...(這一過程很復(fù)雜)
//從本地獲取
val age = dao.getAge()
val name = dao.getName()
//....省略大量字段
//將個(gè)人信息提交至網(wǎng)絡(luò)
http.request(age,name,....)
}
以上案例將創(chuàng)建、獲取、提交三步操作寫到同一個(gè)函數(shù)中,很顯然違背了單一設(shè)計(jì)原則,面臨的問題也很明顯,當(dāng)修改創(chuàng)建、獲取、提交任一過程時(shí)都會(huì)影響到其他二者,千萬不要說"我注意一點(diǎn)就不會(huì)出錯(cuò)"這種話,因?yàn)槿瞬皇菣C(jī)器改動(dòng)就可能出錯(cuò),此時(shí)可以通過單一設(shè)計(jì)原則做一次重構(gòu),代碼如下:
fun getUserDao():UserDao{
...
return dao
}
fun getUserInfo():UserInfo{
val dao = getUserDao()
val userInfo = UserInfo()
userInfo.age = dao.getAge()
userInfo.name = dao.getName()
...
return userInfo
}
fun post(){
val userInfo = getUserInfo()
//將個(gè)人信息提交至網(wǎng)絡(luò)
http.request(userInfo.age,userInfo.name,....)
}
三步操作被拆至三個(gè)函數(shù) 互不影響,從根本上杜絕因改動(dòng)帶來的一系列問題。所以使用面向?qū)ο笳Z言開發(fā)時(shí),不要急著寫代碼,要優(yōu)先考慮下模塊、類、函數(shù)...的設(shè)計(jì)是否足夠單一
2.2 開閉原則
一句話概括開閉原則:對(duì)擴(kuò)展開放,修改關(guān)閉。它即充分詮釋抽象、多肽特性,又是多數(shù)行為型設(shè)計(jì)模式的基礎(chǔ),遍布于各大優(yōu)秀框架之中,是最重要的一條設(shè)計(jì)原則,僅這一條原則就能把你的設(shè)計(jì)能力提高40%
舉個(gè)例子讓大家感受一下:
需求:通過SQLite做CRUD操作
class SQLiteDao{
public void insert() {
//通過SQLite做insert
}
public void delete() {
//通過SQLite做insert
}
}
SQLiteDao dao = new SQLiteDao();
dao.insert();
...
以上是最簡單粗暴的寫法,但存在一個(gè)致命問題,如果某一天想替換SQLite業(yè)務(wù)層基本要?jiǎng)右槐?,改?dòng)就存在出錯(cuò)的可能,并且需要做大量的重復(fù)操作
面對(duì)以上問題可以利用抽象、多肽特性基于開閉原則做出重構(gòu),代碼如下:
interface IDao{
void insert();
void delete();
}
class SQLiteDao implements IDao{
@Override
public void insert() {
//通過SQLite做insert
}
@Override
public void delete() {
//通過SQLite做insert
}
}
class RoomDao implements IDao{
@Override
public void insert() {
//通過Room做insert
}
@Override
public void delete() {
//通過Room做delete
}
}
//擴(kuò)展點(diǎn)
IDao dao = new SQLiteDao();
dao.insert();
- 定義功能接口
IDao - 定義類
SQLiteDao、RoomDao并實(shí)現(xiàn)IDao的功能 - 業(yè)務(wù)層基于接口
IDao進(jìn)行編程
重構(gòu)后,當(dāng)需要將SQLite替換至Room時(shí),只需將注釋擴(kuò)展點(diǎn)處SQLiteDao替換成RoomDao即可,其他地方完全不用改動(dòng)。這就是所謂的擴(kuò)展開放,修改關(guān)閉
在業(yè)務(wù)不斷迭代情況下,唯一不變的就是改變,這種背景下我們能做的只有在代碼中基于開閉原則多留擴(kuò)展點(diǎn)以不變應(yīng)萬變。
2.3 迪米特法則
基本概念:不該有直接依賴關(guān)系的模塊不要有依賴。有依賴關(guān)系的模塊之間,盡量只依賴必要的接口。
迪米特法則很好理解并且非常實(shí)用,違背迪米特法則會(huì)產(chǎn)生什么問題?還以2.1面向過程代碼舉例:
class Wallet{
/**
* 余額
*/
int balance;
/**
* 存錢
*/
void saveMoney(int money){
balance += money;
}
/**
* 花錢
*/
void spendMoney(int money){
balance -= money;
}
}
Wallet的設(shè)計(jì)違背了迪米特法則,畢竟外部只需要save和spend功能,將balance暴漏使用者就有權(quán)限直接修改其值,可能會(huì)對(duì)整個(gè)Wallet功能造成影響。此時(shí)應(yīng)基于迪米特法則對(duì)Wallet進(jìn)行改造,將balance通過封裝特性增加private修飾符
迪米特法則和單一設(shè)計(jì)原則很像,前者符合松耦合后者符合高內(nèi)聚
2.4 接口隔離原則
基本概念:接口的調(diào)用者不應(yīng)該依賴它不需要的接口。
乍一看與迪米特法則很相似。先來看下什么樣的接口違背接口隔離原則:
interface Callback{
/**
* 點(diǎn)擊事件回調(diào)方法
*/
void clickCallback();
/**
* 滾動(dòng)事件回調(diào)方法
*/
void scrollCallback();
}
接口Callback包含點(diǎn)擊、滾動(dòng)兩個(gè)回調(diào)方法,面臨的問題有兩個(gè):
- 某些特定場景
使用者只需要依賴點(diǎn)擊回調(diào),那滾動(dòng)回調(diào)便成了多余,把外部不需要的功能暴露出來就存在誤操作的可能。 -
點(diǎn)擊和滾動(dòng)本來就是兩種特性,強(qiáng)行揉到一塊只能讓接口更臃腫,進(jìn)而降低其復(fù)用性
根據(jù)接口隔離原則改造后如下:
interface ClickCallback{
/**
* 點(diǎn)擊事件回調(diào)方法
*/
void clickCallback();
}
interface ScrollCallback{
/**
* 滾動(dòng)事件回調(diào)方法
*/
void scrollCallback();
}
基于單一設(shè)計(jì)原則把點(diǎn)擊和滾動(dòng)拆分成兩個(gè)接口,將模塊間隔離的更徹底。并且由于粒度更細(xì),所以復(fù)用性也更高
接口隔離原則與迪米特法則目的很相似,都可以降低模塊間依賴關(guān)系。但接口隔離更側(cè)重于設(shè)計(jì)單一接口,提升復(fù)用性并間接降低模塊間依賴關(guān)系,而迪米特法則是直接降低模塊間依賴關(guān)
2.5 里氏替換原則
基本概念:
設(shè)計(jì)子類的時(shí)候,要遵守父類的行為約定。父類定義了函數(shù)的行為約定,子類可以改變函數(shù)的內(nèi)部實(shí)現(xiàn)邏輯,但不能改變函數(shù)原有的行為約定。
里氏替換非常簡單并且很容易遵守,在使用繼承時(shí),允許復(fù)寫父類方法,但不要改變其功能。比如自定義View,子類的onMeasure中一定要調(diào)用setMeasureaDimission()方法(或者直接使用super),否則會(huì)影響父類方法功能(會(huì)拋異常),也既違背了里氏替換原則。
2.6 依賴倒置原則
2.1章節(jié)查看
什么是依賴倒置?
高層模塊(使用者)不應(yīng)依賴低層模塊(被使用者),它們共同依賴同一個(gè)抽象,抽象不要依賴具體實(shí)現(xiàn)細(xì)節(jié),具體實(shí)現(xiàn)細(xì)節(jié)依賴抽象。
其實(shí)核心點(diǎn)就是基于接口而非實(shí)現(xiàn)編程,2.2數(shù)據(jù)庫案例也符合依賴倒置原則,高層模塊(業(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)
3. 設(shè)計(jì)模式只是設(shè)計(jì)原則的產(chǎn)物而已
設(shè)計(jì)模式共有23種,詳細(xì)描述都能出一本書出來。本小結(jié)僅會(huì)分享一些通用的思路,個(gè)人認(rèn)為還是比較硬核的,畢竟設(shè)計(jì)主要還是思想,而非生搬硬套
3.1 設(shè)計(jì)模式該怎么去學(xué)?
本小節(jié)會(huì)分析幾個(gè)常見的設(shè)計(jì)模式核心思想以及設(shè)計(jì)背景,用于拋磚引玉
工廠模式
基本概念:用于創(chuàng)建復(fù)雜對(duì)象
創(chuàng)建復(fù)雜對(duì)象常規(guī)寫法如下:
class B{
...
}
class D{
void test(){
B b = ....(創(chuàng)建B的過程很復(fù)雜)
...
}
}
在使用的地方直接創(chuàng)建,如果直接new倒也沒啥問題,但如果創(chuàng)建過程過于復(fù)雜,當(dāng)修改創(chuàng)建過程時(shí)就會(huì)影響到test(),進(jìn)而存在一些未知的隱患。
這一問題可通過迪米特法則進(jìn)行改造:
class FactoryB{
...
static B createB(){
.....(B創(chuàng)建過程)
return b;
}
}
class D{
void test1(){
B b = FactoryB.create();
...
}
}
B的創(chuàng)建本身就于調(diào)用者無關(guān),將創(chuàng)建過程轉(zhuǎn)移到類FactoryB中,根本上避免了創(chuàng)建過程對(duì)調(diào)用者的影響。改造后就是一個(gè)標(biāo)準(zhǔn)的簡單工廠模式,所以簡單工廠模式的核心思想就是迪米特法則
觀察者模式
基本概念:當(dāng)一個(gè)對(duì)象發(fā)生改變時(shí)需要通知到另一個(gè)對(duì)象
粗暴寫法:
/**
* 觀察者
*/
class Observer{
/**
* 接收通知
*/
void receive(){
//具體邏輯
}
}
/**
* 被觀察者
*/
class Observable{
/**
* 發(fā)送通知
*/
void send(){
Observer observer = new Observer();
observer.receive();
}
}
Observable(被觀察者)內(nèi)部直接持有Observer(觀察者),在合適的時(shí)機(jī)發(fā)出通知,但這種寫法有兩個(gè)很明顯的問題:
- 擴(kuò)展性差:當(dāng)存在多個(gè)觀察者
Observer1,Observer2...時(shí),Observable需要逐個(gè)手動(dòng)創(chuàng)建發(fā)出通知 - 耦合性強(qiáng):
Observable直接持有Observer對(duì)象,而Observer可能暴露出一些Observable不需要的屬性/方法,存在誤操作的風(fēng)險(xiǎn)
面對(duì)以上兩個(gè)問題可以利用開閉原則和接口隔離原則進(jìn)行改造:
interface IObserver{
/**
* 接收通知
*/
void receive();
}
class Observable{
/**
* 觀察者集合
*/
private final List<IObserver> observers = new ArrayList<>();
/**
* 發(fā)送通知
*/
void send(){
for (IObserver observer : observers){
observer.receive();
}
}
}
以上是一個(gè)標(biāo)準(zhǔn)的觀察者模式。通過接口隔離原則設(shè)計(jì)IObserver接口保證其單一性,避免模塊之間依賴關(guān)系過強(qiáng)造成的安全隱患,解決了耦合性強(qiáng)問題。通過開閉原則維護(hù)一個(gè)observers,當(dāng)新增觀察者時(shí)只需添加到observers即可,符合擴(kuò)展開放、修改關(guān)閉,解決類擴(kuò)展性差問題。
所以開閉原則,接口隔離原則是觀察者模式擴(kuò)展性強(qiáng),耦合性低的根本原因吶
以上三個(gè)案例足以表明設(shè)計(jì)模式的核心就是設(shè)計(jì)原則吶,所以學(xué)會(huì)設(shè)計(jì)模式的竅門就是先掌握設(shè)計(jì)原則
3.2 "鹽加少許" 只可意會(huì)
據(jù)我所知有一部分小伙伴覺得用設(shè)計(jì)模式很酷,以至于拿著錘子看什么都是釘子,很簡單的代碼還非要用幾個(gè)設(shè)計(jì)模式包裝下。還有另一部分小伙伴對(duì)設(shè)計(jì)模式理解不夠深刻,把握不好應(yīng)用場景,經(jīng)常生搬硬套做出多余的設(shè)計(jì)。以上兩種現(xiàn)象 不但解決不了任何問題反而會(huì)降低代碼的可讀性
究竟什么場景下需要用設(shè)計(jì)模式呢?關(guān)于這個(gè)問題我只能回答合適的場景,因?yàn)樗緵]有一個(gè)固定答案。就如同老師傅做飯時(shí)講的少許鹽、少許油一樣,因?yàn)椴煌氖巢男枰挠望}不一樣,所以不好去量化,只能根據(jù)自己的經(jīng)驗(yàn)去放?;貧w到代碼中也是一樣的,我們?cè)趯?shí)踐中需要不斷思考,嘗試去發(fā)現(xiàn)開發(fā)中的痛點(diǎn),設(shè)計(jì)模式就是用來解決這些痛點(diǎn)的,所以只有理清背景才能將設(shè)計(jì)模式用的恰到好處
3.3 自創(chuàng)設(shè)計(jì)模式
有一說一23種設(shè)計(jì)模式我也不是全懂,但由于我懂設(shè)計(jì)原則我一樣可以寫出易維護(hù)的代碼,甚至自創(chuàng)設(shè)計(jì)模式
在接觸LiveData之前,其實(shí)我已經(jīng)有意無意感受到了數(shù)據(jù)驅(qū)動(dòng)的思想,在傳統(tǒng)的MVP模式下我會(huì)在View層事先寫好對(duì)應(yīng)UI渲染邏輯,Presenter由接口進(jìn)行驅(qū)動(dòng)(這其實(shí)也是數(shù)據(jù)驅(qū)動(dòng)UI)。
當(dāng)LiveData/DataBinding走進(jìn)視線并且大家開始討論數(shù)據(jù)驅(qū)動(dòng)UI時(shí),那一刻我仿佛找到了組織,自己的想法終于得到了驗(yàn)證。之所以我能夠有意無意遵守數(shù)據(jù)驅(qū)動(dòng)UI是因?yàn)槲以揪驼莆樟?code>控制反轉(zhuǎn)思想
閱讀Retrofit源碼前我甚至不知道門面模式的存在,但我依舊能理解ApiService奧妙之所在,無非就是想將Retrofit的實(shí)現(xiàn)盡量屏蔽在其內(nèi)部,盡可能降低模塊間依賴關(guān)系,符合迪米特法則同時(shí)也是門面模式的一種寫法。
說了這么多還是想告訴大家:設(shè)計(jì)原則才是根本
4. 如何做好架構(gòu)?
掌握設(shè)計(jì)原則可以寫出擴(kuò)展性強(qiáng)、復(fù)用性高...的代碼
掌握設(shè)計(jì)模式可以設(shè)計(jì)出易用性強(qiáng)、安全性高...成熟的框架
掌握設(shè)計(jì)原則、設(shè)計(jì)模式,可以設(shè)計(jì)出容錯(cuò)率更高的架構(gòu)
那什么是架構(gòu)?
架構(gòu)是一個(gè)很籠統(tǒng)的概念,上至框架選型下至業(yè)務(wù)代碼都能稱為架構(gòu)的一部分,比喻到蓋房子 設(shè)計(jì)圖,打地基,選料…都能稱之為架構(gòu),總之能夠提升項(xiàng)目穩(wěn)定性以及開發(fā)效率就是好架構(gòu)。好的架構(gòu)不是一蹴而就,而是根據(jù)面臨的問題不斷添磚加瓦
架構(gòu)是如何衍變的?
- 遠(yuǎn)古時(shí)代,基于Activity和XML開發(fā),XML這種結(jié)構(gòu)可以天然的將視圖與Activity隔離,看起來很美妙,我也很開心..
- 隨著業(yè)務(wù)的發(fā)展,Activity代碼不斷壯大,各種邏輯全都揉到一塊,常常改一處崩多處。我覺得不能再拖了,得趕緊基于
單一設(shè)計(jì)原則將代碼進(jìn)行模塊化。模塊化后效果很明顯,莫名其妙的bug少了很多..- 某一天網(wǎng)絡(luò)請(qǐng)求時(shí)發(fā)現(xiàn)參數(shù)一直對(duì)不上,各種排查才發(fā)現(xiàn)原來是修改某個(gè)
View時(shí)對(duì)應(yīng)的數(shù)據(jù)卻忘記改了,這個(gè)問題真的很頭痛。偶然間發(fā)現(xiàn)LiveData、DataBinding,這玩意基于控制反轉(zhuǎn)+觀察者設(shè)計(jì) 改變數(shù)據(jù)就能修改UI,那我肯定毫不猶豫引入到項(xiàng)目中啊。從此我再也不用擔(dān)心數(shù)據(jù)UI一致性問題了..- 數(shù)年后,項(xiàng)目工程逐漸龐大,編譯一次都要好幾分鐘,找個(gè)文件找半天還容易改錯(cuò),令大家苦不堪言。聽說
Android可以依據(jù)單一原則將代碼拆分至多個(gè)module中并可以單獨(dú)運(yùn)行,試了試果然可以..- 最近有同事經(jīng)常跟我抱怨:
“每個(gè)Activity都有好多重復(fù)代碼啊,而且一不留神容易錯(cuò)寫、忘寫”,這讓我想到了模版設(shè)計(jì)模式,將通用功能封裝在內(nèi)部并暴露一些抽象方法(鉤子方法),新來的同事也變得開心的了,基于這套模板他可以無障礙開發(fā)..- 未完待續(xù)...
以上是一個(gè)簡單架構(gòu)的衍變過程,選用的每一個(gè)庫都是基于設(shè)計(jì)原則,設(shè)計(jì)模式拓展出來用來解決開發(fā)痛點(diǎn)的。但是團(tuán)隊(duì)開發(fā)人員水平可能參次不齊,不一定能領(lǐng)悟到架構(gòu)的含義,僅從口頭上約束可能作用不大,此時(shí)一般會(huì)通過模板模式將通用信息做封裝,在內(nèi)部協(xié)調(diào)好各模塊間關(guān)系,并暴露出對(duì)應(yīng)的泛型、抽象方法(鉤子),這樣開發(fā)人員在使用模板類的時(shí)候就會(huì)被強(qiáng)制遵守現(xiàn)有的規(guī)則。
tips
關(guān)于
面向?qū)ο?、設(shè)計(jì)原則、設(shè)計(jì)模式如果詳細(xì)講解能寫三大本書出來。本文主要描述其基本概念、設(shè)計(jì)背景以及三者之間的關(guān)系,起到拋磚引玉的作用。想真的學(xué)好設(shè)計(jì)、做好架構(gòu) 需要不斷從實(shí)踐中體會(huì)、思考。
綜上所述
- 面向?qū)ο蠼鉀Q了面向過程的開發(fā)痛點(diǎn)
- 設(shè)計(jì)原則是基于面向?qū)ο髷U(kuò)展出來的一套思想,用來解決開發(fā)中痛點(diǎn)
- 設(shè)計(jì)模式就是設(shè)計(jì)原則的產(chǎn)物
- 過度設(shè)計(jì)是
設(shè)計(jì)模式的大忌 - 掌握好設(shè)計(jì)原則、設(shè)計(jì)模式才能做出優(yōu)秀的架構(gòu)