新聞中心
說到 SOLID 原則,可能寫過代碼的同學(xué)們應(yīng)該都聽過吧,這是程序設(shè)計領(lǐng)域最常用到的設(shè)計原則。SOLID 由 羅伯特·C·馬丁 在 21 世紀(jì)早期引入,指代了面向?qū)ο缶幊毯兔嫦驅(qū)ο笤O(shè)計的五個基本原則, SOLID 其實(shí)是以下五個單詞的縮寫:

網(wǎng)站建設(shè)哪家好,找成都創(chuàng)新互聯(lián)!專注于網(wǎng)頁設(shè)計、網(wǎng)站建設(shè)、微信開發(fā)、重慶小程序開發(fā)、集團(tuán)企業(yè)網(wǎng)站建設(shè)等服務(wù)項(xiàng)目。為回饋新老客戶創(chuàng)新互聯(lián)還提供了文成免費(fèi)建站歡迎大家使用!
- Single Responsibility Principle:單一職責(zé)原則
- Open Closed Principle:開閉原則
- Liskov Substitution Principle:里氏替換原則
- Interface Segregation Principle:接口隔離原則
- Dependency Inversion Principle:依賴倒置原則
TypeScript 的出現(xiàn)讓我們可以用面向?qū)ο蟮乃枷刖帉懗龈啙嵉?JavaScript 代碼,在下面的文章中,我們將用 TypeScript 編寫一些示例來分別解釋下這些原則。
單一職責(zé)原則(SRP)
核心思想:類的職責(zé)應(yīng)該單一,不要承擔(dān)過多的職責(zé)。
我們先看看下面這段代碼,我們?yōu)?Book 創(chuàng)建了一個類,但是類中卻承擔(dān)了多個職責(zé),比如把書保存為一個文件:
class Book {
public title: string;
public author: string;
public description: string;
public pages: number;
// constructor and other methods
public saveToFile(): void {
// some fs.write method to save book to file
}
}遵循單一職責(zé)原則,我們應(yīng)該創(chuàng)建兩個類,分別負(fù)責(zé)不同的事情:
class Book {
public title: string;
public author: string;
public description: string;
public pages: number;
// constructor and other methods
}
class Persistence {
public saveToFile(book: Book): void {
// some fs.write method to save book to file
}
}好處:降低類的復(fù)雜度、提高可讀性、可維護(hù)性、擴(kuò)展性、最大限度的減少潛在的副作用。
開閉原則(OCP)
核心思想:類應(yīng)該對擴(kuò)展開放,但對修改關(guān)閉。簡單理解就是當(dāng)別人要修改軟件功能的時候,不能讓他修改我們原有代碼,盡量讓他在原有的基礎(chǔ)上做擴(kuò)展。
先看看下面這段寫的不太好的代碼,我們單獨(dú)封裝了一個 AreaCalculator 類來負(fù)責(zé)計算 Rectangle 和 Circle 類的面積。想象一下,如果我們后續(xù)要再添加一個形狀,我們要創(chuàng)建一個新的類,同時我們也要去修改 AreaCalculator 來計算新類的面積,這違反了開閉原則。
class Rectangle {
public width: number;
public height: number;
constructor(width: number, height: number) {
this.width = width;
this.height = height;
}
}
class Circle {
public radius: number;
constructor(radius: number) {
this.radius = radius;
}
}
class AreaCalculator {
public calculateRectangleArea(rectangle: Rectangle): number {
return rectangle.width * rectangle.height;
}
public calculateCircleArea(circle: Circle): number {
return Math.PI * (circle.radius * circle.radius);
}
}為了遵循開閉原則,我們只需要添加一個名為 Shape 的接口,每個形狀類(矩形、圓形等)都可以通過實(shí)現(xiàn)它來依賴該接口。通過這種方式,我們可以將 AreaCalculator 類簡化為一個帶有參數(shù)的函數(shù),每當(dāng)我們創(chuàng)建一個新的形狀類,都必須實(shí)現(xiàn)這個函數(shù),這樣就不需要修改原有的類了:
interface Shape {
calculateArea(): number;
}
class Rectangle implements Shape {
public width: number;
public height: number;
constructor(width: number, height: number) {
this.width = width;
this.height = height;
}
public calculateArea(): number {
return this.width * this.height;
}
}
class Circle implements Shape {
public radius: number;
constructor(radius: number) {
this.radius = radius;
}
public calculateArea(): number {
return Math.PI * (this.radius * this.radius);
}
}
class AreaCalculator {
public calculateArea(shape: Shape): number {
return shape.calculateArea();
}
}
里氏替換原則(LSP)
核心思想:在使用基類的的地方可以任意使用其子類,能保證子類完美替換基類。簡單理解就是所有父類能出現(xiàn)的地方,子類就可以出現(xiàn),并且替換了也不會出現(xiàn)任何錯誤。
我們必須要求子類的所有相同方法,都必須遵循父類的約定,否則當(dāng)父類替換為子類時就會出錯。
先來看看下面這段代碼,Square 類擴(kuò)展了 Rectangle 類。但是這個擴(kuò)展沒有任何意義,因?yàn)槲覀兺ㄟ^覆蓋寬度和高度屬性來改變了原有的邏輯。
class Rectangle {
public width: number;
public height: number;
constructor(width: number, height: number) {
this.width = width;
this.height = height;
}
public calculateArea(): number {
return this.width * this.height;
}
}
class Square extends Rectangle {
public _width: number;
public _height: number;
constructor(width: number, height: number) {
super(width, height);
this._width = width;
this._height = height;
}
}遵循里氏替換原則,我們不需要覆蓋基類的屬性,而是直接刪除掉 Square 類并,將它的邏輯帶到 Rectangle 類,而且也不改變其用途。
class Rectangle {
public width: number;
public height: number;
constructor(width: number, height: number) {
this.width = width;
this.height = height;
}
public calculateArea(): number {
return this.width * this.height;
}
public isSquare(): boolean {
return this.width === this.height;
}
}好處:增強(qiáng)程序的健壯性,即使增加了子類,原有的子類還可以繼續(xù)運(yùn)行。
接口隔離原則(ISP)
核心思想:類間的依賴關(guān)系應(yīng)該建立在最小的接口上。簡單理解就是接口的內(nèi)容一定要盡可能地小,能有多小就多小。我們要為各個類建立專用的接口,而不要試圖去建立一個很龐大的接口供所有依賴它的類去調(diào)用。
看看下面的代碼,我們有一個名為 Troll 的類,它實(shí)現(xiàn)了一個名為 Character 的接口,但是 Troll 既不會游泳也不會說話,所以它似乎不太適合實(shí)現(xiàn)我們的接口:
interface Character {
shoot(): void;
swim(): void;
talk(): void;
dance(): void;
}
class Troll implements Character {
public shoot(): void {
// some method
}
public swim(): void {
// a troll can't swim
}
public talk(): void {
// a troll can't talk
}
public dance(): void {
// some method
}
}遵循接口隔離原則,我們刪除 Character 接口并將它的功能拆分為四個接口,然后我們的 Troll 類只需要依賴于我們實(shí)際需要的這些接口。
interface Talker {
talk(): void;
}
interface Shooter {
shoot(): void;
}
interface Swimmer {
swim(): void;
}
interface Dancer {
dance(): void;
}
class Troll implements Shooter, Dancer {
public shoot(): void {
// some method
}
public dance(): void {
// some method
}
}
依賴倒置原則(DIP)
核心思想:依賴一個抽象的服務(wù)接口,而不是去依賴一個具體的服務(wù)執(zhí)行者,從依賴具體實(shí)現(xiàn)轉(zhuǎn)向到依賴抽象接口,倒置過來。
看看下面這段代碼,我們有一個 SoftwareProject 類,它初始化了 FrontendDeveloper 和 BackendDeveloper 類:
class FrontendDeveloper {
public writeHtmlCode(): void {
// some method
}
}
class BackendDeveloper {
public writeTypeScriptCode(): void {
// some method
}
}
class SoftwareProject {
public frontendDeveloper: FrontendDeveloper;
public backendDeveloper: BackendDeveloper;
constructor() {
this.frontendDeveloper = new FrontendDeveloper();
this.backendDeveloper = new BackendDeveloper();
}
public createProject(): void {
this.frontendDeveloper.writeHtmlCode();
this.backendDeveloper.writeTypeScriptCode();
}
}
遵循依賴倒置原則,我們創(chuàng)建一個 Developer 接口,由于 FrontendDeveloper 和 BackendDeveloper 是相似的類,它們都依賴于 Developer 接口。
我們不需要在 SoftwareProject 類中以單一方式初始化 FrontendDeveloper 和 BackendDeveloper,而是將它們作為一個列表來遍歷它們,分別調(diào)用每個 develop() 方法。
interface Developer {
develop(): void;
}
class FrontendDeveloper implements Developer {
public develop(): void {
this.writeHtmlCode();
}
private writeHtmlCode(): void {
// some method
}
}
class BackendDeveloper implements Developer {
public develop(): void {
this.writeTypeScriptCode();
}
private writeTypeScriptCode(): void {
// some method
}
}
class SoftwareProject {
public developers: Developer[];
public createProject(): void {
this.developers.forEach((developer: Developer) => {
developer.develop();
});
}
}
好處:實(shí)現(xiàn)模塊間的松耦合,更利于多模塊并行開發(fā)。
當(dāng)前標(biāo)題:基于TypeScript理解程序設(shè)計的SOLID原則
分享鏈接:http://www.5511xx.com/article/copcgcg.html


咨詢
建站咨詢
