軟件設計的七大原則


https://blog.csdn.net/weixin_44036154/article/details/109026656

目錄:

1、開閉原則

2、里氏替換

3、依賴倒置

 

  在軟件開發中,為了提高軟件系統的可維護性和可復用性,增加軟件的可擴展性和靈活性,程序員要盡量根據 7 條原則來開發程序,從而提高軟件開發效率、節約軟件開發成本和維護成本。

  這 7 種設計原則是軟件設計模式必須盡量遵循的原則,各種原則要求的側重點不同。其中,開閉原則是總綱,它告訴我們要對擴展開放,對修改關閉;里氏替換原則告訴我們不要破壞繼承體系;依賴倒置原則告訴我們要面向接口編程;單一職責原則告訴我們實現類要職責單一;接口隔離原則告訴我們在設計接口的時候要精簡單一;迪米特法則告訴我們要降低耦合度;合成復用原則告訴我們要優先使用組合或者聚合關系復用,少用繼承關系復用。

1、開閉原則

(1)原則定義:

軟件實體應當對擴展開放,對修改關閉,這就是開閉原則的經典定義。

  這里的軟件實體包括以下幾個部分:

  • 項目中划分出的模塊
  • 類與接口
  • 方法
開閉原則的含義是:當應用的需求改變時,在不修改軟件實體的源代碼或者二進制代碼的前提下,可以擴展模塊的功能,使其滿足新的需求。

 

(2)實現原理:

   可以通過“抽象約束、封裝變化”來實現開閉原則,即通過接口或者抽象類為軟件實體 定義一個相對穩定的抽象層,而將相同的可變因素封裝在相同的具體實現類中。
  因為抽象靈活性好,適應性廣,只要抽象的合理,可以基本保持軟件架構的穩定。而軟件中易變的細節可以從抽象派生來的實現類來進行擴展,當軟件需要發生變化時,只需要根據需求重新派生一個實現類來擴展就可以了。
 

(3)實例展示:

/**
 * 定義課程接口
 */
public interface ICourse {
    String getName(); // 獲取課程名稱
    Double getPrice(); // 獲取課程價格
    Integer getType(); // 獲取課程類型
}

/**
 * 英語課程接口實現
 */
public class EnglishCourse implements ICourse {
    private String name;
    private Double price;
    private Integer type;
//省略get,set,空參,滿參
}

// 測試
public class Main {
    public static void main(String[] args) {
        ICourse course = new EnglishCourse("小學英語", 199D, "Mr.Zhang");
        System.out.println(
                "課程名字:" + course.getName() + " " +
                        "課程價格:" + course.getPrice() + " " +
                        "課程作者:" + course.getAuthor()
        );
    }
}

項目上線,但是出現了節假日打折的情況出現,現在有以下這幾種方法去修改:

1:修改接口(不可取,修改接口時其他的代碼也得改!)

2:修改實現類(不可取,這樣會有兩個獲取價格的方法!)

3:擴展實現方法(可取,不改變原有代碼,功能可實現!)

直接添加一個子類 SaleEnglishCourse ,重寫 getPrice()方法,這個方案對源代碼沒有影響,符合開閉原則,所以是可執行的方案

public class SaleEnglishCourse extends EnglishCourse {
    public SaleEnglishCourse(String name, Double price, String author) {
        super(name, price, author);
    }
    @Override
    public Double getPrice() {
        return super.getPrice() * 0.85;
    }
}

 

(4)開閉原則的作用

開閉原則是面向對象程序設計的終極目標,它使軟件實體擁有一定的適應性和靈活性的同時具備穩定性和延續性。具體來說,其作用如下。

1. 對軟件測試的影響

軟件遵守開閉原則的話,軟件測試時只需要對擴展的代碼進行測試就可以了,因為原有的測試代碼仍然能夠正常運行。

2. 可以提高代碼的可復用性

粒度越小,被復用的可能性就越大;在面向對象的程序設計中,根據原子和抽象編程可以提高代碼的可復用性。

3. 可以提高軟件的可維護性

遵守開閉原則的軟件,其穩定性高和延續性強,從而易於擴展和維護。

 

 2、里氏替換原則

(1)原則定義

繼承必須確保超類(被繼承的類稱為超類,繼承的類稱為子類)所擁有的性質在子類中仍然成立。

里氏替換原則主要闡述了有關繼承的一些原則,也就是什么時候應該使用繼承,什么時候不應該使用繼承,以及其中蘊含的原理。里氏替換原是繼承復用的基礎,它反映了基類與子類之間的關系,是對開閉原則的補充,是對實現抽象化的具體步驟的規范。

(2)實現原理:

1、子類可以實現父類的抽象方法,但不能覆蓋父類的非抽象方法

2、子類可以有自己的個性
3、覆蓋或實現父類的方法時輸入參數可以被放大

4、覆寫或實現父類的方法時輸出結果可以被縮小

(3)實例展示:

1、子類可以實現父類的抽象方法,但不能覆蓋父類的非抽象方法

子類可以實現父類的抽象方法,但不能覆蓋父類的非抽象方法,父類中凡是已經實現好的方法(相對於抽象方法而言),實際上是在設定一系列的規范和契約,雖然它不強制要求所有的子類必須遵從這些契約,但是如果子類對這些非抽象方法任意修改,就會對整個繼承體系造成破壞。
(略)

2、子類可以有自己的個性

在繼承父類屬性和方法的同時,每個子類也都可以有自己的個性,在父類的基礎上擴展自己的功能。前面其實已經提到,當功能擴展時,子類盡量不要重寫父類的方法,而是另寫一個方法,所以對上面的代碼加以更改,使其符合里氏替換原則。

(略)

3、覆蓋或實現父類的方法時輸入參數可以被放大

當子類的方法重載父類的方法時,方法的前置條件(即方法的形參)要比父類方法的輸入參數更寬松。

String 繼承於CharSequence)
public class ParentClazz {
    public void say(String str) {
        System.out.println("parent execute say " + str);
    }
}

public class ChildClazz extends ParentClazz {
    public void say(CharSequence str) {
        System.out.println("child execute say " + str);
    }
}

// 執行結果:
// parent execute say hello
// parent execute say hello
// 參數大小不一樣,一直用的都是string,父類的

4、覆寫或實現父類的方法時輸出結果可以被縮小

當子類的方法實現父類的抽象方法時,方法的后置條件(即方法的返回值)要比父類更嚴格。

public abstract class Father {
    public abstract Map hello();
}


public class Son extends Father {
    @Override
    public Map hello() {
        HashMap map = new HashMap();
        System.out.println("son execute");
        return map;
    }
}

(4)里氏替換的作用:

里氏替換原則是實現開閉原則的重要方式之一。

它克服了繼承中重寫父類造成的可復用性變差的缺點。
它是動作正確性的保證。即類的擴展不會給已有的系統引入新的錯誤,降低了代碼出錯的可能性。

 3、依賴倒置原則

(1)原則定義:

依賴倒置原則的原始定義為:高層模塊不應該依賴低層模塊,兩者都應該依賴其抽象;抽象不應該依賴細節,細節應該依賴抽象。其核心思想是:要面向接口編程,不要面向實現編程。

依賴倒置原則是實現開閉原則的重要途徑之一,它降低了客戶與實現模塊之間的耦合。
由於在軟件設計中,細節具有多變性,而抽象層則相對穩定,因此以抽象為基礎搭建起來的架構要比以細節為基礎搭建起來的架構要穩定得多。這里的抽象指的是接口或者抽象類,而細節是指具體的實現類。
使用接口或者抽象類的目的是制定好規范和契約,而不去涉及任何具體的操作,把展現細節的任務交給它們的實現類去完成。

(2)實現原理

依賴倒置原則的目的是通過要面向接口的編程來降低類間的耦合性,所以我們在實際編程中只要遵循以下4點,就能在項目中滿足這個規則。

每個類盡量提供接口或抽象類,或者兩者都具備。
  1. 變量的聲明類型盡量是接口或者是抽象類。
  2. 任何類都不應該從具體類派生。
  3. 使用繼承時盡量遵循里氏替換原則。

(3)實例展示

public class T2 {
    public static void main(String[] args) {
        IDriver zhangsan = new Driver();
        Icar benz = new Benz();
        zhangsan.drive(benz);
        Icar bmw = new BMW();
        zhangsan.drive(bmw);
    }
}

interface IDriver {
    // 司機職責就是駕駛汽車
    public void drive(Icar car);
}

class Driver implements IDriver {
    // 司機職責就是駕駛汽車
    @Override
    public void drive(Icar car) {
        car.run();
    }
}

interface Icar {
    // 車的作用就是跑
    public void run();
}

class Benz implements Icar {
    // 車的作用就是跑
    @Override
    public void run() {
        System.out.println("奔馳車跑起來了");
    }
}


class BMW implements Icar {
    // 車的作用就是跑
    @Override
    public void run() {
        System.out.println("寶馬車跑起來了");
    }
}
(4)依賴倒置原則作用:

依賴倒置原則的主要作用如下。

依賴倒置原則可以降低類間的耦合性。
  1. 依賴倒置原則可以提高系統的穩定性。
  2. 依賴倒置原則可以減少並行開發引起的風險。
  3. 依賴倒置原則可以提高代碼的可讀性和可維護性。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM