悉數六大設計原則

悉數六大設計原則

目錄

  • 悉數六大設計原則
    • 前言?
    • 誰發明了設計模式
    • 設計原則
      • 設計原則與設計模式的關系
    • 單一職責
      • 什么是單一職責
      • 不遵循單一職責原則的設計
      • 遵循單一職責原則的設計
      • 單一職責的優點
      • 示例代碼:
    • 里氏替換原則
      • 什么是里氏替換原則
      • 示例代碼:
        • 違反里氏替換原則的代碼
        • 遵循里氏替換原則的代碼
      • 里氏替換原則的優點
    • 依賴倒置原則
      • 什么是依賴倒置原則
      • 依賴倒置原則的核心思想
      • 依賴倒置原則的優點
      • 示例代碼:
        • 不遵循依賴倒置原則的設計
        • 遵循依賴倒置原則的設計
      • 實際應用中的優點
    • 接口隔離原則
      • 什么是接口隔離原則
      • 示例代碼:
        • 不遵循接口隔離原則的設計
        • 遵循接口隔離原則的設計
      • 實際應用中的好處
      • 示例代碼:
    • 迪米特原則
      • 什么是迪米特法則
      • 迪米特法則的規則
      • 示例代碼:
        • 違反迪米特法則的代碼
        • 遵循迪米特法則的代碼
      • 迪米特法則的優點
    • 開閉原則
      • 什么是開閉原則
      • 如何實現開閉原則
      • 示例代碼:
        • 場景描述
        • 違反開閉原則的設計
        • 遵循開閉原則的設計
      • 開閉原則的優點
    • 總結🍭

前言?

大家好,我是Leo哥🫣🫣🫣,今天開始我們來學習一下關于設計模式的內容。提起設計模式,大家肯定不陌生,可能在此之前你也多少了了解過設計模式,但在實際的業務開發中使?用卻不不多,多數時候都是?大?面積堆積ifelse 組裝業務流程,對于?一次次的需求迭代和邏輯補充,只能東拼?西湊 Ctrl+C 、 Ctrl+V 。作為一名優秀的程序員,設計模式可謂是必修課,接下來就跟著Leo哥一起來了解了解設計模式吧。

誰發明了設計模式

設計模式的概念最早是由 克?里里斯托佛·亞歷?山?大 在其著作 《建筑模式語?言》 中?首次提出的。 本書介紹了了城市設計的 語?言,提供了了253個描述城鎮、鄰?里里、住宅、花園、房間及?西部構造的模式, ?而此類 語?言 的基本單元就是模式。后來, 埃?里里希·伽瑪 、 約翰·弗利利賽德斯 、 拉爾夫·約翰遜 和 理理查德·赫爾姆 這四位作者接受了了模式的概念。 1994 年年, 他們出版了了 《設計模式: 可復?用?面向對象軟件的基礎》 ?一書, 將設計模式的概念應?用到程序開發領域中。

設計原則

在學習設計模式之前,我們應該先了解一下設計原則,那么什么是設計原則呢。設計原則是指導軟件設計的一系列準則和規范,旨在幫助開發人員創建高質量的代碼。這些原則強調代碼的可維護性、可擴展性和靈活性,減少系統的復雜性和提高代碼的可理解性。

設計原則與設計模式的關系

  • 設計原則:設計原則是高層次的指導方針,提供了軟件設計的基本框架和標準。這些原則可以應用于任何軟件開發項目,以確保代碼的高質量和長期可維護性。
  • 設計模式:設計模式是針對特定問題的具體解決方案,是對設計原則的具體應用和實現。設計模式提供了可以復用的代碼結構和模板,幫助開發人員解決常見的設計問題。

話不多說,下面我們首先來學習一下經典的六大設計原則吧。

單一職責

首先, 我們來看單一職責的定義。

單一職責原則,全稱Single Responsibility Principle, 簡稱SRP. A class should have only one reason to change 類發生更改的原因應該只有一個 。

什么是單一職責

單一職責原則(Single Responsibility Principle, SRP) 是軟件設計中的一種原則,它強調每個類應該只有一個職責,即一個類只負責一項功能或一類功能的邏輯。這個原則是 SOLID 原則中的第一個,它有助于提高代碼的可維護性、可讀性和可擴展性。

就一個類而言,應該僅有一個引起它變化的原因。應該只有一個職責。如果一個類有一個以上的職責,這些職責就耦合在了一起。一個職責的變化可能會削弱或者抑制這個類完成其他職責的能力。這會導致脆弱的設計。當一個職責發生變化時,可能會影響其它的職責。另外,多個職責耦合在一起,會影響復用性。想要避免這種現象的發生,就要盡可能的遵守單一職責原則。

單一職責原則的核心就是解耦和增強內聚性。

不遵循單一職責原則的設計

public class ReportManager {public String generateReport() {// 生成報告的邏輯return "Report Content";}public void printReport(String report) {// 打印報告的邏輯System.out.println(report);}
}

在上面的代碼示例中,ReportManager 類同時負責生成報告和打印報告。這兩個職責耦合在一起,如果將來需要修改打印報告的方式,我們需要修改 ReportManager 類,這違反了單一職責原則。

遵循單一職責原則的設計

我們可以將生成報告和打印報告的職責分離到不同的類中:

// 生成報告的類
public class ReportGenerator {public String generateReport() {// 生成報告的邏輯return "Report Content";}
}// 打印報告的類
public class ReportPrinter {public void printReport(String report) {// 打印報告的邏輯System.out.println(report);}
}

現在,ReportGenerator 類只負責生成報告,ReportPrinter 類只負責打印報告。這種設計使得每個類的職責單一,如果將來需要修改打印報告的方式,只需要修改 ReportPrinter 類,不會影響到 ReportGenerator 類。

單一職責的優點

  1. 提高可維護性:職責單一的類更容易理解和維護。每個類的代碼量減少,邏輯更加清晰。
  2. 提高可復用性:職責單一的類可以更容易地在不同的上下文中復用,而無需擔心未使用的功能帶來的負擔。
  3. 增強測試性:職責單一的類通常具有較少的依賴,單元測試更容易編寫和執行。
  4. 降低耦合度:將不同的職責分離到不同的類中,減少了類之間的耦合,增強了系統的靈活性和可擴展性。

示例代碼:

下面我們來寫一個一個更完整的代碼示例,展示了如何使用單一職責原則設計一個簡單的學生管理系統:

// 學生類,負責學生信息
public class Student {private String name;private int age;public Student(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public int getAge() {return age;}
}// 學生數據庫操作類,負責與數據庫的交互
public class StudentRepository {public void save(Student student) {// 將學生信息保存到數據庫的邏輯System.out.println("Saving student: " + student.getName());}public Student findByName(String name) {// 從數據庫中查找學生信息的邏輯return new Student(name, 20); // 模擬返回一個學生對象}
}// 學生信息展示類,負責學生信息的展示
public class StudentView {public void displayStudentInfo(Student student) {System.out.println("Student Name: " + student.getName());System.out.println("Student Age: " + student.getAge());}
}// 主類,負責調用其他類完成具體功能
public class Main {public static void main(String[] args) {StudentRepository studentRepository = new StudentRepository();StudentView studentView = new StudentView();Student student = new Student("John Doe", 20);studentRepository.save(student);Student retrievedStudent = studentRepository.findByName("John Doe");studentView.displayStudentInfo(retrievedStudent);}
}
  • Student 類只負責保存學生的基本信息。
  • StudentRepository 類負責與數據庫的交互,處理學生信息的保存和查詢。
  • StudentView 類負責展示學生信息。

里氏替換原則

什么是里氏替換原則

里氏替換原則(Liskov Substitution Principle,LSP)是由計算機科學家 Barbara Liskov 在 1987 年提出的,是面向對象設計的五大基本原則之一(SOLID 原則中的 L)。里氏替換原則的核心思想是:在一個程序中,如果基類可以被子類替換,而不影響程序的正確性,那么這個子類是正確的。換句話說,子類對象應該能夠替換基類對象而不改變程序的行為。

里式替換原則是用來幫助我們在繼承關系中進行父子類的設計。

里氏替換原則(Liskov Substitution principle)是對子類型的特別定義的. 為什么叫里式替換原則呢?因為這項原則最早是在1988年,由麻省理工學院的一位姓里的女士(Barbara Liskov)提出來的。

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

里式替換原則有兩層定義:

定義1

If S is a subtype of T, then objects of type T may be replaced with objects of type S, without breaking the program。

如果S是T的子類,則T的對象可以替換為S的對象,而不會破壞程序。

定義2:

Functions that use pointers of references to base classes must be able to use objects of derived classes without knowing it。

所有引用其父類對象方法的地方,都可以透明的替換為其子類對象

示例代碼:

下面是一個違反里氏替換原則的代碼示例。

違反里氏替換原則的代碼

假設我們有一個基類 Bird 和一個子類 Penguin

class Bird {public void fly() {System.out.println("I can fly");}
}class Penguin extends Bird {@Overridepublic void fly() {throw new UnsupportedOperationException("Penguins can't fly");}
}

這個代碼示例中,Penguin 類重寫了 fly 方法并拋出異常,這違反了里氏替換原則,因為如果我們使用 Penguin 對象替換 Bird 對象,程序將會拋出異常,導致行為不一致。

遵循里氏替換原則的代碼

為了遵循里氏替換原則,我們可以引入一個接口 Flyable,并讓 Bird 和其他可以飛的鳥類實現這個接口,而 Penguin 則不實現這個接口:

interface Flyable {void fly();
}class Bird {public void eat() {System.out.println("I can eat");}
}class Sparrow extends Bird implements Flyable {@Overridepublic void fly() {System.out.println("I can fly");}
}class Penguin extends Bird {// 企鵝沒有實現Flyable這個接口
}

在這個重構后的設計中,Penguin 類不再需要實現 fly 方法,從而避免了違反里氏替換原則。現在,如果我們有一個方法需要處理所有可以飛的鳥,我們可以使用 Flyable 接口:

public void letBirdFly(Flyable bird) {bird.fly();
}public static void main(String[] args) {Sparrow sparrow = new Sparrow();Penguin penguin = new Penguin();letBirdFly(sparrow); // This works// letBirdFly(penguin); // 這將導致編譯時錯誤
}

通過這種方式,我們確保了替換基類對象不會影響程序的行為,從而遵循了里氏替換原則。

里氏替換原則的優點

  1. 提高代碼的可維護性:遵循里氏替換原則,可以確保子類和基類的行為一致,減少代碼中的錯誤,提升代碼的可維護性。
  2. 增強代碼的可擴展性:通過接口和抽象類的使用,可以更容易地擴展系統,添加新的子類而不影響現有代碼。
  3. 增強代碼的可重用性:遵循里氏替換原則,可以提高代碼的重用性,使得基類和子類之間的關系更加明確和穩固。

依賴倒置原則

什么是依賴倒置原則

**依賴倒置原則(Dependency Inversion Principle,DIP)**是面向對象設計的五個SOLID原則之一。該原則強調:

  1. 高層模塊不應該依賴于低層模塊。二者都應該依賴于抽象。
  2. 抽象不應該依賴于具體實現。具體實現應該依賴于抽象。

簡單來說,依賴倒置原則提倡面向接口編程,而不是面向實現編程。這可以減少高層模塊與低層模塊之間的耦合,使系統更具靈活性和可擴展性。

依賴倒置原則的核心思想

  1. 依賴于抽象(接口或抽象類),而不是具體類:通過依賴于抽象,可以在不修改高層模塊的情況下更換低層模塊的實現。
  2. 通過依賴注入來實現依賴倒置:使用構造器注入、方法注入或屬性注入的方式,將具體實現傳遞給高層模塊。

依賴倒置原則的優點

  • 降低耦合:高層模塊和低層模塊之間通過接口或抽象類解耦。
  • 增強可維護性:修改低層模塊的實現不會影響高層模塊。
  • 提高可擴展性:可以方便地替換或新增實現而不改變現有代碼。

示例代碼:

不遵循依賴倒置原則的設計

在這個例子中,Light 類和 Switch 類之間有直接的依賴關系:

// 燈類
class Light {public void turnOn() {System.out.println("Light is turned on.");}public void turnOff() {System.out.println("Light is turned off.");}
}// 開關類
class Switch {private Light light;public Switch() {this.light = new Light();}public void operate(String command) {if (command.equalsIgnoreCase("ON")) {light.turnOn();} else if (command.equalsIgnoreCase("OFF")) {light.turnOff();}}
}public class Main {public static void main(String[] args) {Switch lightSwitch = new Switch();lightSwitch.operate("ON");lightSwitch.operate("OFF");}
}

在這個設計中,Switch 類直接依賴于 Light 類,如果需要更換 Light 的實現,需要修改 Switch 類的代碼。

遵循依賴倒置原則的設計

在這個例子中,通過引入接口 Switchable,實現依賴倒置原則:

// 開關接口
interface Switchable {void turnOn();void turnOff();
}// 燈類實現開關接口
class Light implements Switchable {public void turnOn() {System.out.println("Light is turned on.");}public void turnOff() {System.out.println("Light is turned off.");}
}// 開關類依賴于開關接口,而不是具體的實現
class Switch {private Switchable device;public Switch(Switchable device) {this.device = device;}public void operate(String command) {if (command.equalsIgnoreCase("ON")) {device.turnOn();} else if (command.equalsIgnoreCase("OFF")) {device.turnOff();}}
}public class Main {public static void main(String[] args) {Switchable light = new Light();Switch lightSwitch = new Switch(light);lightSwitch.operate("ON");lightSwitch.operate("OFF");}
}

在上面的設計中,Switch 類依賴于 Switchable 接口,而不是具體的 Light 類。如果將來需要更換實現,只需實現 Switchable 接口并傳遞新的實現給 Switch 類。

實際應用中的優點

  1. 增強代碼的可測試性:通過依賴注入,可以輕松地將實際實現替換為模擬對象,從而進行單元測試。
  2. 增加代碼的靈活性和可擴展性:通過依賴抽象,代碼可以適應不同的實現,而不需要修改高層模塊。
  3. 提高代碼的可維護性:代碼的變更只會影響具體實現,不會波及依賴于抽象的高層模塊。

接口隔離原則

什么是接口隔離原則

接口隔離原則(Interface Segregation Principle,ISP) 是面向對象設計的五個SOLID原則之一。該原則強調:

  1. 客戶不應該被迫依賴他們不使用的方法。
  2. 多個特定客戶端接口要好于一個寬泛用途的接口。

Clients should not be forced to depend upon interfaces that they don’t use. 客戶端只依賴于它所需要的接口;它需要什么接口就提供什么接口,把不需要的接口剔除掉。

The dependency of one class to another one should depend on the smallest possible interface. 類間的依賴關系應建立在最小的接口上。

換句話說,接口隔離原則提倡將大接口拆分為多個小接口,使得接口更具針對性和靈活性。這樣,客戶端只需依賴它們真正需要的接口,避免了冗余和不必要的依賴。

也就是說: 接口盡量細化,接口中的方法盡量少

示例代碼:

不遵循接口隔離原則的設計

在這個例子中,Worker 接口包含了所有工作者可能需要的方法,但具體的工作者類可能只需要其中的一部分:

// 工作者接口
public interface Worker {void work();void eat();
}// 開發者類
public class Developer implements Worker {@Overridepublic void work() {System.out.println("Developer is working.");}@Overridepublic void eat() {System.out.println("Developer is eating.");}
}// 機器人類
public class Robot implements Worker {@Overridepublic void work() {System.out.println("Robot is working.");}@Overridepublic void eat() {// 機器人不需要吃飯,但必須實現這個方法}
}

在這個設計中,Robot 類被迫實現了 eat 方法,這違反了接口隔離原則。

遵循接口隔離原則的設計

通過將 Worker 接口拆分為更細化的接口,可以避免上述問題:

// 工作接口
public interface Workable {void work();
}// 吃飯接口
public interface Eatable {void eat();
}// 開發者類實現了工作和吃飯接口
public class Developer implements Workable, Eatable {@Overridepublic void work() {System.out.println("Developer is working.");}@Overridepublic void eat() {System.out.println("Developer is eating.");}
}// 機器人類只實現了工作接口
public class Robot implements Workable {@Overridepublic void work() {System.out.println("Robot is working.");}
}

在這個設計中,Developer 類實現了 WorkableEatable 接口,而 Robot 類只實現了 Workable 接口,遵循了接口隔離原則。

實際應用中的好處

  1. 提高靈活性:將大接口拆分為多個小接口,使得類可以選擇實現自己需要的接口,增加了系統的靈活性。
  2. 減少冗余:客戶端只依賴它們實際需要的接口,減少了不必要的方法實現。
  3. 增強可維護性:接口的細化使得系統更易于理解和維護,修改和擴展時影響范圍更小。
  4. 提高可測試性:小接口更容易進行單元測試,因為每個接口只包含了特定的功能方法。

示例代碼:

// 工具使用接口
public interface ToolUsable {void useTool();
}// 吃飯接口
public interface Eatable {void eat();
}// 工人類實現了工具使用和吃飯接口
public class Worker implements ToolUsable, Eatable {@Overridepublic void useTool() {System.out.println("Worker is using a tool.");}@Overridepublic void eat() {System.out.println("Worker is eating.");}
}// 機器人類只實現了工具使用接口
public class Robot implements ToolUsable {@Overridepublic void useTool() {System.out.println("Robot is using a tool.");}
}public class Main {public static void main(String[] args) {ToolUsable workerToolUser = new Worker();Eatable workerEater = new Worker();ToolUsable robotToolUser = new Robot();workerToolUser.useTool();workerEater.eat();robotToolUser.useTool();}
}

在這個示例中,我們將 Worker 類和 Robot 類的接口細化,使得它們只實現自己需要的接口,遵循了接口隔離原則。

迪米特原則

什么是迪米特法則

迪米特法則(Law of Demeter, LoD),又稱為最少知識原則(Principle of Least Knowledge),是一種軟件設計原則,其主要思想是:一個對象應該對其他對象有盡可能少的了解,即一個對象不應該知道太多不屬于它直接責任的對象細節。

如果兩個類不必彼此直接通信,那么這兩個類就不應當發生直接的相互作用。如果其中的一個類需要調用另一個類的某一個方法的話,可以通過第三者轉發這個調用。

朋友圈的確定“朋友”條件:

  1. 當前對象本身(this)
  2. 以參數形式傳入到當前對象方法中的對象. 方法入參是一個對象, 這是這個對象和當前類是朋友
  3. 當前對象的實例變量直接引用的對象 定一個一個類, 里面的屬性引用了其他對象, 那么這個對象的實例和當前實例是朋友
  4. 當前對象的實例變量如果是一個聚集,那么聚集中的元素也都是朋友 如果屬性是一個對象, 那么屬性和對象里的元素都是朋友
  5. 當前對象所創建的對象

任何一個對象,如果滿足上面的條件之一,就是當前對象的“朋友”;否則就是“陌生人”。

狹義的迪米特法則的缺點:

在系統里造出大量的小方法,這些方法僅僅是傳遞間接的調用,與系統的業務邏輯無關。 遵循類之間的迪米特法則會是一個系統的局部設計簡化,因為每一個局部都不會和遠距離的對象有直接的關聯。但是,這也會造成系統的不同模塊之間的通信效率降低,也會使系統的不同模塊之間不容易協調。

迪米特法則的規則

  1. 只調用自己的方法:對象只能調用自己方法,或者調用由自身創建的對象的方法。
  2. 只調用直接朋友的方法:對象只能調用作為參數傳遞給它的對象的方法,或是它的成員變量、全局變量的方法。

示例代碼:

違反迪米特法則的代碼

假設我們有一個 Car 類,它包含一個 Engine 對象,而 Engine 對象包含一個 Oil 對象:

class Oil {public void checkOilLevel() {System.out.println("Checking oil level");}
}class Engine {private Oil oil;public Engine() {this.oil = new Oil();}public Oil getOil() {return oil;}
}class Car {private Engine engine;public Car() {this.engine = new Engine();}public Engine getEngine() {return engine;}
}public class Main {public static void main(String[] args) {Car car = new Car();// 違反迪米特法則的代碼:直接訪問內部對象的內部對象的方法car.getEngine().getOil().checkOilLevel();}
}

在上面的例子中,Main 類通過 Car 對象訪問 Engine 對象,再通過 Engine 對象訪問 Oil 對象,最終調用 checkOilLevel 方法。這違反了迪米特法則,因為 Main 類知道了太多關于 EngineOil 的細節。

遵循迪米特法則的代碼

我們可以通過在 Car 類中添加一個方法,來避免直接訪問內部對象的內部對象:

class Oil {public void checkOilLevel() {System.out.println("Checking oil level");}
}class Engine {private Oil oil;public Engine() {this.oil = new Oil();}public void checkOilLevel() {oil.checkOilLevel();}
}class Car {private Engine engine;public Car() {this.engine = new Engine();}public void checkOilLevel() {engine.checkOilLevel();}
}public class Main {public static void main(String[] args) {Car car = new Car();// 遵循迪米特法則的代碼:只調用直接對象的方法car.checkOilLevel();}
}

在這個重構后的例子中,Main 類只調用了 Car 對象的方法 checkOilLevelCar 對象內部處理了所有與 EngineOil 對象的交互。這遵循了迪米特法則,降低了對象之間的耦合度。

迪米特法則的優點

  1. 降低耦合度:減少對象之間的依賴關系,使得系統更容易維護和擴展。
  2. 提高內聚性:每個對象只關注自身的職責,增強了代碼的內聚性。
  3. 增強可讀性:減少了代碼的復雜性,使代碼更容易理解和閱讀。

開閉原則

什么是開閉原則

開閉原則(Open-Closed Principle, OCP)是面向對象設計中的重要原則之一,由 Bertrand Meyer 于 1988 年提出。它是 SOLID 原則中的第二個,指的是軟件實體(類、模塊、函數等)應該對擴展開放,對修改關閉。這意味著一個系統在需要改變的時候,應該通過擴展已有代碼的行為來實現,而不是修改已有的代碼。

如何實現開閉原則

“需求總是變化”、“世界上沒有一個軟件是不變的”。這里投射出的意思是:需求總是變化的, 可是對于軟件設計者來說, 如何才能做到不對原有系統修改的前提下, 實現靈活的擴展. 這就是開閉原則要實現的。

我們在設計系統的時候, 不可能設想一次性把需求確定后, 后面就不改變了.這不科學也不現實的. 既然需求是一定會變化的, 那么我們要如何優雅的面對這種變化呢? 如何設計可以使軟件相對容易修改, 不至于需求一變, 就要把整個程序推到重來?

開封-封閉原則. 設計軟件要容易維護且不容易出問題的最好辦法, 就是多擴展, 少修改。

開閉原則通常通過使用抽象和多態性來實現。具體來說,可以通過以下幾種方法:

  1. 使用接口或抽象類:定義一個接口或抽象類,并通過不同的實現類來擴展功能。
  2. 使用設計模式:策略模式、裝飾器模式、工廠模式等設計模式都可以幫助實現開閉原則。

示例代碼:

場景描述

假設我們要開發一個簡單的繪圖應用程序,該程序可以繪制不同的形狀(如圓形、矩形)。我們希望能夠在不修改現有代碼的情況下,輕松添加新的形狀。

違反開閉原則的設計

以下代碼在添加新形狀時需要修改 ShapeDrawer 類,違反了開閉原則:

class ShapeDrawer {public void draw(String shapeType) {if (shapeType.equals("circle")) {System.out.println("Drawing a circle");} else if (shapeType.equals("rectangle")) {System.out.println("Drawing a rectangle");}// 添加新形狀時需要修改此處代碼}
}public class Main {public static void main(String[] args) {ShapeDrawer drawer = new ShapeDrawer();drawer.draw("circle");drawer.draw("rectangle");}
}
遵循開閉原則的設計

我們可以使用策略模式,通過抽象類或接口來擴展新形狀,而不需要修改現有的代碼:

// 定義抽象類 Shape
abstract class Shape {public abstract void draw();
}// 圓形類
class Circle extends Shape {@Overridepublic void draw() {System.out.println("Drawing a circle");}
}// 矩形類
class Rectangle extends Shape {@Overridepublic void draw() {System.out.println("Drawing a rectangle");}
}// 使用策略模式的繪圖類
class ShapeDrawer {private Shape shape;public void setShape(Shape shape) {this.shape = shape;}public void drawShape() {shape.draw();}
}public class Main {public static void main(String[] args) {ShapeDrawer drawer = new ShapeDrawer();drawer.setShape(new Circle());drawer.drawShape();drawer.setShape(new Rectangle());drawer.drawShape();// 可以通過添加新類來擴展新形狀,而無需修改 ShapeDrawer 類class Triangle extends Shape {@Overridepublic void draw() {System.out.println("Drawing a triangle");}}drawer.setShape(new Triangle());drawer.drawShape();}
}

在這個示例代碼中,我們定義了一個抽象類 Shape,并為每種形狀創建一個具體實現類。ShapeDrawer 類通過組合一個 Shape 對象來繪制形狀。在需要添加新形狀時,只需創建一個新的形狀類并實現 Shape 抽象類,而無需修改 ShapeDrawer 類的代碼。這就實現了開閉原則。

開閉原則的優點

  1. 提高可維護性:減少了對已有代碼的修改,降低了引入新錯誤的風險。
  2. 提高可擴展性:通過擴展現有代碼來實現新功能,而不是修改現有代碼,增加了系統的靈活性。
  3. 提高復用性:抽象和實現分離,使得代碼更易于復用。

總結🍭

在軟件設計中,遵循設計原則有助于提高代碼的可維護性、可擴展性和復用性。

  1. 單一職責原則(SRP):
    1. 核心思想:每個類應當只有一個引起其變化的原因,即一個類只負責一項職責。
    2. 優勢:提高代碼的可讀性和可維護性,降低類之間的耦合度,增強系統的靈活性。
    3. 示例:將生成報告和打印報告的功能分離到不同的類中。
  2. 里氏替換原則(LSP):
    1. 核心思想:子類必須能夠替換基類而不影響程序的正確性。
    2. 優勢:確保子類能夠正確擴展基類功能,提高代碼的穩定性和可擴展性。
    3. 示例:通過接口和抽象類實現多態性,避免子類破壞基類的行為。
  3. 迪米特法則(LoD):
    1. 核心思想:一個對象應當盡可能少地了解其他對象,即只與直接的朋友通信。
    2. 優勢:降低對象之間的耦合度,提高系統的內聚性和可維護性。
    3. 示例:通過在 Car 類中添加方法來避免直接訪問內部對象的內部對象。
  4. 開閉原則(OCP):
    1. 核心思想:軟件實體應當對擴展開放,對修改關閉。
    2. 優勢:通過擴展現有代碼來實現新功能,而不是修改已有代碼,減少引入新錯誤的風險,提高系統的靈活性和可擴展性。
    3. 示例:使用策略模式,通過抽象類或接口來擴展新形狀,而不修改現有代碼。

在實際開發中,這些設計原則通常是相輔相成的。 例如,通過遵循單一職責原則,可以提高類的內聚性和可維護性,而結合里氏替換原則和開閉原則,可以設計出更靈活和可擴展的系統。此外,迪米特法則可以進一步降低類之間的耦合度,提高系統的健壯性。

通過理解和應用這些設計原則,可以構建出更高質量的軟件系統,減少后期維護的復雜性,并提高開發效率和代碼復用性。這些原則不僅適用于面向對象編程,也同樣適用于其他編程范式,是軟件開發過程中不可或缺的指導思想。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/21761.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/21761.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/21761.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

解讀信創產業根基,操作系統發展歷程

信創產業根基之一操作系統 操作系統是一個關鍵的控制程序,負責協調、管理和控制計算機硬件和軟件資源。作為硬件的首要軟件擴展,它位于裸機與用戶之間,充當了兩者之間的橋梁。通過其核心程序,操作系統高效地管理著系統中的各類資源…

static修飾變量和函數

static修飾的變量和函數只能在定義它的cpp源文件中使用,如果在頭文件中定義,則需要注意 在頭文件中定義static變量和static函數: 變量 如果在頭文件中定義了static變量,那么,所有包含這個頭文件的源文件都會定義自己…

vm-bhyve虛擬機安裝ubuntu22版本后進入grub無法啟動

問題:安裝ubuntu22版本后無法啟動 安裝好ubuntu22之后,重啟進入了grub模式,沒有自動啟動ubuntu 網上查了一下,這算一個通病。 問題解決 在grub模式下輸入boot命令: boot (lvm/ubuntu--vg-ubuntu--lv)/boot error: …

有哪些兼職軟件一天能賺幾十元?盤點十個能長期做下去的掙錢軟件

在當今這個信息泛濫的時代,眾人紛紛尋求迅速致富的捷徑。許多人在從事兼職或副業時,并不期望取得巨大的成就,只要每天能額外收入數十元,便已心滿意足。 今天,我將帶領大家深入探究,揭開那些隱藏在日常生活…

【小海實習日記】Git使用規范

1.Git使用流程 1.1 從master分支拉一個分支,命名要符合規范且清晰。 1.2 commit到本地,push 到遠端。 1.3 在Gitlab創建MR,選擇develp分支。 1.4 如果要修改的話,先把Gitlab上的MR修改為Draft(修改態),然后在本地修改代…

Dubbo中的Invoker與Exporter機制詳解

Dubbo作為一款成熟的高性能、輕量級的Java RPC框架,其核心機制之一便是Invoker與Exporter機制,它們在服務提供端和服務消費端扮演著至關重要的角色,是實現服務調用和管理的基礎。下面將詳細解析這兩個核心組件的工作原理及其在Dubbo框架中的作…

9.1.1 簡述目標檢測領域中的單階段模型和兩階段模型的性能差異及其原因

9.1目標檢測 場景描述 目標檢測(Object Detection)任務是計算機視覺中極為重要的基礎問題,也是解決實例分割(Instance Segmentation)、場景理解(Scene Understanding)、目標跟蹤(Ob…

詳解 Spark SQL 代碼開發之用戶自定義函數

一、UDF 一進一出函數 /**語法:SparkSession.udf.register(func_name: String, op: T > K) */ object TestSparkSqlUdf {def main(args: Array[String]): Unit {// 創建 sparksql 環境對象val conf new SparkConf().setMaster("local[*]").setAppNam…

subline text3安裝numpy,scipy,matplotlib,pandas,sklearn,ipynb

1,numpy(基礎數值算法) 安裝,要是在cmd直接安裝到最后會報錯, import numpy as np ModuleNotFoundError: No module named numpy 直接進入python環境,輸入python -m pip install numpy就不會報錯…

【SringBoot項目中MyBatis-Plus多數據源應用實踐】

文章目錄 前言 一、Mybatis-Plus是什么? 二、多數據源是什么? 三、使用步驟 1. 新建一個SpringBoot項目 2. 引入必要的MyBatis架包 3. 新建兩個數據庫及兩張表 3.3.1 新建數據庫:DB_A,并創建一張數據表alarm_kind,以及…

云端數據提取:安全、高效地利用無限資源

在當今的大數據時代,企業和組織越來越依賴于云平臺存儲和處理海量數據。然而,隨著數據的指數級增長,數據的安全性和高效的數據處理成為了企業最為關心的議題之一。本文將探討云端數據安全的重要性,并提出一套既高效又安全的數據提…

淺測 長亭雷池 WAF “動態防護”

本文首發于 Anyeの小站 前言 雷池 WAF 社區版的更新速度是真快啊,幾乎一周一個小版本,倆月一個大版本,攻城獅們真的狠啊,沒法測了。 廢話不多說,前兩天看到了 這篇文章,對雷池的“動態防護”功能挺感興趣…

Android應用的基本構造及威脅(apk)

目錄 APK文件是什么 apk文件解壓后的目錄結構 apk文件的存儲位置

去掉el-table表頭右側類名是gutter,width=17px的空白區域(包括表頭樣式及表格奇偶行樣式和表格自動滾動)

代碼如下&#xff1a; <el-table:data"tableData"ref"scroll_Table":header-cell-style"getRowClass":cell-style"styleBack"height"350px"style"width: 100%"><el-table-column prop"id" l…

Scrum團隊在迭代中如何處理計劃外的工作

認為 Scrum 團隊不做計劃其實是一個誤區&#xff0c;實際上很多 Scrum 團隊在沖刺計劃會議以及在細化工作項時均會進行詳細規劃。此外&#xff0c;他們還會創建一個路線圖&#xff0c;以便顯示他們在多個沖刺中的計劃。 Scrum 團隊需要經常進行計劃&#xff0c;以便在不斷變化…

linux學習:進程

目錄 例子1 獲取當前進程的進程標識符 例子2 創建一個新的子進程 例子3 展示了父進程和子進程的進程標識符 例子4 區分父進程和子進程 例子5 區分父進程和子進程的行為 例子6 比較進程標識符來區分父進程和子進程 例子7 子進程如何修改一個變量&…

混合動力電動汽車介紹(二)

接續前一章內容&#xff0c;本篇文章介紹混合動力汽車串聯、并聯和混聯的系統組成和工作原理。 一、串聯混合動力電動汽車的系統組成和工作原理 上圖為串聯混合動力電動汽車的結構簡圖。汽車由電動機-發電機驅動行駛&#xff0c;電機控制器的動力來自油箱-發動機-發電機-發電機…

Python 爬蟲零基礎:探索網絡數據的神秘世界

Python 爬蟲零基礎&#xff1a;探索網絡數據的神秘世界 在數字化時代&#xff0c;網絡數據如同無盡的寶藏&#xff0c;等待著我們去發掘。Python爬蟲&#xff0c;作為獲取這些數據的重要工具&#xff0c;正逐漸走進越來越多人的視野。對于零基礎的學習者來說&#xff0c;如何入…

基于Spring Boot框架的分頁查詢和文件上傳

分頁查詢 分析 要想從數據庫中進行分頁查詢&#xff0c;我們要使用LIMIT關鍵字&#xff0c;格式為&#xff1a;limit 開始索引 每頁顯示的條數 假設一頁想展示10條數據 查詢第1頁數據的SQL語句是&#xff1a; select * from emp limit 0,10; 查詢第2頁數據的SQL語句是&…

【Pytest官方文檔翻譯及學習】2.2 如何在測試中編寫和報告斷言

目錄 2.2 如何在測試中編寫和報告斷言 2.2.1 使用assert語句斷言 2.2.2 關于預期異常的斷言 2.2.3 關于預期警告的斷言 2.2.4 應用上下文相關的比較 2.2.5 為失敗的斷言定義自己的解釋 2.2.6 斷言內省細節 2.2 如何在測試中編寫和報告斷言 2.2.1 使用assert語句斷言 p…