自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Java編程中必知必會(huì)的五條SOLID原則

開發(fā) 后端
在本文中,我們介紹SOLID原則的核心內(nèi)容,并幫助您理解這些原則如何應(yīng)用到項(xiàng)目中。

在面向?qū)ο缶幊蹋∣OP)領(lǐng)域,SOLID原則是類設(shè)計(jì)的指導(dǎo)準(zhǔn)則。這五個(gè)原則形成了一套規(guī)則和最佳實(shí)踐,開發(fā)人員在設(shè)計(jì)類結(jié)構(gòu)時(shí)應(yīng)遵循這些原則。通過理解和應(yīng)用這些原則,我們可以發(fā)揮出設(shè)計(jì)模式的潛力,創(chuàng)建強(qiáng)大的軟件架構(gòu)。

在本文中,我們介紹SOLID原則的核心內(nèi)容,并幫助您理解這些原則如何應(yīng)用到項(xiàng)目中。

核心目標(biāo):編寫可理解、無回歸和可測試的代碼

SOLID原則的核心目標(biāo)是構(gòu)建可理解、無回歸、可讀和可測試的代碼。這些代碼作為一個(gè)畫布,多個(gè)開發(fā)人員可以輕松地進(jìn)行協(xié)作,促進(jìn)生產(chǎn)力和創(chuàng)新的環(huán)境。

現(xiàn)在,我們逐個(gè)深入討論SOLID原則,揭示它們?cè)谒茉祛愒O(shè)計(jì)中的重要性。

  • S — 單一責(zé)任原則
  • O — 開放封閉原則
  • L — 里氏替換原則
  • I — 接口隔離原則
  • D — 依賴反轉(zhuǎn)原則

1. 單一責(zé)任原則

SOLID的第一個(gè)支柱,單一責(zé)任原則(SRP),強(qiáng)調(diào)一個(gè)類應(yīng)該只有一個(gè)變化的原因。遵循這個(gè)原則,我們確保每個(gè)類只負(fù)責(zé)一個(gè)任務(wù),可使將來的維護(hù)和修改更容易。

/*手機(jī)類,具有屬性和構(gòu)造函數(shù)*/
Class MobilePhone{
    String brandName;
    Float price;
    Date manufactureDate;
  public MobilePhone(String brandName,Float price,Date manufactureDate){
      this.brandName=brandName;
      this.price=price;
      this.manufactureDate=manufactureDate;
  }
};

/*Invoice類擁有一個(gè)手機(jī)(mobile phone)和該手機(jī)的數(shù)量(quantity)*/
Class Invoice{
    private MobilePhone mPhone;
    int quantity;
  public Invoice(MobilePhone mPhone,quantity){
    this.mPhone=mPhone;
    this.quantity=quantity;
  }
  public float calculateTotalPrice(){
    return mPhone.price*this.quantity;//返回發(fā)票的總金額
  }
  public void printInvoice(){
    //打印發(fā)票的邏輯
  }
  public void sendNotification(){
    //發(fā)送通知的邏輯
  }

}

以上代碼,與計(jì)算、打印或通知邏輯相關(guān)的任何更改都需要修改Invoice類。因此,發(fā)票類缺乏明確的關(guān)注點(diǎn)分離,對(duì)一個(gè)方面的修改會(huì)影響到其他功能。為了遵循SRP,關(guān)鍵是將Invoice類重構(gòu)為更小、更專注的類, 每個(gè)類都獨(dú)立處理特定的職責(zé),比如計(jì)算、打印或通知邏輯。

根據(jù)職責(zé)將代碼分離成獨(dú)立的類是遵循單一責(zé)任原則(SRP)并促進(jìn)可維護(hù)和靈活的代碼庫的正確方法。

在這種設(shè)計(jì)中,我們應(yīng)該有:

  • Invoice類只包含計(jì)算邏輯。
  • InvoicePrint類只包含打印邏輯。
  • InvoiceNotify類只包含發(fā)送通知的邏輯。
/*手機(jī)類,具有屬性和構(gòu)造函數(shù)*/
Class MobilePhone{
    String brandName;
    Float price;
    Date manufactureDate;
  public MobilePhone(String brandName,Float price,Date manufactureDate){
      this.brandName=brandName;
      this.price=price;
      this.manufactureDate=manufactureDate;
  }
};

/*發(fā)票類,擁有手機(jī)和數(shù)量屬性*/
Class Invoice{
    private MobilePhone mPhone;
    int quantity;
  public Invoice(MobilePhone mPhone,quantity){
    this.mPhone=mPhone;
    this.quantity=quantity;
  }
  public float calculateTotalPrice(){
    return mPhone.price*this.quantity;//返回發(fā)票的總金額
  }
}

Class InvoicePrint{
  private Invoice invoice;
  
  public InvoicePrint(Invoice invoice){
  this.invoice=invoice
  }
  public void printInvoice(){
    //打印發(fā)票的邏輯
  }
}/*如果打印邏輯發(fā)生變化,只有InvoicePrint類會(huì)發(fā)生變化。*/
Class InvoiceNotify{
  private Invoice invoice;
  
  public InvoiceNotify(Invoice invoice){
  this.invoice=invoice
  public void sendNotification(){
    //發(fā)送通知給用戶的邏輯
  }
}/*如果通知邏輯發(fā)生變化,只有InvoiceNotify類會(huì)發(fā)生變化。*/

2. 開放-封閉原則

第二個(gè)原則是開放-封閉原則(OCP),它鼓勵(lì)軟件實(shí)體對(duì)擴(kuò)展開放,但對(duì)修改封閉。換句話說,一旦一個(gè)類被建立,它應(yīng)該能夠輕松地進(jìn)行擴(kuò)展,而不需要修改其現(xiàn)有的代碼。這促進(jìn)了代碼的重用和穩(wěn)定性。

讓我們以上面使用的InvoiceNotify類為例,InvoiceNotify類經(jīng)過測試,并且當(dāng)前在客戶端中實(shí)際使用,通過電子郵件發(fā)送發(fā)票通知。

現(xiàn)在有一個(gè)客戶需求,他們需要通過推送通知發(fā)送通知。

Class InvoiceNotify{
  private Invoice invoice;
  
  public InvoiceNotify(Invoice invoice){
  this.invoice=invoice
  public void sendNotification(){
    //發(fā)送通知給用戶的邏輯
  }
  public void sendPushNotification(){
    //發(fā)送推送通知給用戶的邏輯
  }
}

以上代碼,通過在現(xiàn)有類中添加一個(gè)新方法,我們違反了開放/封閉原則

與其在現(xiàn)有類中添加一個(gè)新方法,我們應(yīng)該設(shè)計(jì)一個(gè)接口并在各個(gè)類中實(shí)現(xiàn)。

Interface InvoiceNotification{
 public void sendNotification();
}
Class EmailNotification implements InvoiceNotification{
 private Invoice invoice;
  public EmailNotification(Invoice invoice){
     this.invoice=invoice;
  }
  @Override
  public void sendNotification(){
  //通過電子郵件發(fā)送通知的邏輯
  }
}
Class PushNotification implements InvoiceNotification{
 private Invoice invoice;
  public PushNotification(Invoice invoice){
     this.invoice=invoice;
  }
  @Override
  public void sendNotification(){
  //發(fā)送推送通知的邏輯
  }
}

如果進(jìn)一步增強(qiáng)需求,需要通過短信發(fā)送通知,無需修改現(xiàn)有類。相反,我們可以創(chuàng)建一個(gè)名為TextNotification的新類,它實(shí)現(xiàn)了InvoiceNotification接口并重寫了sendNotification()方法。這樣,我們就能夠順利地集成新功能,而不會(huì)破壞現(xiàn)有的代碼庫。

3. 里氏替換原則

里氏替換原則(LSP)定義了基類和派生類之間的契約。它規(guī)定派生類應(yīng)該能夠替代其基類,而不會(huì)影響程序的正確性。實(shí)質(zhì)上,遵循這個(gè)原則可以確保繼承被謹(jǐn)慎地使用,并保持類層次結(jié)構(gòu)的完整性。

例如:在數(shù)學(xué)中,正方形可以被歸類為矩形的一種特殊形式。它們之間的“是一個(gè)”關(guān)系可能會(huì)導(dǎo)致我們考慮在代碼中使用繼承來建模這種關(guān)系。然而,將正方形實(shí)現(xiàn)為矩形的派生類可能會(huì)導(dǎo)致意外的行為。

在數(shù)學(xué)中,正方形確實(shí)是矩形的一種特殊形式,正如“是一個(gè)”關(guān)系所暗示的那樣。這往往會(huì)引誘我們?cè)诖a中使用繼承來建模這種關(guān)系。然而,將正方形實(shí)現(xiàn)為矩形的派生類可能會(huì)導(dǎo)致意想不到和違反直覺的行為。

我們用一個(gè)簡單的Java代碼示例來說明這個(gè)問題:

class Rectangle {
    protected int width;
    protected int height;

    public void setWidth(int width) {
        this.width = width;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    public int calculateArea() {
        return width * height;
    }
}

class Square extends Rectangle {
    @Override
    public void setWidth(int width) {
        this.width = width;
        this.height = width; // 正方形的邊長始終相等,所以兩個(gè)維度都設(shè)置為相同的值。
    }

    @Override
    public void setHeight(int height) {
        this.height = height;
        this.width = height; // 正方形的邊長始終相等,所以兩個(gè)維度都設(shè)置為相同的值。
    }
}

public class Main {
    public static void main(String[] args) {
        Rectangle rectangle = new Square();
        rectangle.setWidth(5);
        rectangle.setHeight(3);

        System.out.println("Area: " + rectangle.calculateArea());
    }
}

在這個(gè)例子中,我們有一個(gè)基類Rectangle,其中包含setWidth和setHeight方法,分別用于設(shè)置矩形的寬度和高度。Square類繼承Rectangle類,并重寫這些方法,以確保兩個(gè)維度保持相等,以保持正方形的特性。

在主方法中,我們創(chuàng)建一個(gè)Rectangle引用,指向一個(gè)Square對(duì)象。當(dāng)我們嘗試為寬度和高度設(shè)置不同的值(分別為5和3)時(shí),我們得到了一個(gè)邊長為3的正方形,而不是實(shí)際寬度為5、高度為3的矩形。因此,計(jì)算得到的面積(9)與我們期望從寬度為5、高度為3的矩形得到的面積不符。

這個(gè)場景展示了里氏替換原則被違反的情況,通過Rectangle引用使用Square對(duì)象導(dǎo)致了意外的行為。

為了解決Square繼承Rectangle的問題,我們需要重新評(píng)估繼承關(guān)系和類設(shè)計(jì)。一種方法是在這種情況下避免使用繼承,而是專注于公共接口或組合。我們用Java代碼來說明解決方案:

interface Shape {
    int calculateArea();
}

class Rectangle implements Shape {
    protected int width;
    protected int height;

    public void setWidth(int width) {
        this.width = width;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    @Override
    public int calculateArea() {
        return width * height;
    }
}

class Square implements Shape {
    protected int side;

    public void setSide(int side) {
        this.side = side;
    }

    @Override
    public int calculateArea() {
        return side * side;
    }
}

public class Main {
    public static void main(String[] args) {
        Shape rectangle = new Rectangle();
        rectangle.setWidth(5);
        rectangle.setHeight(3);
        System.out.println("矩形面積: " + rectangle.calculateArea());

        Shape square = new Square();
        square.setSide(5);
        System.out.println("正方形面積: " + square.calculateArea());
    }
}

在這個(gè)解決方案中,我們引入了一個(gè)名為Shape的公共接口,定義了calculateArea()方法。現(xiàn)在,Rectangle和Square都實(shí)現(xiàn)了這個(gè)接口。Rectangle類保留了setWidth和setHeight方法,而Square類有一個(gè)setSide方法。每個(gè)類根據(jù)自己特定的屬性計(jì)算面積。

現(xiàn)在,在main方法中,我們?yōu)镽ectangle和Square對(duì)象分別創(chuàng)建了不同的Shape引用。我們可以適當(dāng)設(shè)置尺寸而不會(huì)遇到任何問題。

通過使用組合和共同接口,我們確保每個(gè)形狀都能獨(dú)立運(yùn)作,并且按預(yù)期運(yùn)行,而不違反里氏替換原則。這種設(shè)計(jì)使我們能夠優(yōu)雅地處理不同的形狀,促進(jìn)了更清晰和可維護(hù)的代碼庫。

4. 接口隔離原則

接口隔離原則(ISP)建議客戶端不應(yīng)被強(qiáng)迫依賴于它們不使用的接口。與其擁有龐大而笨重的接口,更好的做法是創(chuàng)建小而專注的接口,以滿足客戶端的特定需求。

讓我們通過一個(gè)簡單的Java代碼示例來說明ISP:

假設(shè)我們有一個(gè)名為Printer的接口,提供打印功能:

interface DocumentProcessor {
    void print();
    void fax();
}

class LaserPrinter implements DocumentProcessor {
    @Override
    public void print() {
        System.out.println("Printing with a laser printer.");
    }

    @Override
    public void fax() {
        System.out.println("Sending a fax with a laser printer.");
    }
}

class FaxMachine implements DocumentProcessor {
    @Override
    public void print() {
        // 傳真機(jī)無法打印,所以將這個(gè)方法保持為空。
    }

    @Override
    public void fax() {
        System.out.println("Sending a fax with a fax machine.");
    }
}

這個(gè)設(shè)計(jì)的問題在于FaxMachine類對(duì)于print()方法沒有有意義的實(shí)現(xiàn),因?yàn)閭髡鏅C(jī)無法打印文檔。盡管如此,F(xiàn)axMachine類仍然被強(qiáng)制實(shí)現(xiàn)print()方法,這是因?yàn)镈ocumentProcessor接口的設(shè)計(jì)。

這種對(duì)接口隔離原則的違反顯而易見,因?yàn)镕axMachine類現(xiàn)在需要實(shí)現(xiàn)它不需要或使用的方法。

5. 依賴反轉(zhuǎn)原則

SOLID原則的最后一塊拼圖是依賴反轉(zhuǎn)原則(Dependency Inversion Principle,DIP)。該原則主張高層模塊不應(yīng)依賴于低層模塊,而應(yīng)依賴于抽象。通過遵循這一原則,我們實(shí)現(xiàn)了解耦,從而增強(qiáng)了靈活性、可維護(hù)性和測試的便捷性。

讓我們通過一個(gè)小的Java代碼示例來說明違反依賴反轉(zhuǎn)原則的情況:

假設(shè)我們有一個(gè)ReportGenerator類,它直接依賴于一個(gè)DatabaseConnection類:

class DatabaseConnection {
    public void connect() {
        System.out.println("Connected to the database.");
    }

    public void executeQuery(String query) {
        System.out.println("Executing query: " + query);
    }

    public void close() {
        System.out.println("Connection closed.");
    }
}

class ReportGenerator {
    private DatabaseConnection databaseConnection;

    public ReportGenerator() {
        this.databaseConnection = new DatabaseConnection();
    }

    public void generateReport() {
        databaseConnection.connect();
        databaseConnection.executeQuery("SELECT * FROM data_table");
        databaseConnection.close();
        System.out.println("Report generated successfully.");
    }
}

在這段代碼中,ReportGenerator類在其構(gòu)造函數(shù)中直接創(chuàng)建了一個(gè)DatabaseConnection實(shí)例。結(jié)果,ReportGenerator與DatabaseConnection緊密耦合。對(duì)DatabaseConnection類的任何更改都可能會(huì)影響到ReportGenerator。

為了解決這個(gè)問題,我們需要應(yīng)用依賴反轉(zhuǎn)原則,引入一個(gè)兩個(gè)類都依賴的接口:

interface Connection {
    void connect();
    void executeQuery(String query);
    void close();
}

class DatabaseConnection implements Connection {
    @Override
    public void connect() {
        System.out.println("Connected to the database.");
    }

    @Override
    public void executeQuery(String query) {
        System.out.println("Executing query: " + query);
    }

    @Override
    public void close() {
        System.out.println("Connection closed.");
    }
}

class ReportGenerator {
    private Connection connection;

    public ReportGenerator(Connection connection) {
        this.connection = connection;
    }

    public void generateReport() {
        connection.connect();
        connection.executeQuery("SELECT * FROM data_table");
        connection.close();
        System.out.println("Report generated successfully.");
    }
}

public class Main {
    public static void main(String[] args) {
        Connection databaseConnection = new DatabaseConnection();
        ReportGenerator reportGenerator = new ReportGenerator(databaseConnection);

        reportGenerator.generateReport();
    }
}

通過遵循依賴反轉(zhuǎn)原則,我們通過Connection接口解耦了ReportGenerator和DatabaseConnection類。這種方法允許我們?cè)诓恍薷腞eportGenerator的情況下輕松切換和擴(kuò)展Connection接口的實(shí)現(xiàn)。現(xiàn)在的代碼符合原則,更易于維護(hù)和靈活。

結(jié)論

SOLID原則是面向?qū)ο箢愒O(shè)計(jì)的基石,對(duì)于每個(gè)尋求創(chuàng)建高效、可維護(hù)和協(xié)作的軟件的開發(fā)人員來說至關(guān)重要。當(dāng)你踏上編碼之旅時(shí),請(qǐng)記住SOLID運(yùn)用原則!

責(zé)任編輯:趙寧寧 來源: Java學(xué)研大本營
相關(guān)推薦

2024-06-19 10:08:34

GoChannel工具

2015-08-17 16:05:35

javascript對(duì)象編程

2010-08-04 14:32:33

求職

2023-12-26 12:10:13

2020-07-10 07:58:14

Linux

2024-01-10 18:01:22

編程技巧Java 12

2024-11-15 11:11:48

2024-01-03 07:56:50

2022-05-18 09:01:19

JSONJavaScript

2018-10-26 14:10:21

2023-05-08 15:25:19

Python編程語言編碼技巧

2023-04-20 14:31:20

Python開發(fā)教程

2022-08-19 10:31:32

Kafka大數(shù)據(jù)

2024-07-26 08:32:44

panic?Go語言

2023-10-19 21:30:36

架構(gòu)CQRS模式

2021-01-20 15:43:01

機(jī)器學(xué)習(xí)深度學(xué)習(xí)科學(xué)

2020-08-23 18:18:27

Python列表數(shù)據(jù)結(jié)構(gòu)

2021-06-09 11:06:00

數(shù)據(jù)分析Excel

2015-10-20 09:46:33

HTTP網(wǎng)絡(luò)協(xié)議

2019-01-30 14:14:16

LinuxUNIX操作系統(tǒng)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)