如何學(xué)好設(shè)計(jì),做好架構(gòu)? 核心思想才是關(guān)鍵

前言

不久前我寫了兩篇關(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ì)違背了迪米特法則,畢竟外部只需要savespend功能,將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)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容