TypeScript 是否支援所有面向物件原則?


在程式設計世界中,面向物件原則為設計和構建軟體系統提供了堅實的基礎。面向物件程式設計 (OOP) 語言使開發人員能夠將現實世界中的實體建模為物件,封裝資料和行為,並在物件之間建立關係。TypeScript 作為 JavaScript 的超集,為該語言引入了靜態型別,並提供了許多支援面向物件程式設計的功能。在本教程中,我們將探討各種場景,以瞭解 TypeScript 與面向物件程式設計的核心原則的契合程度。

封裝

封裝是指將資料和方法捆綁到一個單元(稱為類)中,並隱藏內部實現細節。TypeScript 透過類和訪問修飾符完全支援封裝。

在下面的示例中,Car 類有兩個屬性(品牌和年份),並用訪問修飾符標記。private 修飾符限制了對類內品牌的訪問,而 protected 修飾符允許子類訪問年份屬性。startEngine 方法定義為 public,使其可以從類外部訪問。並且我們建立了一個 Car 類的例項並呼叫了 startEngine 方法。

class Car {
   private brand: string;
   protected year: number;    
   constructor(brand: string, year: number) {
      this.brand = brand;
      this.year = year;
   }    
   public startEngine() {
      console.log(`Starting the ${this.brand} engine...`);
   }
}
const myCar = new Car("Tesla", 2023);
myCar.startEngine();

編譯後,上述程式碼將生成以下 JavaScript 程式碼:

var Car = /** @class */ (function () {
   function Car(brand, year) {
      this.brand = brand;
      this.year = year;
   }
   Car.prototype.startEngine = function () {
      console.log("Starting the ".concat(this.brand, " engine..."));
   };
   return Car;
}());
var myCar = new Car("Tesla", 2023);
myCar.startEngine();

輸出

Starting the Tesla engine...

在此示例中,我們將品牌和年份資訊封裝在 Car 類中,並公開 startEngine 方法以供外部使用。封裝原則確保類的內部細節對外部世界隱藏,從而促進模組化和可維護的程式碼。

繼承

繼承是面向物件程式設計中的一個基本概念,它允許一個類繼承另一個類的屬性和方法。TypeScript 為繼承提供了強大的支援,使開發人員能夠在類之間構建層次結構關係。

在此示例中,Dog 類使用 extends 關鍵字擴充套件了 Animal 類。因此,Dog 類繼承了 Animal 的 name 屬性,並定義了自己的 bark 方法。並且我們建立了一個 Dog 類的例項並呼叫了 move 和 bark 方法。

class Animal {
   protected name: string;    
   constructor(name: string) {
      this.name = name;
   }    
   public move() {
      console.log(`${this.name} is moving...`);
   }
}
class Dog extends Animal {
   public bark() {
      console.log(`${this.name} is barking...`);
   }
}
const myDog = new Dog("Jimmy");
myDog.move();
myDog.bark();

編譯後,上述程式碼將生成 JavaScript 程式碼。

var __extends = (this && this.__extends) || (function () {
   var extendStatics = function (d, b) {
      extendStatics = Object.setPrototypeOf ||
         ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
         function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
      return extendStatics(d, b);
   };
   return function (d, b) {
      if (typeof b !== "function" && b !== null)
         throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
      extendStatics(d, b);
      function __() { this.constructor = d; }
      d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
   };
})();
var Animal = /** @class */ (function () {
   function Animal(name) {
      this.name = name;
   }
   Animal.prototype.move = function () {
      console.log("".concat(this.name, " is moving..."));
   };
   return Animal;
}());
var Dog = /** @class */ (function (_super) {
   __extends(Dog, _super);
   function Dog() {
      return _super !== null && _super.apply(this, arguments) || this;
   }
   Dog.prototype.bark = function () {
      console.log("".concat(this.name, " is barking..."));
   };
   return Dog;
}(Animal));
var myDog = new Dog("Jimmy");
myDog.move();
myDog.bark();

輸出

編譯後,上述程式碼將生成 JavaScript 程式碼。JavaScript 程式碼將產生以下結果:

Jummy is moving…
Jimmy is barking…

TypeScript 中的繼承功能允許我們建立類層次結構,促進程式碼重用並能夠以結構化的方式組織相關類。

多型

多型是指物件能夠採用多種形式或根據上下文以不同方式執行的能力。TypeScript 透過方法覆蓋和方法過載支援多型。

方法覆蓋

方法覆蓋允許子類覆蓋在其超類中定義的方法。以下是一個演示方法覆蓋的示例。在此示例中,我們有一個抽象類 Shape,它定義了一個抽象方法 calculateArea()。Circle 和 Rectangle 類擴充套件 Shape 並提供其 calculateArea() 方法的實現。並且我們建立了 Circle 和 Rectangle 類的例項並計算了它們各自的面積。

abstract class Shape {
   protected color: string;    
   constructor(color: string) {
      this.color = color;
   }    
   public abstract calculateArea(): number;
}
class Circle extends Shape {
   private radius: number;    
   constructor(color: string, radius: number) {
      super(color);
      this.radius = radius;
   }    
   public calculateArea(): number {
      return Math.PI * this.radius * this.radius;
   }
}
class Rectangle extends Shape {
   private width: number;
   private height: number;    
   constructor(color: string, width: number, height: number) {
      super(color);
      this.width = width;
      this.height = height;
   }    
   public calculateArea(): number {
       return this.width * this.height;
   }
}
const myCircle = new Circle("red", 5);
const myRectangle = new Rectangle("blue", 4, 6);
console.log(myCircle.calculateArea()); 
console.log(myRectangle.calculateArea());

編譯後,上述程式碼將生成 JavaScript 程式碼。

var __extends = (this && this.__extends) || (function () {
   var extendStatics = function (d, b) {
      extendStatics = Object.setPrototypeOf ||
         ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
         function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
      return extendStatics(d, b);
   };
   return function (d, b) {
      if (typeof b !== "function" && b !== null)
         throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
      extendStatics(d, b);
      function __() { this.constructor = d; }
      d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
   };
})();
var Shape = /** @class */ (function () {
   function Shape(color) {
      this.color = color;
   }
   return Shape;
}());
var Circle = /** @class */ (function (_super) {
   __extends(Circle, _super);
   function Circle(color, radius) {
      var _this = _super.call(this, color) || this;
      _this.radius = radius;
      return _this;
   }
   Circle.prototype.calculateArea = function () {
      return Math.PI * this.radius * this.radius;
   };
   return Circle;
}(Shape));
var Rectangle = /** @class */ (function (_super) {
   __extends(Rectangle, _super);
   function Rectangle(color, width, height) {
      var _this = _super.call(this, color) || this;
      _this.width = width;
      _this.height = height;
      return _this;
   }
   Rectangle.prototype.calculateArea = function () {
      return this.width * this.height;
   };
   return Rectangle;
}(Shape));
var myCircle = new Circle("red", 5);
var myRectangle = new Rectangle("blue", 4, 6);
console.log(myCircle.calculateArea());
console.log(myRectangle.calculateArea());

輸出

編譯後,上述程式碼將生成 JavaScript 程式碼。JavaScript 程式碼將產生以下結果:

78.53981633974483
24

在此示例中,Circle 和 Rectangle 類都繼承了抽象 Shape 類的 calculateArea() 方法。但是,每個子類都以不同的方式實現該方法,以根據其特定形狀計算面積。這展示了多型性,其中不同類的物件可以被視為公共超類的例項,並表現出不同的行為。

方法過載

另一方面,方法過載使我們能夠定義多個具有相同名稱但引數不同的方法。TypeScript 根據傳遞的引數數量或型別確定要呼叫的適當方法。

在下面的程式碼片段中,我們有一個 Calculator 類,它定義了兩個 add 方法——一個用於新增數字,另一個用於連線字串。並且我們建立了一個 Calculator 類的例項並呼叫了兩個 add 方法。

class Calculator {
   public add(a: string, b: string): string;
   public add(a: number, b: number): number;

   public add(a: any, b: any): any {
      return a + b;
   }
}

const myCalculator = new Calculator();
const sum = myCalculator.add(3, 5);
const concatenated = myCalculator.add("Hello, ", "TypeScript!");

console.log(sum);
console.log(concatenated);

編譯後,上述程式碼將生成以下 JavaScript 程式碼。

var Calculator = /** @class */ (function () {
   function Calculator() {
   }
   Calculator.prototype.add = function (a, b) {
      return a + b;
   };
   return Calculator;
}());
var myCalculator = new Calculator();
var sum = myCalculator.add(3, 5);
var concatenated = myCalculator.add("Hello, ", "TypeScript!");
console.log(sum);
console.log(concatenated);

輸出

它將產生以下結果:

8
Hello, TypeScript!

抽象

抽象允許我們定義物件的必要特徵,同時隱藏實現細節。TypeScript 透過抽象類和方法支援抽象。抽象類充當其他類的藍圖,不能直接例項化。它可能包含派生類必須實現的抽象方法。

在此示例中,Shape 類被宣告為抽象類,並且它定義了一個名為 draw 的抽象方法。Circle 類擴充套件 Shape 併為 draw 方法提供了一個實現。並且我們建立了一個 Circle 類的例項並呼叫了 draw 方法。

abstract class Shape {
   protected color: string;
    
   constructor(color: string) {
      this.color = color;
   }
    
   public abstract draw(): void;
}

class Circle extends Shape {
   private radius: number;
    
   constructor(color: string, radius: number) {
      super(color);
      this.radius = radius;
   }
    
   public draw() {
      console.log(`Drawing a ${this.color} circle with radius ${this.radius}`);
   }
}

const myCircle = new Circle("red", 5);
myCircle.draw();

編譯後,上述程式碼將生成以下 JavaScript 程式碼。

var __extends = (this && this.__extends) || (function () {
   var extendStatics = function (d, b) {
      extendStatics = Object.setPrototypeOf ||
         ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
         function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
      return extendStatics(d, b);
   };
   return function (d, b) {
      if (typeof b !== "function" && b !== null)
         throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
      extendStatics(d, b);
      function __() { this.constructor = d; }
      d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
   };
})();
var Shape = /** @class */ (function () {
   function Shape(color) {
      this.color = color;
   }
   return Shape;
}());
var Circle = /** @class */ (function (_super) {
   __extends(Circle, _super);
   function Circle(color, radius) {
      var _this = _super.call(this, color) || this;
      _this.radius = radius;
      return _this;
   }
   Circle.prototype.draw = function () {
      console.log("Drawing a ".concat(this.color, " circle with radius ").concat(this.radius));
   };
   return Circle;
}(Shape));
var myCircle = new Circle("red", 5);
myCircle.draw();

輸出

編譯後,上述程式碼將生成 JavaScript 程式碼。JavaScript 程式碼將產生以下結果:

Drawing a red circle with radius 5

抽象類 Shape 提供了一個公共介面(draw),所有派生類都必須實現。這使我們能夠定義通用行為並在不同的子類之間強制執行某些方法,促進抽象並確保應用程式結構的一致性。

結論

TypeScript 為面向物件程式設計原則提供了強大的支援,包括封裝、繼承、多型和抽象。透過利用類、訪問修飾符、繼承、方法覆蓋、方法過載和抽象類,開發人員可以以更結構化和可維護的方式設計和實現面向物件系統。TypeScript 的靜態型別功能進一步增強了面向物件程式碼庫的可靠性和可擴充套件性。

更新於:2023年8月21日

353 次瀏覽

開啟您的 職業生涯

透過完成課程獲得認證

開始學習
廣告

© . All rights reserved.