一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

設(shè)計(jì)模式-七大軟件設(shè)計(jì)原則

 悅光陰 2021-06-03

設(shè)計(jì)模式

參考資料

圖解設(shè)計(jì)模式

大話設(shè)計(jì)模式

設(shè)計(jì)模式之禪

github我見過最好的設(shè)計(jì)模式

http://c./view/1326.html

基本原則

開閉原則

在設(shè)計(jì)的時(shí)候盡可能的考慮,需求的變化,新需求來了盡可能少的改動(dòng)代碼,擁抱變化

定義:指的是軟件中一個(gè)實(shí)體,如類、模塊和函數(shù)應(yīng)該對(duì)擴(kuò)展開放,對(duì)修改關(guān)閉。

面向抽象編程

開閉是對(duì)擴(kuò)展和修改的約束

強(qiáng)調(diào):用抽象構(gòu)建框架,用實(shí)現(xiàn)擴(kuò)展細(xì)節(jié)。

優(yōu)點(diǎn):提高軟件系統(tǒng)的可復(fù)用性及可維護(hù)性

  • 面向?qū)ο笞罨A(chǔ)的設(shè)計(jì)原則
  • 指導(dǎo)我們構(gòu)建穩(wěn)定的系統(tǒng)
    • 代碼不是一次性的,更多時(shí)間在維護(hù)
    • 大多是代碼版本的更新迭代
    • 我們最好對(duì)已有的源碼很少修改
      • 一般都是新增擴(kuò)展,類來修改
      • 能夠降低風(fēng)險(xiǎn)

關(guān)于變化

  • 邏輯變化
    • 比如說算法從a*b*c變化成a*b+c其實(shí)是可以直接修改的,前提是所有依賴或者關(guān)聯(lián)類都按照相同的邏輯來處理
  • 子模塊變化
    • 子模塊變化可能直接引起整體也就是高層的變化
  • 可見視圖變化
    • 如果說需求上多了一些原有邏輯不存在的,可能這種變化是恐怖的,需要我們靈活的設(shè)計(jì)

例子

  • 彈性工作時(shí)間,時(shí)間是固定的,上下班是可變的

頂層接口

接口是規(guī)范,抽象是實(shí)現(xiàn)

image-20201228195512054

通過繼承來解決

價(jià)格的含義已經(jīng)變化了,所以不能夠子類直接繼承getPrice(),因?yàn)楫?dāng)前已經(jīng)是折扣價(jià)格了,可能需要價(jià)格和折扣價(jià)格

問題

為什么要遵循開閉原則,從軟件工程角度怎么理解這點(diǎn)。

  • 開閉原則對(duì)擴(kuò)展開放對(duì)修改關(guān)閉,程序和需求一定是不斷修改的,我們需要把共性和基礎(chǔ)的東西抽出來,把常常修改的東西讓他能夠擴(kuò)展出去,這樣我們程序后期維護(hù)的風(fēng)險(xiǎn)就會(huì)小很多

為什么重要

  • 對(duì)于測試的影響
    • 一處變更可能導(dǎo)致原有測試用例都不管用了
  • 提高復(fù)用性
    • 高內(nèi)聚,低耦合
  • 提高可維護(hù)性
  • 面向?qū)ο箝_發(fā)的要求

如何使用

  • 抽象約束
  • 參數(shù)抽到配置中
    • 例如sql的連接信息
    • 國際化信息
  • 指定項(xiàng)目章程
    • 約定項(xiàng)目中Bean都是用自動(dòng)注入,通過注解來做裝配
    • 團(tuán)隊(duì)成員達(dá)成一致
    • 公共類走統(tǒng)一的入口,大家都是用統(tǒng)一的公共類
  • 封裝變化
  • 提前預(yù)知變化

依賴倒置原則

定義

高層模塊不應(yīng)該依賴低層模塊,二者都應(yīng)該依賴其抽象。抽象不應(yīng)該依賴細(xì)節(jié),細(xì)節(jié)應(yīng)該依賴抽象。

說白了就是針對(duì)接口編程,不要針對(duì)實(shí)現(xiàn)編程

什么是倒置

  • 不可分割的原子邏輯是底層模塊,原子邏輯在組裝就是高層模塊
  • 抽象就是接口或者抽象類
    • 都不能被實(shí)例化的
  • 細(xì)節(jié)
    • 細(xì)節(jié)就是具體實(shí)現(xiàn)類

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

  • 通過依賴倒置,能夠減少類和類之間的耦合性,提高系統(tǒng)的穩(wěn)定性,提高代碼的可讀性和穩(wěn)定性。降低修改程序的風(fēng)險(xiǎn)

例子

image-20201228201249393

public class DipTest {

  public static void main(String[] args) {
    //=====  V1  ========
    //        Tom tom = new Tom();
    //        tom.studyJavaCourse();
    //        tom.studyPythonCourse();
    //        tom.studyAICourse();


    //=====  V2  ========
    //        Tom tom = new Tom();
    //        tom.study(new JavaCourse());
    //        tom.study(new PythonCourse());


    //=====  V3  ========
    //        Tom tom = new Tom(new JavaCourse());
    //        tom.study();


    //=====  V4  ========
    Tom tom = new Tom();
    tom.setiCourse(new JavaCourse());
    tom.study();
  }
}

重點(diǎn)

  • 先頂層后細(xì)節(jié)
  • 自頂向下來思考全局不要一開始沉浸于細(xì)節(jié)
  • 高層不依賴于低層,關(guān)系應(yīng)該用抽象來維護(hù)
  • 針對(duì)接口編程不要針對(duì)實(shí)現(xiàn)編程

以抽象為基準(zhǔn)比以細(xì)節(jié)為基準(zhǔn)搭建起來的架構(gòu)要穩(wěn)定得多,因此大家在拿到需求之后, 要面向接口編程,先頂層再細(xì)節(jié)來設(shè)計(jì)代碼結(jié)構(gòu)。

問題

為什么要依賴抽象,抽象表示我還可以擴(kuò)展還沒有具體實(shí)現(xiàn),按照自己的話來解釋一遍

  • 一般軟件中抽象分成兩種,接口和抽象類,接口是規(guī)范,抽象是模板,我們通過抽象的方式,也就是使用規(guī)范和模板這樣我們能夠使得上層,也就是調(diào)用層能夠復(fù)用邏輯,而我們底層是能夠快速更改實(shí)現(xiàn)的,例如Spring的依賴注入,Dubbo的SPI,SpringBoot的SPI都如此

依賴的常見寫法

  • 構(gòu)造傳遞依賴對(duì)象
  • setter方法傳遞依賴對(duì)象
  • 接口聲明傳遞對(duì)象

最佳實(shí)踐

  • 每個(gè)類盡量都有接口或抽象類,或者抽象類和接口兩者都具備這是依賴倒置的基本要求,接口和抽象類都是屬于抽象的,有了抽 象才可能依賴倒置。
  • 變量的表面類型盡量是接口或者是抽象類
    • 很多書上說變量的類型一定要是接口或者是抽象類,這個(gè)有點(diǎn)絕對(duì) 化了,比如一個(gè)工具類,xxxUtils一般是不需要接口或是抽象類的。還 有,如果你要使用類的clone方法,就必須使用實(shí)現(xiàn)類,這個(gè)是JDK提供 的一個(gè)規(guī)范。
  • 任何類都不應(yīng)該從具體類派生
    • 如果一個(gè)項(xiàng)目處于開發(fā)狀態(tài),確實(shí)不應(yīng)該有從具體類派生出子類的 情況,但這也不是絕對(duì)的,因?yàn)槿硕际菚?huì)犯錯(cuò)誤的,有時(shí)設(shè)計(jì)缺陷是在 所難免的,因此只要不超過兩層的繼承都是可以忍受的。特別是負(fù)責(zé)項(xiàng) 目維護(hù)的同志,基本上可以不考慮這個(gè)規(guī)則,為什么?維護(hù)工作基本上 都是進(jìn)行擴(kuò)展開發(fā),修復(fù)行為,通過一個(gè)繼承關(guān)系,覆寫一個(gè)方法就可 以修正一個(gè)很大的Bug,何必去繼承最高的基類呢?(當(dāng)然這種情況盡 量發(fā)生在不甚了解父類或者無法獲得父類代碼的情況下。)
  • 盡量不要覆寫基類的方法
    • 如果基類是一個(gè)抽象類,而且這個(gè)方法已經(jīng)實(shí)現(xiàn)了,子類盡量不要 覆寫。類間依賴的是抽象,覆寫了抽象方法,對(duì)依賴的穩(wěn)定性會(huì)產(chǎn)生一 定的影響。

單一職責(zé)原則

不要存在多余一個(gè)導(dǎo)致類變更的原因

  • 接口
  • 方法

只負(fù)責(zé)一項(xiàng)職責(zé)

如果不是這樣設(shè)計(jì),一個(gè)接口負(fù)責(zé)兩個(gè)職責(zé),一旦需求變更,修改其中一個(gè)職責(zé)的邏輯代碼會(huì)導(dǎo)致另外一個(gè)職責(zé)的功能發(fā)生故障。

案例

image-20201229165519313

用戶信息案例

image-20201229195050632

上述圖片用戶的屬性和用戶的行為并沒有分開

  • 下圖把
    • 用戶信息抽成BO(Business Object,業(yè)務(wù)對(duì)象)
    • 用戶行為抽成Biz(Business Logic 業(yè)務(wù)邏輯對(duì)象)

image-20201229195105954

電話

image-20201229195459993

電話通話會(huì)發(fā)生下面四個(gè)過程

  • 撥號(hào)
  • 通話
  • 回應(yīng)
  • 掛機(jī)

上圖的接口做了兩個(gè)事情

  • 協(xié)議管理
    • dial 撥號(hào)接通
    • hangup 掛機(jī)
  • 數(shù)據(jù)傳送
    • chat

引起變化的點(diǎn)

  1. 協(xié)議接通會(huì)引起會(huì)引起變化(連接導(dǎo)致不傳輸數(shù)據(jù))
  2. 可以有不同的通話方式打電話上網(wǎng)

從上面可以看到包含了兩個(gè)職責(zé),應(yīng)該考慮拆分成兩個(gè)接口

image-20201229195901812

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

  • 類的復(fù)雜性降低,實(shí)現(xiàn)什么職責(zé)都有清晰明確的定義;
  • 可讀性提高,復(fù)雜性降低,那當(dāng)然可讀性提高了;
  • 可維護(hù)性提高,可讀性提高,那當(dāng)然更容易維護(hù)了;
  • 變更引起的風(fēng)險(xiǎn)降低,變更是必不可少的,如果接口的單一職責(zé) 做得好,一個(gè)接口修改只對(duì)相應(yīng)的實(shí)現(xiàn)類有影響,對(duì)其他的接口無影響,這對(duì)系統(tǒng)的擴(kuò)展性、維護(hù)性都有非常大的幫助。

注意

單一職責(zé)原則提出了一個(gè)編寫程序的標(biāo)準(zhǔn),用“職責(zé)”或“變 化原因”來衡量接口或類設(shè)計(jì)得是否優(yōu)良,但是“職責(zé)”和“變化原因”都 是不可度量的,因項(xiàng)目而異,因環(huán)境而異。

This is sometimes hard to see ,單一職責(zé)確實(shí)收到很多因素制約

  • 工期
  • 成本
  • 技術(shù)水平
  • 硬件情況
  • 網(wǎng)絡(luò)情況
  • 政府政策

接口隔離原則

  • 兩個(gè)類之間的依賴應(yīng)該建立在最小的接口上
  • 建立單一接口,不要建立龐大臃腫的接口
  • 盡量細(xì)化接口,接口中的方法盡量少

高內(nèi)聚低耦合

例子

image-20201229165802993

問題

為什么要把IAnimal拆分成IFlyAnimal,ISwimAnimal,不拆分會(huì)有什么樣的問題

  • 一個(gè)類所提供的功能應(yīng)該是他所真正具有的,不拆分會(huì)導(dǎo)致他不提供的功能但是強(qiáng)行需要實(shí)現(xiàn),而且會(huì)有臃腫的類出現(xiàn)
  • 可能適配器模式也是為了解決這個(gè)問題吧

最佳實(shí)踐

  • 一個(gè)接口只服務(wù)于一個(gè)子模塊或者業(yè)務(wù)邏輯
  • 通過業(yè)務(wù)邏輯壓縮接口中的public方法,接口時(shí)常去回顧,盡量 讓接口達(dá)到“滿身筋骨肉”,而不是“肥嘟嘟”的一大堆方法;
  • 已經(jīng)被污染了的接口,盡量去修改,若變更的風(fēng)險(xiǎn)較大,則采用適配器模式進(jìn)行轉(zhuǎn)化處理;
  • 了解環(huán)境,拒絕盲從。每個(gè)項(xiàng)目或產(chǎn)品都有特定的環(huán)境因素,別看到大師是這樣做的你就照抄。千萬別,環(huán)境不同,接口拆分的標(biāo)準(zhǔn)就不同。深入了解業(yè)務(wù)邏輯,最好的接口設(shè)計(jì)就出自你的手中!

迪米特法則

一個(gè)對(duì)象應(yīng)該對(duì)其他對(duì)象保證最少的了解,也稱最少知道原則,如果兩個(gè)類不必彼此直接通信,那么這兩個(gè)類就不應(yīng)該發(fā)生直接的相互作用,如果其中一個(gè)類需要調(diào)用另外一個(gè)類的某個(gè)方法的話,可以通過第三者轉(zhuǎn)發(fā)這個(gè)調(diào)用

能夠降低類與類之間的耦合

  • 強(qiáng)調(diào)只和朋友交流

  • 出現(xiàn)在成員變量、方法的輸入、輸出參數(shù)中的類都可以稱之為成員朋友類, 而出現(xiàn)在方法體內(nèi)部的類不屬于朋友類。

這里面感覺有點(diǎn)職責(zé)分開的感覺,不同的對(duì)象應(yīng)該關(guān)注不同的內(nèi)容,所做的事情也應(yīng)該是自己所關(guān)心的

例子

teamLeader只關(guān)心結(jié)果,不關(guān)心Course

image-20201229171207046

錯(cuò)誤類圖如下

image-20201229171436946

問題

如果以后你要寫代碼和重構(gòu)代碼你怎么分析怎么重構(gòu)?

  1. 先分析相應(yīng)代碼的職責(zé)
  2. 把不同的對(duì)象需要關(guān)心的內(nèi)容抽離出來
  3. 每個(gè)對(duì)象應(yīng)該只創(chuàng)建和關(guān)心自己所關(guān)心的部分
  4. 一定要使用的話可以通過三方來使用
  5. 合適的使用作用域,不要暴露過多的公共方法和非靜態(tài)的公共方法

注意

迪米特法則要求類“羞澀”一點(diǎn),盡量不要對(duì)外公布太多的 public方法和非靜態(tài)的public變量,盡量內(nèi)斂,多使用private、packageprivate、protected等訪問權(quán)限。

在實(shí)際的項(xiàng)目中,需要適度地考慮這個(gè)原則,別為了套用原則而做項(xiàng)目。原則只是供參考,如果 違背了這個(gè)原則,項(xiàng)目也未必會(huì)失敗,這就需要大家在采用原則時(shí)反復(fù) 度量,不遵循是不對(duì)的,嚴(yán)格執(zhí)行就是“過猶不及”。

序列化引起的坑

  • 謹(jǐn)慎使用Serializable
    • 在一個(gè)項(xiàng)目中使用 RMI(Remote Method Invocation,遠(yuǎn)程方法調(diào)用)方式傳遞一個(gè) VO(Value Object,值對(duì)象),這個(gè)對(duì)象就必須實(shí)現(xiàn)Serializable接口 (僅僅是一個(gè)標(biāo)志性接口,不需要實(shí)現(xiàn)具體的方法),也就是把需要網(wǎng) 絡(luò)傳輸?shù)膶?duì)象進(jìn)行序列化,否則就會(huì)出現(xiàn)NotSerializableException異 常。突然有一天,客戶端的VO修改了一個(gè)屬性的訪問權(quán)限,從private 變更為public,訪問權(quán)限擴(kuò)大了,如果服務(wù)器上沒有做出相應(yīng)的變更, 就會(huì)報(bào)序列化失敗,就這么簡單。但是這個(gè)問題的產(chǎn)生應(yīng)該屬于項(xiàng)目管 理范疇,一個(gè)類或接口在客戶端已經(jīng)變更了,而服務(wù)器端卻沒有同步更 新,難道不是項(xiàng)目管理的失職嗎?

遵循的原則

?如果 一個(gè)方法放在本類中,既不增加類間關(guān)系,也對(duì)本類不產(chǎn)生負(fù)面影響, 那就放置在本類中。

里氏替換原則

一個(gè)軟件實(shí)體如果能夠適用一個(gè)父親的話,那么一定適用其子類,所有引用父親的地方必須能透明的使用其子類的對(duì)象,子類能夠替換父類對(duì)象

  • 子類可以實(shí)現(xiàn)父類的抽象方法,但是不能覆蓋父類的非抽象方法
  • 子類中可以增加自己特有的方法
  • 子類的方法重載父類的方法時(shí),入?yún)⒁雀割惖姆椒ㄝ斎雲(yún)?shù)更寬松
  • 子類實(shí)現(xiàn)父類方法的時(shí)候(重寫/重載或?qū)崿F(xiàn)抽象方法),方法的后置條件(方法的輸出,返回)要比父類更加嚴(yán)格或者相等

例子

價(jià)格重寫問題

價(jià)格不是直接重寫,而是新寫一個(gè)方法

public class JavaDiscountCourse extends JavaCourse {
  public JavaDiscountCourse(Integer id, String name, Double price) {
    super(id, name, price);
  }
  public Double getDiscountPrice(){
    return super.getPrice() * 0.61;
  }
}

長方形和正方形問題

image-20201229180802166

public static void resize(Rectangle rectangle){
  while (rectangle.getWidth() >= rectangle.getHeight()){
    rectangle.setHeight(rectangle.getHeight() + 1);
    System.out.println("Width:" +rectangle.getWidth() +",Height:" + rectangle.getHeight());
  }
  System.out.println("Resize End,Width:" +rectangle.getWidth() +",Height:" + rectangle.getHeight());
}
public class Square extends Rectangle {
  private long length;

  //勝率

  @Override
  public void setHeight(long height) {
    setLength(height);
  }
}

當(dāng)前設(shè)計(jì)會(huì)出現(xiàn)死循環(huán)

解決辦法

抽象接口

public interface QuadRangle {
    long getWidth();
    long getHeight();
}

返回共同的length

public class Square implements QuadRangle {
  private long length;

  public long getLength() {
    return length;
  }

  public void setLength(long length) {
    this.length = length;
  }

  public long getWidth() {
    return length;
  }

  public long getHeight() {
    return length;
  }
}

當(dāng)前方式子類就能夠隨時(shí)替換父類了

問題

  1. 你怎么理解里氏替換原則,為什么要保證使用父類的地方可以透明地使用子類
    • 子類必須實(shí)現(xiàn)父類中沒有實(shí)現(xiàn)的方法
    • is-a的問題
    • 如果父類的地方替換成子類不行的話程序復(fù)雜性增加,繼承反而帶來了程序的復(fù)雜度
    • 子類只能在父類的基礎(chǔ)上增加新的方法
  2. 在具體場景中怎么保證使用父類的地方可以透明地使用子類
    • 父類返回多使用具體實(shí)現(xiàn),入?yún)⒍嗍褂贸橄蠡蛘哒f頂層接口
    • 子類可以新增一些自己特有的方法

注意

如果子類不能完整地實(shí)現(xiàn)父類的方法,或者父類的某些方法 在子類中已經(jīng)發(fā)生“畸變”,則建議斷開父子繼承關(guān)系,采用依賴、聚 集、組合等關(guān)系代替繼承。

盡量避免子類的“個(gè)性”,一旦子 類有“個(gè)性”,這個(gè)子類和父類之間的關(guān)系就很難調(diào)和了,把子類當(dāng)做父 類使用,子類的“個(gè)性”被抹殺——委屈了點(diǎn);把子類單獨(dú)作為一個(gè)業(yè)務(wù) 來使用,則會(huì)讓代碼間的耦合關(guān)系變得撲朔迷離——缺乏類替換的標(biāo) 準(zhǔn)。

合成復(fù)用原則

盡可能使用對(duì)象組合 has-a組合 或者是 contains-a聚合而不是通過繼承來達(dá)到軟件復(fù)用的目的。

  • 繼承是白箱復(fù)用
    • 所有細(xì)節(jié)都暴露給了子類
  • 組合和聚合是黑箱復(fù)用
    • 對(duì)象歪的對(duì)象獲取不到細(xì)節(jié)

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

image-20201229192102942

問題

為什么要多用組合和聚合少用繼承

  • 繼承是侵入性的
  • Java只支持單繼承
  • 降低了代碼的靈活性,子類多了很多約束
  • 增強(qiáng)了耦合性,父類修改的時(shí)候需要考慮子類的修改
    • 會(huì)導(dǎo)致關(guān)鍵代碼被修改

總結(jié)

如果你只有一把鐵錘, 那么任何東西看上去都像是釘子。

  • 適當(dāng)?shù)膱鼍笆褂眠m當(dāng)?shù)脑O(shè)計(jì)原則
  • 需要考慮,人力,成本,時(shí)間,質(zhì)量,不要刻意追求完美
  • 需要多思考才能用好工具

我的筆記倉庫地址gitee 快來給我點(diǎn)個(gè)Star吧

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多

    欧美日韩少妇精品专区性色| 亚洲av日韩一区二区三区四区| 国产欧美一区二区另类精品 | 91福利视频日本免费看看| 日韩一级免费中文字幕视频| 国产女性精品一区二区三区| 女同伦理国产精品久久久| 日本午夜免费观看视频| 丝袜诱惑一区二区三区| 欧美精品女同一区二区| 欧美日韩中黄片免费看| 欧美亚洲三级视频在线观看| 手机在线观看亚洲中文字幕| 欧美一区二区日韩一区二区| 老司机精品线观看86| 又黄又色又爽又免费的视频| 日韩国产亚洲一区二区三区| 91插插插外国一区二区婷婷| 中文字幕人妻一区二区免费 | 日韩成人动作片在线观看| 亚洲av专区在线观看| 中文字幕乱码亚洲三区| 国产一区二区三区精品免费| 欧洲日韩精品一区二区三区| 亚洲香艳网久久五月婷婷| 久久精品国产亚洲熟女| 日本av在线不卡一区| 国产精品大秀视频日韩精品| 日本男人女人干逼视频| 欧美一本在线免费观看| 久久99这里只精品热在线| 欧美人妻盗摄日韩偷拍| a久久天堂国产毛片精品| 熟女乱一区二区三区四区| 精品国产亚洲一区二区三区| 老司机亚洲精品一区二区| 国产麻豆视频一二三区| 欧美日韩国产另类一区二区| 东京热男人的天堂久久综合| 日韩精品一区二区三区射精| 国产亚洲午夜高清国产拍精品|