首頁  >  文章  >  Java  >  在 Typescript 和 Java 中應用“里氏替換原則”

在 Typescript 和 Java 中應用“里氏替換原則”

PHPz
PHPz原創
2024-08-30 06:01:32798瀏覽

Aplicando o

概念

介面

介面定義類別必須實現的契約或一組方法和屬性。介面用於確保類別遵循某種格式,但它們不提供方法的實現,僅提供方法的簽名。

每當一個類別實作一個介面時,它就會簽署該介面的所有契約(方法和屬性)。每個屬性和方法都是強制實現的。

堅硬的

SOLID 是一個縮寫詞,代表物件導向程式設計的五個基本原則,由 Robert C. Martin(鮑伯大叔)提出。在這裡您可以閱讀有關他的文章的更多資訊。
這些原則旨在改進程式碼的結構和維護,使其更加靈活、可擴展且更易於理解。這些原則可以幫助程式設計師創建更有組織的程式碼、劃分職責、減少依賴、簡化重構過程並促進程式碼重複使用。

關於LSP

縮寫中的「L」代表「里氏替換原理」。 Bob叔叔用來定義這個原則的一句話是:

「衍生類別必須能夠完全替換基底類別」

因此建議衍生類別應該盡可能接近基底類別,這樣派生類別就可以在程式碼不做任何修改的情況下替換其基底類別。

此原則由 Barbara Liskov 於 1988 年基於資料抽象化和類型理論提出。源自契約式設計 (DBC) 的概念,由 Bertrand Meyer 於 1986 年推廣。

這項原則的另一個具體說明是:

子類型應該用作您的基本類型,沒有任何意外。

在程式設計中,變化和意外可能會導致問題。如果需要更換某項系統功能,新功能必須提供相同類型的信息,否則系統可能會發生故障。為了確保類別 S 具有與基底類別 T 相同的行為,必須使用定義實作新功能的強制方法的契約(介面或抽象類別),以確保類別 S 之間相似性的完整性和 T 類別。

實際應用

考慮一個帶有 Fly() 方法的 Bird 基類,該方法將在兩個子類中使用:Sparrow 和 Ostrich。

檔:bird.java

class Bird {
    void fly() {
        System.out.println("I can fly!");
    }
}

class Sparrow extends Bird {
    // Herda o comportamento de 'fly' da classe 'Bird'
}

class Ostrich extends Bird {
    @Override
    void fly() {
        throw new UnsupportedOperationException("I cannot fly");
    }
}

文件:bird.ts

class Bird {
  fly() {
    console.log("I can fly!");
  }
}

class Sparrow extends Bird {}

class Ostrich extends Bird {
  fly() {
    throw new Error("I cannot fly");
  }
}

遇到的問題

在這裡,Sparrow 類遵循 LSP,因為麻雀確實可以飛。然而,Ostrich 類別違反了 LSP,因為它重寫了 voo() 方法,從根本上改變了其行為,打破了 Ave 類別設定的期望。

如何修復?

我們需要透過將Sparrow 和Ostrich 類別的每個特殊性劃分為合約(介面或抽象類,這裡我將使用介面)來應用LSP,它們必須簽署這些合約來調整每個類別的行為:

檔:bird.java

interface Bird {
    String getName();
    void makeSound();
}

interface FlyingBird extends Bird {
    void fly();
}

class Sparrow implements FlyingBird {
    private String name;

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

    @Override
    public String getName() {
        return this.name;
    }

    @Override
    public void makeSound() {
        System.out.println("Chirp chirp!");
    }

    @Override
    public void fly() {
        System.out.println(this.name + " is flying!");
    }
}

class Ostrich implements Bird {
    private String name;

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

    @Override
    public String getName() {
        return this.name;
    }

    @Override
    public void makeSound() {
        System.out.println("Boom boom!");
    }
}

public class Main {
    public static void main(String[] args) {
        Sparrow sparrow = new Sparrow("Little Sparrow");
        sparrow.makeSound(); // Chirp chirp!
        sparrow.fly(); // Little Sparrow is flying!

        Ostrich ostrich = new Ostrich("Ostrich");
        ostrich.makeSound(); // Boom boom!
        ostrich.fly(); // Error: Method 'fly' does not exist on 'Ostrich'
    }
}

文件:bird.ts

interface Bird {
  name: string;
  makeSound(): void;
}

interface FlyingBird extends Bird {
  fly(): void;
}

class Sparrow implements FlyingBird {
  name: string;

  constructor(name: string) {
    this.name = name;
  }

  makeSound() {
    console.log("Chirp chirp!");
  }

  fly() {
    console.log(`${this.name} is flying!`);
  }
}

class Ostrich implements Bird {
  name: string;

  constructor(name: string) {
    this.name = name;
  }

  makeSound() {
    console.log("Boom boom!");
  }
}

const sparrow = new Sparrow("Little Sparrow");
sparrow.makeSound(); // Chirp chirp!
sparrow.fly(); // Little Sparrow is flying!

const ostrich = new Ostrich("Ostrich");
ostrich.makeSound(); // Boom boom!
ostrich.fly(); // Error: Method 'fly' does not exist on 'Ostrich'

分析

更正說明
Bird Interface:定義所有鳥類共有的行為,例如makeSound()。所有鳥類都必須實作此介面。

FlyingBird 介面:繼承自 Ave 並添加 Fly() 行為,該行為特定於會飛的鳥類。

Sparrow 類別:實作 FlyingBird 接口,因為麻雀可以飛。此類定義了發出聲音和飛行的行為。

鴕鳥類:僅實現 Bird 接口,因為鴕鳥不會飛。類別沒有 Fly() 方法,因此不違反 LSP。

結論

LSP 對於確保程式碼模組化、可重複使用且易於維護至關重要。違反 LSP 可能會導致脆弱的程式碼在引入新子類別或修改現有子類別時中斷,因為這可能會導致依賴超類別的部分程式碼出現意外行為。

子類型替換允許模組無需修改即可擴展,這對於開閉原則 (OCP) 提供的靈活性至關重要,而里氏替換原則使之成為可能。契約(透過介面或抽象類別實現)對於安全設計至關重要,但程式設計師必須充分理解它們,有助於避免遺留軟體中的常見錯誤。他們還提供了有關如何實施和使用程式碼的寶貴指導,只需遵守相關合約即可。

實際意義

  1. 設計子類別時,請確保它們可以在使用其超類別的任何地方使用,而不會引入錯誤或需要特殊處理。
  2. 避免創建違反超類預期行為的子類,因為這可能會導致維護問題和意外錯誤。

理解並應用里氏替換原則可以幫助開發人員創建更可預測和穩定的物件導向系統。

以上是在 Typescript 和 Java 中應用“里氏替換原則”的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn