国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

代碼質(zhì)量評(píng)價(jià)及設(shè)計(jì)原則

這篇具有很好參考價(jià)值的文章主要介紹了代碼質(zhì)量評(píng)價(jià)及設(shè)計(jì)原則。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

1.評(píng)價(jià)代碼質(zhì)量的標(biāo)準(zhǔn)

1.1?可維護(hù)性

可維護(hù)性強(qiáng)的代碼指的是:

?在不去破壞原有的代碼設(shè)計(jì)以及不引入新的BUG的前提下,能夠快速的修改或者新增代碼.

不易維護(hù)的代碼指的是:

在添加或者修改一些功能邏輯的時(shí)候,存在極大的引入新的BUG的風(fēng)險(xiǎn),并且需要花費(fèi)的時(shí)間也很長(zhǎng).

代碼可維護(hù)性的評(píng)判標(biāo)準(zhǔn)比較模糊, 因?yàn)槭欠褚拙S護(hù)是針對(duì)維護(hù)的人員來(lái)說(shuō)的,不同水平的人對(duì)于同一份代碼的維護(hù)能力是不同的. 所謂 ''難者不會(huì) 會(huì)者不難''. 對(duì)于同樣的系統(tǒng),熟悉它的資深工程師會(huì)覺(jué)得代碼可維護(hù)性還可以,而新人則會(huì)因?yàn)槟芰Σ蛔恪⒘私獠粔蛏钊氲仍蛴X(jué)得代碼的可維護(hù)性不是很好.

1.2可讀性

軟件開(kāi)發(fā)教父,Martin Fowler曾經(jīng)說(shuō)過(guò)一句話: "任何傻瓜都能夠編寫(xiě)計(jì)算機(jī)能理解的代碼,而優(yōu)秀的程序員能夠編寫(xiě)人類能理解的代碼。" 這句話的意思非常容易理解,就是要求我們寫(xiě)出的代碼是易讀的、易理解的,因?yàn)榇a的可讀性會(huì)在很大程度上影響代碼的可維護(hù)行性.

code review ( 代碼審查,一種測(cè)試代碼可讀性的手段 )
?1.檢查代碼風(fēng)格和編程規(guī)范:

代碼是否符合編碼規(guī)范、命名是否達(dá)意、注釋是否詳盡、模塊劃分是否清晰等
2.檢查常規(guī)的 bad smell 和代碼 bug:

是否存在重復(fù)代碼、過(guò)長(zhǎng)函數(shù)、過(guò)大類、過(guò)于親密的兩個(gè) classes等

1.3可擴(kuò)展性

代碼的可擴(kuò)展性表示,我們?cè)诓恍薷幕蛏倭啃薷脑写a的情況下,通過(guò)擴(kuò)展的方式添加新的功能代碼。

可擴(kuò)展性的背后其實(shí)就是: "對(duì)修改關(guān)閉,對(duì)擴(kuò)展開(kāi)放" 這條設(shè)計(jì)原則,后面我們會(huì)詳細(xì)的講解

1.4靈活性

"靈活" 是指在添加新代碼的時(shí)候,已有代碼能夠不受影響,不產(chǎn)生沖突,不出現(xiàn)排斥,在保證自身不遭到破壞的前提下靈活地接納新代碼。

下面的幾個(gè)場(chǎng)景,可以體現(xiàn)代碼的靈活性

1.添加新的功能代碼時(shí),原有代碼已經(jīng)預(yù)留了擴(kuò)展點(diǎn),我們不需要修改 直接在擴(kuò)展點(diǎn)上新增代碼即可.

2.當(dāng)我們想實(shí)現(xiàn)一個(gè)功能模塊時(shí),發(fā)現(xiàn)原有代碼中,已經(jīng)抽象出了很多底層可以復(fù)用的模塊、類等代碼,我們可以直接拿來(lái)使用
3.當(dāng)我們使用某組接口時(shí),這組接口可以應(yīng)對(duì)各種使用場(chǎng)景,滿足不同需求,這個(gè)接口設(shè)計(jì)的十分的靈活易用.

1.5 簡(jiǎn)潔性

我們要遵從KISS ( Keep It Simple Stupid) 原則,代碼要盡可能的簡(jiǎn)單;但是思從深而行從簡(jiǎn),真正的高手能云淡風(fēng)輕地用最簡(jiǎn)單的方法解決最復(fù)雜的問(wèn)題。這也是一個(gè)編程老手跟編程新手的本質(zhì)區(qū)別之一。

代碼的寫(xiě)法應(yīng)當(dāng)使別人理解它所需的時(shí)間最小化.

1.6可復(fù)用性

代碼的可復(fù)用性可以簡(jiǎn)單地理解為,盡量減少重復(fù)代碼的編寫(xiě),復(fù)用已有的代碼.

可復(fù)用性也是一個(gè)非常重要的代碼評(píng)價(jià)標(biāo)準(zhǔn),是很多設(shè)計(jì)原則、思想、模式等所 要達(dá)到的最終效果
可復(fù)用性與DRY(Don't Repeat Yourself) 避免編寫(xiě)重復(fù)的代碼邏輯. 原則關(guān)系緊密,后面我們會(huì)介紹有哪些編程方法可以提高代碼復(fù)用性.

1.7可測(cè)試性

單元測(cè)試在一個(gè)完整的軟件開(kāi)發(fā)流程中是必不可少的、非常重要的一個(gè)環(huán)節(jié)。通常寫(xiě)單元測(cè)試并不難,但有的時(shí)候,有的代碼和功能難以測(cè)試,導(dǎo)致寫(xiě)起測(cè)試來(lái)困難重重。所以寫(xiě)出的代碼具有可測(cè)試性,具有很重要的作用。

代碼可測(cè)試性的好壞,能從側(cè)面上非常準(zhǔn)確地反應(yīng)代碼質(zhì)量的好壞

2.UML圖

統(tǒng)一建模語(yǔ)言(Unified ?Modeling Language,UML)是用來(lái)設(shè)計(jì)軟件的可視化建模語(yǔ)言。它的特點(diǎn)是簡(jiǎn)單、統(tǒng)一、圖形化、能表達(dá)軟件設(shè)計(jì)中的動(dòng)態(tài)與靜態(tài)信息。

UML 從目標(biāo)系統(tǒng)的不同角度出發(fā),定義了用例圖、類圖、對(duì)象圖、狀態(tài)圖、活動(dòng)圖、時(shí)序圖、協(xié)作圖、構(gòu)件圖、部署圖等 9 種圖。

這里我們只介紹類圖.

我們要去研究一個(gè)設(shè)計(jì)模式的時(shí)候,是需要借助UML類圖更加準(zhǔn)確的描述所使用的設(shè)計(jì)模式,和設(shè)計(jì)模式下類與類之間的關(guān)系

2.1類圖概述

類圖(Class diagram)是顯示了模型的靜態(tài)結(jié)構(gòu),特別是模型中存在的類、類的內(nèi)部結(jié)構(gòu)以及它們與其他類的關(guān)系等。類圖不顯示暫時(shí)性的信息。類圖是面向?qū)ο蠼5闹饕M成部分。

2.1.1類圖的作用

在軟件工程中,類圖是一種靜態(tài)的結(jié)構(gòu)圖,描述了系統(tǒng)的類的集合,類的屬性和類之間的關(guān)系,可以簡(jiǎn)化了人們對(duì)系統(tǒng)的理解;
類圖是系統(tǒng)分析和設(shè)計(jì)階段的重要產(chǎn)物,是系統(tǒng)編碼和測(cè)試的重要模型。

2.1.2類圖表示法

UML類圖中具體類、抽象類、接口和包有不同的表示方法。

2.1.3在UML類圖中表示具體類

具體類在類圖中用矩形框表示,矩形框分為三層:第一層是類名字。第二層是類的成員變量;第三層是類的方法。成員變量以及方法前的訪問(wèn)修飾符用符號(hào)來(lái)表示:

“+” 表示 `public`;
“-” 表示 `private`;
“#” 表示 `protected`;
不帶符號(hào)表示 `default`。

代碼質(zhì)量評(píng)價(jià)及設(shè)計(jì)原則,23種設(shè)計(jì)模式,23種設(shè)計(jì)模式

2.1.4在UML類圖中表示抽象類

抽象類在UML類圖中同樣用矩形框表示,但是抽象類的類名以及抽象方法的名字都用斜體字表示,如圖所示。

代碼質(zhì)量評(píng)價(jià)及設(shè)計(jì)原則,23種設(shè)計(jì)模式,23種設(shè)計(jì)模式

2.1.5在UML類圖中表示接口

接口在類圖中也是用矩形框表示,但是與類的表示法不同的是,接口在類圖中的第一層頂端用構(gòu)造型 <<interface>>表示,下面是接口的名字,第二層是方法。

代碼質(zhì)量評(píng)價(jià)及設(shè)計(jì)原則,23種設(shè)計(jì)模式,23種設(shè)計(jì)模式

2.1.6在類圖中表示關(guān)系

類和類、類和接口、接口和接口之間存在一定關(guān)系,UML類圖中一般會(huì)有連線指明它們之間的關(guān)系。

關(guān)系共有六種類型 ,如下圖:

代碼質(zhì)量評(píng)價(jià)及設(shè)計(jì)原則,23種設(shè)計(jì)模式,23種設(shè)計(jì)模式

2.1.6.1實(shí)現(xiàn)關(guān)系(implements)

實(shí)現(xiàn)關(guān)系是接口與實(shí)現(xiàn)類之間的關(guān)系。在這種關(guān)系中,類實(shí)現(xiàn)了接口,類中的操作實(shí)現(xiàn)了接口中所聲明的所有的抽象操作。

在 UML 類圖中,實(shí)現(xiàn)關(guān)系使用帶空心三角箭頭的虛線來(lái)表示,箭頭從實(shí)現(xiàn)類指向接口。

例如,汽車(chē)和船實(shí)現(xiàn)了交通工具,其類圖:

代碼質(zhì)量評(píng)價(jià)及設(shè)計(jì)原則,23種設(shè)計(jì)模式,23種設(shè)計(jì)模式

?

2.1.6.2泛化關(guān)系(extends)

泛化關(guān)系是對(duì)象之間耦合度最大的一種關(guān)系,表示一般與特殊的關(guān)系,是父類與子類之間的關(guān)系,是一種繼承關(guān)系。

在 UML 類圖中,泛化關(guān)系用帶空心三角箭頭的實(shí)線來(lái)表示,箭頭從子類指向父類。在代碼實(shí)現(xiàn)時(shí),使用面向?qū)ο蟮睦^承機(jī)制來(lái)實(shí)現(xiàn)泛化關(guān)系。

例如,Student 類和 Teacher 類都是 Person 類的子類,其類圖如下圖所示:

代碼質(zhì)量評(píng)價(jià)及設(shè)計(jì)原則,23種設(shè)計(jì)模式,23種設(shè)計(jì)模式

?

2.1.6.3關(guān)聯(lián)關(guān)系

關(guān)聯(lián)關(guān)系是對(duì)象之間的一種引用關(guān)系,用于表示一類對(duì)象與另一類對(duì)象之間的聯(lián)系,如老師和學(xué)生、師傅和徒弟、丈夫和妻子等。關(guān)聯(lián)關(guān)系是類與類之間最常用的一種關(guān)系,分為一般關(guān)聯(lián)關(guān)系、聚合關(guān)系和組合關(guān)系。

我們先介紹一般關(guān)聯(lián)關(guān)系, 一般關(guān)聯(lián)關(guān)系又可以分為單向關(guān)聯(lián),雙向關(guān)聯(lián),自關(guān)聯(lián)。

2.1.6.3.1單向關(guān)聯(lián)

在UML類圖中單向關(guān)聯(lián)用一個(gè)帶箭頭的實(shí)線表示。上圖表示每個(gè)顧客都有一個(gè)地址,這通過(guò)讓Customer類持有一個(gè)類型為Address的成員變量類實(shí)現(xiàn)。

代碼質(zhì)量評(píng)價(jià)及設(shè)計(jì)原則,23種設(shè)計(jì)模式,23種設(shè)計(jì)模式

2.1.6.3.2雙向關(guān)聯(lián)?

雙向關(guān)聯(lián)就是雙方各自持有對(duì)方類型的成員變量。

在UML類圖中,雙向關(guān)聯(lián)用一個(gè)不帶箭頭的直線表示。上圖中在Customer類中維護(hù)一個(gè)List<Product>,表示一個(gè)顧客可以購(gòu)買(mǎi)多個(gè)商品;在Product類中維護(hù)一個(gè)Customer類型的成員變量表示這個(gè)產(chǎn)品被哪個(gè)顧客所購(gòu)買(mǎi)。

代碼質(zhì)量評(píng)價(jià)及設(shè)計(jì)原則,23種設(shè)計(jì)模式,23種設(shè)計(jì)模式

2.1.6.3.3自關(guān)聯(lián)?

自關(guān)聯(lián)在UML類圖中用一個(gè)帶有箭頭且指向自身的線表示。上圖的意思就是Node類包含類型為Node的成員變量,也就是“自己包含自己”。

代碼質(zhì)量評(píng)價(jià)及設(shè)計(jì)原則,23種設(shè)計(jì)模式,23種設(shè)計(jì)模式

2.1.6.4聚合關(guān)系

在 UML 類圖中,聚合關(guān)系可以用帶空心菱形的實(shí)線來(lái)表示,菱形指向整體。

聚合關(guān)系是關(guān)聯(lián)關(guān)系的一種,表示一種弱的‘擁有’關(guān)系,體現(xiàn)的是A對(duì)象可以包含B對(duì)象,但是B對(duì)象不是A對(duì)象的一部分

在代碼中: 比如A 類對(duì)象包含 B 類對(duì)象,B 類對(duì)象的生命周期可以不依賴 A 類對(duì)象的生命周期,也就是說(shuō)可以單獨(dú)銷(xiāo)毀 A 類對(duì)象而不影響 B 對(duì)象

public class A{

	private B b;

	public A(B b){
		this.b = b;
	}
}

代碼質(zhì)量評(píng)價(jià)及設(shè)計(jì)原則,23種設(shè)計(jì)模式,23種設(shè)計(jì)模式

2.1.6.5組合關(guān)系

在 UML 類圖中,組合關(guān)系用帶實(shí)心菱形的實(shí)線來(lái)表示,菱形指向整體。

組合關(guān)系是一種強(qiáng)‘擁有’關(guān)系,體現(xiàn)了嚴(yán)格的部分和整體的關(guān)系,部分和整體的聲明周期一樣

在代碼中: 比如A 類對(duì)象包含 B 類對(duì)象,B 類對(duì)象的生命周期依賴A 類對(duì)象的生命周期,B 類對(duì)象不可以單獨(dú)存在

public class A{
  
    private B b;
  
    public A(){
        this.b = new B();
    }
  
}

?代碼質(zhì)量評(píng)價(jià)及設(shè)計(jì)原則,23種設(shè)計(jì)模式,23種設(shè)計(jì)模式

2.1.6.6依賴關(guān)系

依賴關(guān)系是一種使用關(guān)系,它是對(duì)象之間耦合度最弱的一種關(guān)聯(lián)方式,是臨時(shí)性的關(guān)聯(lián)。

在代碼中,某個(gè)類的方法通過(guò)局部變量、方法的參數(shù)或者對(duì)靜態(tài)方法的調(diào)用來(lái)訪問(wèn)另一個(gè)類(被依賴類)中的某些方法來(lái)完成一些職責(zé)。

在 UML 類圖中,依賴關(guān)系使用帶箭頭的虛線來(lái)表示,箭頭從使用類指向被依賴的類。

下圖所示是司機(jī)和汽車(chē)的關(guān)系圖,司機(jī)駕駛汽車(chē):

代碼質(zhì)量評(píng)價(jià)及設(shè)計(jì)原則,23種設(shè)計(jì)模式,23種設(shè)計(jì)模式

?3.六大設(shè)計(jì)原則 (SOLID)

3.1單一職責(zé)原則

單一職責(zé)原則,英文縮寫(xiě)SRP,全稱 Single Responsibility Principle。

在<<架構(gòu)整潔之道>>一書(shū)中 關(guān)于這個(gè)原則的英文描述是這樣的:A class or module should have a single responsibility 。如果我們把它翻譯成中文,那就是:一個(gè)類或者模塊只負(fù)責(zé)完成一個(gè)職責(zé)(或者功能)。

通俗解釋

單一職責(zé)原則的定義描述非常簡(jiǎn)單,也不難理解。一個(gè)類只負(fù)責(zé)完成一個(gè)職責(zé)或者功能

也就是說(shuō)在類的設(shè)計(jì)中 我們不要設(shè)計(jì)大而全的類,而是要設(shè)計(jì)粒度小、功能單一的類.

比如 我們?cè)O(shè)計(jì)一個(gè)類里面既包含了用戶的一些操作,又包含了支付的一些操作,那這個(gè)類的職責(zé)就不夠單一,應(yīng)該將該類進(jìn)行拆分,拆分成多個(gè)功能更加單一的,粒度更細(xì)的類.

3.1.1場(chǎng)景示例

那么該如何判斷一個(gè)類的職責(zé)是否單一 ?

>其實(shí)在軟件設(shè)計(jì)中,要真正用好單一職責(zé)原則并不簡(jiǎn)單,因?yàn)樽裱@一原則最關(guān)鍵的地方在于職責(zé)的劃分,而職責(zé)的劃分是根據(jù)需求定的,同一個(gè)類(接口)的設(shè)計(jì),在不同的需求里面,可能職責(zé)的劃分并不一樣.

我們來(lái)看下面這個(gè)例子:

在一個(gè)社交媒體產(chǎn)品中,我們使用UserInfo去記錄用戶的信息,包括如下的屬性.

代碼質(zhì)量評(píng)價(jià)及設(shè)計(jì)原則,23種設(shè)計(jì)模式,23種設(shè)計(jì)模式

請(qǐng)問(wèn)上面的UserInfo類是否滿足單一職責(zé)原則呢 ?

觀點(diǎn)1: 滿足,因?yàn)橛涗浀亩际歉脩粝嚓P(guān)的信息
觀點(diǎn)2: 不滿足,因?yàn)榈刂沸畔?yīng)該被拆分出來(lái),單獨(dú)放到地址表中保存.

正確答案: 根據(jù)實(shí)際業(yè)務(wù)場(chǎng)景選擇是否拆分

該社交產(chǎn)品的有用戶信息只是用來(lái)展示的,那么這個(gè)類這樣設(shè)計(jì)就沒(méi)有問(wèn)題
假設(shè)后面這個(gè)社交產(chǎn)品又添加了電商模塊, 那就需要將地址信息提取出來(lái),單獨(dú)設(shè)計(jì)一個(gè)類

代碼質(zhì)量評(píng)價(jià)及設(shè)計(jì)原則,23種設(shè)計(jì)模式,23種設(shè)計(jì)模式?

總結(jié): 不同的應(yīng)用場(chǎng)景、不同階段的需求背景下,對(duì)同一個(gè)類的職責(zé)是否單一的判定,可能都是不一樣的,最好的方式就是,我們可以先寫(xiě)一個(gè)粗粒度的類,滿足業(yè)務(wù)需求。隨著業(yè)務(wù)的發(fā)展,如果粗粒度的類越來(lái)越龐大,代碼越來(lái)越多,這個(gè)時(shí)候,我們就可以將這個(gè)粗粒度的類,拆分成幾個(gè)更細(xì)粒度的類。這就是所謂的持續(xù)重構(gòu)

3.1.2如何判斷一個(gè)類的職責(zé)是否單一?

這里沒(méi)有一個(gè)具體的金科玉律,但從實(shí)際代碼開(kāi)發(fā)經(jīng)驗(yàn)上,有一些可執(zhí)行性的側(cè)面判斷指標(biāo),可供參考:

類中的代碼行數(shù)、函數(shù)、或者屬性過(guò)多;
類依賴的其他類過(guò)多
私有方法過(guò)多
類中大量的方法都是集中操作類中的幾個(gè)屬性

?3.2開(kāi)閉原則

開(kāi)閉原則規(guī)定軟件中的對(duì)象、類、模塊和函數(shù)對(duì)擴(kuò)展應(yīng)該是開(kāi)放的,但對(duì)于修改是封閉的。這意味著應(yīng)該用抽象定義結(jié)構(gòu),用具體實(shí)現(xiàn)擴(kuò)展細(xì)節(jié),以此確保軟件系統(tǒng)開(kāi)發(fā)和維護(hù)過(guò)程的可靠性。

通俗解釋

定義:對(duì)擴(kuò)展開(kāi)放,對(duì)修改關(guān)閉

對(duì)擴(kuò)展開(kāi)放和對(duì)修改關(guān)閉表示當(dāng)一個(gè)類或一個(gè)方法有新需求或者需求發(fā)生改變時(shí)應(yīng)該采用擴(kuò)展的方式而不應(yīng)該采用修改原有邏輯的方式來(lái)實(shí)現(xiàn)。因?yàn)閿U(kuò)展了新的邏輯如果有問(wèn)題只會(huì)影響新的業(yè)務(wù),不會(huì)影響老業(yè)務(wù);而如果采用修改的方式,很有可能就會(huì)影響到老業(yè)務(wù)受影響。
開(kāi)閉原則是所有設(shè)計(jì)模式的最核心目標(biāo),也是最難實(shí)現(xiàn)的目標(biāo),但是所有的軟件設(shè)計(jì)模式都應(yīng)該以開(kāi)閉原則當(dāng)作標(biāo)準(zhǔn),才能使軟件更加的穩(wěn)定和健壯。

3.2.1優(yōu)點(diǎn):

1. 新老邏輯解耦,需求發(fā)生改變不會(huì)影響老業(yè)務(wù)的邏輯
2. 改動(dòng)成本最小,只需要追加新邏輯,不需要改的老邏輯
3. 提供代碼的穩(wěn)定性和可擴(kuò)展性

3.2.2場(chǎng)景示例

系統(tǒng)A與系統(tǒng)B之間進(jìn)行數(shù)據(jù)傳輸使用的是427版本的協(xié)議,一年以后對(duì)427版本的協(xié)議進(jìn)行了修正。

設(shè)計(jì)時(shí)應(yīng)該考慮的數(shù)據(jù)傳輸協(xié)議的可變性,抽象出具有報(bào)文解譯、編制、校驗(yàn)等所有版本協(xié)議使用的通用方法,調(diào)用方針對(duì)接口進(jìn)行編程即可,如上述示例設(shè)計(jì)類圖如下

代碼質(zhì)量評(píng)價(jià)及設(shè)計(jì)原則,23種設(shè)計(jì)模式,23種設(shè)計(jì)模式

?調(diào)用方依賴于報(bào)文接口,報(bào)文接口是穩(wěn)定的,而不針對(duì)具體的427協(xié)議或427修正協(xié)議。利用接口多態(tài)技術(shù),實(shí)現(xiàn)了開(kāi)閉原則

3.2.3頂層設(shè)計(jì)思維

抽象意識(shí)
封裝意識(shí)
擴(kuò)展意識(shí)

在寫(xiě)代碼的時(shí)候后,我們要多花點(diǎn)時(shí)間往前多思考一下,這段代碼未來(lái)可能有哪些需求變 更、如何設(shè)計(jì)代碼結(jié)構(gòu),事先留好擴(kuò)展點(diǎn),以便在未來(lái)需求變更的時(shí)候,不需要改動(dòng)代碼整 體結(jié)構(gòu)、做到最小代碼改動(dòng)的情況下,新的代碼能夠很靈活地插入到擴(kuò)展點(diǎn)上,做到“對(duì)擴(kuò) 展開(kāi)放、對(duì)修改關(guān)閉”。

3.3里氏替換原則

如果S是T的子類型,對(duì)于S類型的任意對(duì)象,如果將他們看作是T類型的對(duì)象,則對(duì)象的行為也理應(yīng)與期望的行為一致。

子類對(duì)象能夠替換程序中父類對(duì)象出現(xiàn)的任何地方,并且保證原來(lái)程序的邏輯行為不變及正確性不被破壞。

3.3.1?通俗解釋

什么是替換 ?

替換的前提是面向?qū)ο笳Z(yǔ)言所支持的多態(tài)特性,同一個(gè)行為具有多個(gè)不同表現(xiàn)形式或形態(tài)的能力。

以JDK的集合框架為例,List接口的定義為有序集合,List接口有多個(gè)派生類,比如大家耳熟能詳?shù)?ArrayList, LinkedList。那當(dāng)某個(gè)方法參數(shù)或變量是 List接口類型時(shí),既可以是 ArrayList的實(shí)現(xiàn), 也可以是 LinkedList的實(shí)現(xiàn),這就是替換。

什么是與期望行為一致的替換?

在不了解派生類的情況下,僅通過(guò)接口或基類的方法,即可清楚的知道方法的行為,而不管哪種派生類的實(shí)現(xiàn),都與接口或基類方法的期望行為一致。

不需要關(guān)心是哪個(gè)類對(duì)接口進(jìn)行了實(shí)現(xiàn),因?yàn)椴还艿讓尤绾螌?shí)現(xiàn),最終的結(jié)果都會(huì)符合接口中關(guān)于方法的描述(也就是與接口中方法的期望行為一致).
或者說(shuō)接口或基類的方法是一種契約,使用方按照這個(gè)契約來(lái)使用,派生類也按照這個(gè)契約來(lái)實(shí)現(xiàn)。這就是與期望行為一致的替換。

3.3.2場(chǎng)景示例

里氏替換原則要求我們?cè)诰幋a時(shí)使用基類或接口去定義對(duì)象變量,使用時(shí)可以由具體實(shí)現(xiàn)對(duì)象進(jìn)行賦值,實(shí)現(xiàn)變化的多樣性,完成代碼對(duì)修改的封閉,擴(kuò)展的開(kāi)放。

比如在一個(gè)商城項(xiàng)目中, 定義結(jié)算接口Istrategy,該接口有三個(gè)具體實(shí)現(xiàn)類,分別為 PromotionalStrategy (滿減活動(dòng),兩百以上百八折)、RebateStrategy (打折活動(dòng))、 ReduceStrategy(返現(xiàn)活動(dòng))

代碼質(zhì)量評(píng)價(jià)及設(shè)計(jì)原則,23種設(shè)計(jì)模式,23種設(shè)計(jì)模式

public interface Istrategy {
    public double realPrice(double consumePrice);
}

public class PromotionalStrategy implements Istrategy {
    public double realPrice(double consumePrice) {
        if (consumePrice > 200) {
            return 200 + (consumePrice - 200) * 0.8;
        } else {
            return consumePrice;
        }
    }
}
public class RebateStrategy implements Istrategy {
    private final double rate;
    public RebateStrategy() {
        this.rate = 0.8;
    }
    public double realPrice(double consumePrice) {
        return consumePrice * this.rate;
    }
}
public class ReduceStrategy implements Istrategy {
    public double realPrice(double consumePrice) {
        if (consumePrice >= 1000) {
            return consumePrice - 200;
        } else {
            return consumePrice;
        }
    }
}

?調(diào)用方為Context,在此類中使用接口定義了一個(gè)對(duì)象。

代碼質(zhì)量評(píng)價(jià)及設(shè)計(jì)原則,23種設(shè)計(jì)模式,23種設(shè)計(jì)模式

public class Context {
    //使用基類定義對(duì)象變量
    private Istrategy strategy;
    // 注入當(dāng)前活動(dòng)使用的具體對(duì)象
    public void setStrategy(Istrategy strategy) {
        this.strategy = strategy;
    }
    // 計(jì)算并返回費(fèi)用
    public double cul(double consumePrice) {
        // 使用具體商品促銷(xiāo)策略獲得實(shí)際消費(fèi)金額
        double realPrice = this.strategy.realPrice(consumePrice);
        // 格式化保留小數(shù)點(diǎn)后1位,即:精確到角
        BigDecimal bd = new BigDecimal(realPrice);
        bd = bd.setScale(1, BigDecimal.ROUND_DOWN);
        return bd.doubleValue();
    }
}

Context 中代碼使用接口定義對(duì)象變量,這個(gè)對(duì)象變量可以是實(shí)現(xiàn)了lStrategy接口的PromotionalStrategy、RebateStrategy 、 ReduceStrategy任意一個(gè)。

里氏代換原則與多態(tài)的區(qū)別 ?

雖然從定義描述和代碼實(shí)現(xiàn)上 來(lái)看,多態(tài)和里式替換有點(diǎn)類似,但它們關(guān)注的角度是不一樣的。多態(tài)是面向?qū)ο缶幊痰囊?大特性,也是面向?qū)ο缶幊陶Z(yǔ)言的一種語(yǔ)法。它是一種代碼實(shí)現(xiàn)的思路。而里式替換是一種 設(shè)計(jì)原則,用來(lái)指導(dǎo)繼承關(guān)系中子類該如何設(shè)計(jì),子類的設(shè)計(jì)要保證在替換父類的時(shí)候,不 改變?cè)谐绦虻倪壿嫾安黄茐脑谐绦虻恼_性。

里氏替換原則和依賴倒置原則,構(gòu)成了面向接口編程的基礎(chǔ),正因?yàn)槔锸咸鎿Q原則,才使得程序呈現(xiàn)多樣性。

3.4接口隔離原則

客戶端不應(yīng)該被迫依賴于它不使用的方法.

該原則還有另外一個(gè)定義:一個(gè)類對(duì)另一個(gè)類的依賴應(yīng)該建立在最小的接口上

3.4.1通俗解釋

上面兩個(gè)定義的含義用一句話概括就是:要為各個(gè)類建立它們需要的專用接口,而不要試圖去建立一個(gè)很龐大的接口供所有依賴它的類去調(diào)用。

接口隔離原則與單一職責(zé)原則的區(qū)別

接口隔離原則和單一職責(zé)都是為了提高類的內(nèi)聚性、降低它們之間的耦合性,體現(xiàn)了封裝的思想,但兩者是不同的:

單一職責(zé)原則注重的是職責(zé),而接口隔離原則注重的是對(duì)接口依賴的隔離。
單一職責(zé)原則主要是約束類,它針對(duì)的是程序中的實(shí)現(xiàn)和細(xì)節(jié);接口隔離原則主要約束接口,主要針對(duì)抽象和程序整體框架的構(gòu)建。

3.4.2?場(chǎng)景示例

微服務(wù)用戶系統(tǒng)提供了一組跟用戶相關(guān)的 API 給其他系統(tǒng) 使用,比如:注冊(cè)、登錄、獲取用戶信息等。

代碼質(zhì)量評(píng)價(jià)及設(shè)計(jì)原則,23種設(shè)計(jì)模式,23種設(shè)計(jì)模式

public interface UserService {
    boolean register(String cellphone, String password);
    boolean login(String cellphone, String password);
    UserInfo getUserInfoById(long id);
    UserInfo getUserInfoByCellphone(String cellphone);
}

public class UserServiceImpl implements UserService {
		//...
}

需求: 后臺(tái)管理系統(tǒng)要實(shí)現(xiàn)刪除用戶的功能,希望用戶系統(tǒng)提供一個(gè)刪除用戶的接口,應(yīng)該如何設(shè)計(jì)這個(gè)接口(假設(shè)這里我們不去考慮使用鑒權(quán)框架).

方案1: 直接在UserService接口中添加一個(gè)刪除用戶的接口

?這個(gè)方法可以解決問(wèn)題,但是也隱藏了一些安全隱患。刪除用戶是一個(gè)非常慎重的操作,我們只希望通過(guò)后臺(tái)管理系統(tǒng)來(lái)執(zhí)行,所以這個(gè)接口只限于給后臺(tái)管理系統(tǒng)使用。如果我們把它放到 UserService 中,那所有使用到 UserService的系統(tǒng),都可以調(diào)用這個(gè)接口。不加限制地被其他業(yè)務(wù)系統(tǒng)調(diào)用,就有可能導(dǎo)致誤刪用戶。
方案2: 遵照接口隔離原則,為依賴接口的類定制服務(wù)。只提供調(diào)用者需要的方法,屏蔽不需要的方法。
將刪除接口單獨(dú)放到另外 一個(gè)接口 RestrictedUserService 中, 然后將 RestrictedUserService 只打包提供給后臺(tái)管理系統(tǒng)來(lái)使用。

代碼質(zhì)量評(píng)價(jià)及設(shè)計(jì)原則,23種設(shè)計(jì)模式,23種設(shè)計(jì)模式

public interface UserService {
    boolean register(String cellphone, String password);
    boolean login(String cellphone, String password);
    UserInfo getUserInfoById(long id);
    UserInfo getUserInfoByCellphone(String cellphone);
}

public interface RestrictedUserService {
 boolean deleteUserByCellphone(String cellphone);
 boolean deleteUserById(long id);
}

public class UserServiceImpl implements UserService, RestrictedUserService {
		//...
}

遵循接口隔離原則的優(yōu)勢(shì)

1. 將臃腫龐大的接口分解為多個(gè)粒度小的接口,可以預(yù)防外來(lái)變更的擴(kuò)散,提高系統(tǒng)的靈活性和可維護(hù)性。
2. 使用多個(gè)專門(mén)的接口還能夠體現(xiàn)對(duì)象的層次,因?yàn)榭梢酝ㄟ^(guò)接口的繼承,實(shí)現(xiàn)對(duì)總接口的定義。
3. 能減少項(xiàng)目工程中的代碼冗余。過(guò)大的大接口里面通常放置許多不用的方法,當(dāng)實(shí)現(xiàn)這個(gè)接口的時(shí)候,被迫設(shè)計(jì)冗余的代碼.

3.5依賴倒置原則

依賴倒置原則(Dependence Inversion Principle,DIP)是指在設(shè)計(jì)代碼架構(gòu)時(shí),高層模塊不應(yīng)該依賴于底層模塊,二者都應(yīng)該依賴于抽象。抽象不應(yīng)該依賴于細(xì)節(jié),細(xì)節(jié)應(yīng)該依賴于抽象。

3.5.1通俗解釋

依賴倒置原則是實(shí)現(xiàn)開(kāi)閉原則的重要途徑之一,它降低了客戶與實(shí)現(xiàn)模塊之間的耦合。

1. 高層級(jí)的模塊應(yīng)該依賴的是低層級(jí)的模塊的行為的抽象,取決于具體編程語(yǔ)言,可以是抽象類或者接口等技術(shù);
2. 第2句話其實(shí)很簡(jiǎn)單,只有一個(gè)意思:只要依賴了實(shí)現(xiàn),就是耦合了代碼,所以我們需要始終依賴的是抽象,而不是實(shí)現(xiàn)。

傳統(tǒng)的自定向下的設(shè)計(jì)

?傳統(tǒng)設(shè)計(jì)方式采用自頂向下的原則, 逐級(jí)依賴,中層模塊和高層模塊的耦合度很高,如果需要修改其中的一個(gè)模塊,則可能會(huì)導(dǎo)致其它很多模塊也需要修改,牽一發(fā)動(dòng)全身,不易于維護(hù)。
?不使用依賴反轉(zhuǎn)的系統(tǒng)構(gòu)架,控制流和依賴關(guān)系流的依賴箭頭是一個(gè)方向的,由高層指向底層,也就是高層依賴底層

代碼質(zhì)量評(píng)價(jià)及設(shè)計(jì)原則,23種設(shè)計(jì)模式,23種設(shè)計(jì)模式

依賴倒置原則

依賴倒置原則的好處:
減少類間的耦合性,提高系統(tǒng)的穩(wěn)定性 . (根據(jù)類與類之間的耦合度從弱到強(qiáng)排列:依賴關(guān)系、關(guān)聯(lián)關(guān)系、聚合關(guān)系、組合關(guān)系、泛化關(guān)系和實(shí)現(xiàn)關(guān)系 )
降低并行開(kāi)發(fā)引起的風(fēng)險(xiǎn) (兩個(gè)類之間有依賴關(guān)系,只要制定出兩者之間的接口(或抽象類)就可以獨(dú)立開(kāi)發(fā)了)?提高代碼的可讀性和可維護(hù)性

代碼質(zhì)量評(píng)價(jià)及設(shè)計(jì)原則,23種設(shè)計(jì)模式,23種設(shè)計(jì)模式

3.5.2場(chǎng)景示例

假設(shè)我們現(xiàn)在要組裝一臺(tái)電腦,需要的配件有 cpu,硬盤(pán),內(nèi)存條。只有這些配置都有了,計(jì)算機(jī)才能正常的運(yùn)行。選擇cpu有很多選擇,如Intel,AMD等,硬盤(pán)可以選擇希捷,西數(shù)等,內(nèi)存條可以選擇金士頓,海盜船等

代碼質(zhì)量評(píng)價(jià)及設(shè)計(jì)原則,23種設(shè)計(jì)模式,23種設(shè)計(jì)模式

希捷硬盤(pán)類(XiJieHardDisk):

public class XiJieHardDisk implements HardDisk {

    public void save(String data) {
        System.out.println("使用希捷硬盤(pán)存儲(chǔ)數(shù)據(jù)" + data);
    }

    public String get() {
        System.out.println("使用希捷希捷硬盤(pán)取數(shù)據(jù)");
        return "數(shù)據(jù)";
    }
}

Intel處理器(IntelCpu):

public class IntelCpu implements Cpu {

    public void run() {
        System.out.println("使用Intel處理器");
    }
}

金士頓內(nèi)存條(KingstonMemory):

public class KingstonMemory implements Memory {

? ? public void save() {
? ? ? ? System.out.println("使用金士頓作為內(nèi)存條");
? ? }
}

?電腦(Computer):

public class Computer {

    private XiJieHardDisk hardDisk;
    private IntelCpu cpu;
    private KingstonMemory memory;

    public IntelCpu getCpu() {
        return cpu;
    }

    public void setCpu(IntelCpu cpu) {
        this.cpu = cpu;
    }

    public KingstonMemory getMemory() {
        return memory;
    }

    public void setMemory(KingstonMemory memory) {
        this.memory = memory;
    }

    public XiJieHardDisk getHardDisk() {
        return hardDisk;
    }

    public void setHardDisk(XiJieHardDisk hardDisk) {
        this.hardDisk = hardDisk;
    }

    public void run() {
        System.out.println("計(jì)算機(jī)工作");
        cpu.run();
        memory.save();
        String data = hardDisk.get();
        System.out.println("從硬盤(pán)中獲取的數(shù)據(jù)為:" + data);
    }
}

測(cè)試類用來(lái)組裝電腦。

public class TestComputer {
? ? public static void main(String[] args) {
? ? ? ? Computer computer = new Computer();
? ? ? ? computer.setHardDisk(new XiJieHardDisk());
? ? ? ? computer.setCpu(new IntelCpu());
? ? ? ? computer.setMemory(new KingstonMemory());

? ? ? ? computer.run();
? ? }
}

上面代碼可以看到已經(jīng)組裝了一臺(tái)電腦,但是似乎組裝的電腦的cpu只能是Intel的,內(nèi)存條只能是金士頓的,硬盤(pán)只能是希捷的,這對(duì)用戶肯定是不友好的,用戶有了機(jī)箱肯定是想按照自己的喜好,選擇自己喜歡的配件。

根據(jù)依賴倒轉(zhuǎn)原則進(jìn)行改進(jìn):

代碼我們需要修改Computer類,讓Computer類依賴抽象(各個(gè)配件的接口),而不是依賴于各個(gè)組件具體的實(shí)現(xiàn)類。

類圖如下:

?

代碼質(zhì)量評(píng)價(jià)及設(shè)計(jì)原則,23種設(shè)計(jì)模式,23種設(shè)計(jì)模式?

電腦(Computer):

public class Computer {

    private HardDisk hardDisk;
    private Cpu cpu;
    private Memory memory;

    //getter/setter......
  
    public void run() {
        System.out.println("計(jì)算機(jī)工作");
    }
}

3.5.3關(guān)于依賴倒置、依賴注入、控制反轉(zhuǎn)這三者之間的區(qū)別與聯(lián)系

1 ) ?依賴倒置原則

依賴倒置是一種通用的軟件設(shè)計(jì)原則, 主要用來(lái)指導(dǎo)框架層面的設(shè)計(jì)。

高層模塊不依賴低層模塊,它們共同依賴同一個(gè)抽象。抽象不要依賴具體實(shí)現(xiàn)細(xì)節(jié),具體實(shí)現(xiàn)細(xì)節(jié)依賴抽象。

2 ) 控制反轉(zhuǎn)

控制反轉(zhuǎn)與依賴倒置有一些相似, 它也是一種框架設(shè)計(jì)常用的模式,但并不是具體的方法。

?“控制”指的是對(duì)程序執(zhí)行流程的控制,而“反轉(zhuǎn)”指的是在沒(méi)有使用框架之前,程序員自己控制整個(gè)程序的執(zhí)行。在使用框架之后,整個(gè)程序的執(zhí)行流程通過(guò)框架來(lái)控制。流程的控制權(quán)從程序員“反轉(zhuǎn)”給了框架。
Spring框架,核心模塊IoC容器,就是通過(guò)控制反轉(zhuǎn)這一種思想進(jìn)行設(shè)計(jì)的

3 ) 依賴注入

依賴注入是實(shí)現(xiàn)控制反轉(zhuǎn)的一個(gè)手段,它是一種具體的編碼技巧。

們不通過(guò) new 的方式在類內(nèi)部創(chuàng)建依賴的對(duì)象,而是將依賴的對(duì)象在外部創(chuàng)建好之后,通過(guò)構(gòu)造函數(shù)等方式傳遞(或注入)進(jìn)來(lái), 給類來(lái)使用。
依賴注入真正實(shí)現(xiàn)了面向接口編程的愿景,可以很方便地替換同一接口的不同實(shí)現(xiàn),而不會(huì)影響到依賴這個(gè)接口的客戶端。

3.6迪米特法則

迪米特法則(LoD:Law of Demeter)又叫最少知識(shí)原則(LKP:Least Knowledge Principle ),指的是一個(gè)類/模塊對(duì)其他的類/模塊有越少的了解越好。簡(jiǎn)言之:talk only to your immediate friends(只跟你最親密的朋友交談),不跟陌生人說(shuō)話。

3.6.1通俗解釋

大部分設(shè)計(jì)原則和思想都非常抽象,有各種各樣的解讀,要想靈活地應(yīng)用到 實(shí)際的開(kāi)發(fā)中,需要有實(shí)戰(zhàn)經(jīng)驗(yàn)的積累。迪米特法則也不例外。

簡(jiǎn)單來(lái)說(shuō)迪米特法則想要表達(dá)的思想就是: ?不該有直接依賴關(guān)系的類之間,不要有依賴;有依賴關(guān)系的類之間,盡量只依賴必要的接口。

如果兩個(gè)軟件實(shí)體無(wú)須直接通信,那么就不應(yīng)當(dāng)發(fā)生直接的相互調(diào)用,可以通過(guò)第三方轉(zhuǎn)發(fā)該調(diào)用。其目的是降低類之間的耦合度,提高模塊的相對(duì)獨(dú)立性。

3.6.2場(chǎng)景示例

我們一起來(lái)看下面這個(gè)例子:

明星由于全身心投入藝術(shù),所以許多日常事務(wù)由經(jīng)紀(jì)人負(fù)責(zé)處理,如和粉絲的見(jiàn)面會(huì),和媒體公司的業(yè)務(wù)洽淡等。這里的經(jīng)紀(jì)人是明星的朋友,而粉絲和媒體公司是陌生人,所以適合使用迪米特法則。

代碼質(zhì)量評(píng)價(jià)及設(shè)計(jì)原則,23種設(shè)計(jì)模式,23種設(shè)計(jì)模式

迪米特法則的獨(dú)特之處在于它簡(jiǎn)潔而準(zhǔn)確的定義,它允許在編寫(xiě)代碼時(shí)直接應(yīng)用,幾乎自動(dòng)地應(yīng)用了適當(dāng)?shù)姆庋b、高內(nèi)聚和低耦合。

但是,過(guò)度使用迪米特法則會(huì)使系統(tǒng)產(chǎn)生大量的中介類,從而增加系統(tǒng)的復(fù)雜性,使模塊之間的通信效率降低。所以,在釆用迪米特法則時(shí)需要反復(fù)權(quán)衡,確保高內(nèi)聚和低耦合的同時(shí),保證系統(tǒng)的結(jié)構(gòu)清晰。

明星類(Star)

public class Star {
    private String name;

    public Star(String name) {
        this.name=name;
    }

    public String getName() {
        return name;
    }
}

粉絲類(Fans)

public class Fans {
    private String name;

    public Fans(String name) {
        this.name=name;
    }

    public String getName() {
        return name;
    }
}

媒體公司類(Company)

public class Company {
    private String name;

    public Company(String name) {
        this.name=name;
    }

    public String getName() {
        return name;
    }
}

經(jīng)紀(jì)人類(Agent)

public class Agent {
    private Star star;
    private Fans fans;
    private Company company;

    public void setStar(Star star) {
        this.star = star;
    }

    public void setFans(Fans fans) {
        this.fans = fans;
    }

    public void setCompany(Company company) {
        this.company = company;
    }

    public void meeting() {
        System.out.println(fans.getName() + "與明星" + star.getName() + "見(jiàn)面了。");
    }

    public void business() {
        System.out.println(company.getName() + "與明星" + star.getName() + "洽淡業(yè)務(wù)。");
    }
}

4.設(shè)計(jì)原則總結(jié)

我們之前給的大家介紹了評(píng)判代碼質(zhì)量的標(biāo)準(zhǔn),比如可讀性、可復(fù)用性、可擴(kuò)展性等等,這是從代碼的整體質(zhì)量的角度來(lái)評(píng)判.

而設(shè)計(jì)原則就是我們要使用到的更加具體的對(duì)于代碼進(jìn)行評(píng)判的標(biāo)準(zhǔn),比如, 我們說(shuō)這段代碼的可擴(kuò)展性比較差,主要原因是違背了開(kāi)閉原則。

我們所學(xué)習(xí)的SOLID 原則它包含了:

1. 單一職責(zé)原則(SRP)
2. 開(kāi)閉原則(OCP)
3. 里氏替換原則(LSP)
4. 接口隔離原則(ISP)
5. 依賴倒置原則(DIP)
6. 迪米特法則 (LKP)

4.1重點(diǎn)關(guān)注三個(gè)常用的原則:

1 ) 單一職責(zé)原則

單一職責(zé)原則是類職責(zé)劃分的重要參考依據(jù),是保證代碼”高內(nèi)聚“的有效手段,是我們?cè)谶M(jìn)行面向?qū)ο笤O(shè)計(jì)時(shí)的主要指導(dǎo)原則。

單一職責(zé)原則的難點(diǎn)在于,對(duì)代碼職責(zé)是否足夠單一的判定。這要根據(jù)具體的場(chǎng)景來(lái)具體分析。同一個(gè)類的設(shè)計(jì),在不同的場(chǎng)景下,對(duì)職責(zé)是否單一的判定,可能是不同的。

2 ) 開(kāi)閉原則

開(kāi)閉原則是保證代碼可擴(kuò)展性的重要指導(dǎo)原則,是對(duì)代碼擴(kuò)展性的具體解讀。很多設(shè)計(jì)模式誕生的初衷都是為了提高代碼的擴(kuò)展性,都是以滿足開(kāi)閉原則為設(shè)計(jì)目的的。

開(kāi)閉原則是所有設(shè)計(jì)模式的最核心目標(biāo),也是最難實(shí)現(xiàn)的目標(biāo),但是所有的軟件設(shè)計(jì)模式都應(yīng)該以開(kāi)閉原則當(dāng)作標(biāo)準(zhǔn),才能使軟件更加的穩(wěn)定和健壯。

3 ) 依賴倒置原則

依賴倒置原則主要用來(lái)指導(dǎo)框架層面的設(shè)計(jì)。高層模塊不依賴低層模塊,它們共同依賴同一個(gè)抽象。

依賴倒置原則其實(shí)也是實(shí)現(xiàn)開(kāi)閉原則的重要途徑之一,它降低了類之間的耦合,提高了系統(tǒng)的穩(wěn)定性和可維護(hù)性,同時(shí)這樣的代碼一般更易讀,且便于傳承。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-772414.html

到了這里,關(guān)于代碼質(zhì)量評(píng)價(jià)及設(shè)計(jì)原則的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包