TypeScript入门(五)类与面向对象编程:构建代码的"数字王国"

90 阅读8分钟

第5章 类与面向对象编程:构建代码的"数字王国"

想象你正在设计一座城市——类(Class) 就是你的建筑蓝图,对象(Object) 是根据蓝图建造的实际建筑。TypeScript的面向对象编程(OOP)让你能创建结构良好、可重用且易于维护的代码王国。这一章,我们将学会如何用类构建强大而优雅的程序结构!

5.1 类的基础——绘制你的"代码蓝图"

类是创建对象的模板,就像建筑师的设计图纸一样精确定义了结构和功能。

🏗️ 基础类定义

// 定义汽车类 - 就像汽车制造蓝图
class Car {
  // 属性(字段)- 汽车的基本特征
  brand: string;
  model: string;
  year: number;
  
  // 构造函数 - 创建汽车实例时调用
  constructor(brand: string, model: string, year: number) {
    this.brand = brand;
    this.model = model;
    this.year = year;
  }
  
  // 方法 - 汽车的行为功能
  startEngine() {
    console.log(`${this.brand} ${this.model} 引擎启动!`);
  }
  
  getAge(currentYear: number) {
    return currentYear - this.year;
  }
  
  getInfo() {
    return `${this.year}${this.brand} ${this.model}`;
  }
}

// 创建汽车实例 - 按照蓝图"制造"汽车
const myCar = new Car("比亚迪", "L3", 2014);
myCar.startEngine(); // "比亚迪 L3 引擎启动!"

console.log(`车龄:${myCar.getAge(2025)}年`); // "车龄:11年"
console.log(myCar.getInfo()); // "2014年 比亚迪 L3"

// 创建多个实例
const yourCar = new Car("本田", "雅阁", 2022);
const hisCar = new Car("宝马", "X5", 2021);

💡 核心思想:类是对象的工厂模板,定义了对象应该有什么属性和能做什么事情,就像工厂的生产线标准。

🎯 类的基本组成部分

  • 属性(Properties):对象的状态数据(如品牌、型号)
  • 构造函数(Constructor):初始化对象的特殊方法
  • 方法(Methods):对象的行为功能(如启动引擎)
  • 访问修饰符:控制属性和方法的可见性

5.2 继承——建立"家族血统"

继承让子类能够获得父类的特性,就像孩子继承父母的基因一样自然。

🌳 基础继承:父类与子类

// 基类(父类)- 交通工具的通用特性
class Vehicle {
  constructor(public type: string, public maxSpeed: number) {} // 简写语法
  
  move() {
    console.log(`${this.type} 正在以最高时速 ${this.maxSpeed}km/h 移动...`);
  }
  
  stop() {
    console.log(`${this.type} 已停止`);
  }
}

// 派生类(子类)- 飞机继承交通工具
class Airplane extends Vehicle {
  altitude: number;
  
  constructor(maxSpeed: number) {
    super("飞机", maxSpeed); // 必须调用super()初始化父类
    this.altitude = 0;
  }
  
  // 添加子类特有方法
  takeOff() {
    console.log("飞机起飞!");
    this.altitude = 10000;
  }
  
  land() {
    console.log("飞机降落!");
    this.altitude = 0;
  }
  
  // 重写父类方法 - 提供专门的实现
  move() {
    console.log(`飞机在 ${this.altitude}米高空飞行,时速 ${this.maxSpeed}km/h`);
  }
}

// 另一个子类 - 轮船
class Ship extends Vehicle {
  constructor(maxSpeed: number) {
    super("轮船", maxSpeed);
  }
  
  // 重写父类方法
  move() {
    console.log(`轮船在海面航行,时速 ${this.maxSpeed}km/h`);
  }
  
  anchor() {
    console.log("轮船抛锚停泊");
  }
}

// 使用继承
const boeing = new Airplane(900);
boeing.takeOff(); // "飞机起飞!"
boeing.move();    // "飞机在 10000米高空飞行,时速 900km/h"
boeing.stop();    // "飞机 已停止" (继承自父类)

const titanic = new Ship(40);
titanic.move();   // "轮船在海面航行,时速 40km/h"
titanic.anchor(); // "轮船抛锚停泊"

🎭 多态:同一接口,不同实现

多态让相同的方法调用在不同对象上产生不同的行为,就像同样的"移动"指令对不同交通工具有不同含义。

// 多态演示函数
function startJourney(vehicle: Vehicle) {
  console.log("=== 开始旅程 ===");
  vehicle.move(); // 根据实际对象类型调用不同的move实现
  vehicle.stop();
  console.log("=== 旅程结束 ===\n");
}

// 创建不同类型的交通工具
const plane = new Airplane(800);
const ship = new Ship(35);
const car = new Vehicle("汽车", 120);

plane.takeOff(); // 飞机特有操作

// 多态调用 - 同一函数处理不同类型
startJourney(plane); // 调用Airplane的move方法
startJourney(ship);  // 调用Ship的move方法
startJourney(car);   // 调用Vehicle的move方法

🌟 继承的核心优势

  1. 代码复用:子类自动获得父类的属性和方法
  2. 层次结构:建立清晰的类型分类体系
  3. 多态性:统一接口处理不同类型的对象
  4. 扩展性:在不修改原有代码的基础上添加新功能

5.3 访问修饰符——设置"安全门禁"

TypeScript提供三种访问控制修饰符,就像给类的不同部分设置不同的安全级别。

🔐 访问权限对比表

修饰符类内部子类类外部描述
public默认级别,完全公开访问
private仅当前类内部可访问
protected类内部和子类可访问

🏦 银行账户系统:访问控制实战

class BankAccount {
  public readonly accountNumber: string;  // 公开但只读 - 账号可查看不可修改
  private balance: number;                 // 私有属性 - 余额只能内部操作
  protected owner: string;                 // 受保护属性 - 账户持有人信息
  private transactionHistory: string[] = []; // 私有 - 交易记录
  
  constructor(owner: string, initialBalance: number) {
    this.accountNumber = this.generateAccountNumber();
    this.balance = initialBalance;
    this.owner = owner;
    this.addTransaction(`开户,初始余额:${initialBalance}`);
  }
  
  // 私有方法 - 生成账号
  private generateAccountNumber(): string {
    return `ACC-${Math.random().toString(36).substring(2, 10).toUpperCase()}`;
  }
  
  // 私有方法 - 记录交易
  private addTransaction(description: string) {
    const timestamp = new Date().toLocaleString();
    this.transactionHistory.push(`${timestamp}: ${description}`);
  }
  
  // 公开方法 - 存款
  public deposit(amount: number): void {
    if (amount <= 0) {
      throw new Error("存款金额必须大于0");
    }
    this.balance += amount;
    this.addTransaction(`存入 ${amount},余额:${this.balance}`);
    console.log(`存款成功!当前余额:${this.balance}`);
  }
  
  // 公开方法 - 取款
  public withdraw(amount: number): void {
    if (amount <= 0) {
      throw new Error("取款金额必须大于0");
    }
    if (amount > this.balance) {
      throw new Error("余额不足");
    }
    this.balance -= amount;
    this.addTransaction(`取出 ${amount},余额:${this.balance}`);
    console.log(`取款成功!当前余额:${this.balance}`);
  }
  
  // 公开方法 - 查询余额
  public getBalance(): number {
    return this.balance;
  }
  
  // 受保护方法 - 获取账户信息(子类可用)
  protected getAccountInfo(): string {
    return `账户:${this.accountNumber},持有人:${this.owner}`;
  }
}

// 储蓄账户 - 继承银行账户
class SavingsAccount extends BankAccount {
  private interestRate: number;
  
  constructor(owner: string, initialBalance: number, interestRate: number) {
    super(owner, initialBalance);
    this.interestRate = interestRate;
  }
  
  // 添加利息 - 可以访问受保护的属性和方法
  addInterest(): void {
    const interest = this.getBalance() * this.interestRate;
    this.deposit(interest); // 调用父类的公开方法
    console.log(`${this.getAccountInfo()} 获得利息:${interest}`);
    // console.log(this.owner); // 可以访问受保护属性
    // this.balance += interest; // 错误!不能直接访问私有属性
  }
}

// 使用示例
const myAccount = new BankAccount("张三", 1000);
console.log(`账号:${myAccount.accountNumber}`); // 可以访问公开只读属性
myAccount.deposit(500);  // 可以调用公开方法
myAccount.withdraw(200); // 可以调用公开方法

// 以下操作都会报错
// myAccount.balance = 1000000;           // 错误!私有属性不可访问
// myAccount.generateAccountNumber();     // 错误!私有方法不可访问
// myAccount.owner = "李四";              // 错误!受保护属性不可访问

// 储蓄账户使用
const savingsAccount = new SavingsAccount("李四", 5000, 0.03);
savingsAccount.addInterest(); // 子类可以使用受保护的方法和属性

🛡️ 安全提醒:访问修饰符只在编译时检查,JavaScript运行时仍可能通过特殊方式访问私有成员。对于真正的数据保护,需要结合其他安全措施。

5.4 抽象类——定义"半成品蓝图"

抽象类是不能被直接实例化的基类,就像建筑设计的概念图——定义了基本结构但需要具体实现才能建造。

🎨 抽象类的设计哲学

抽象类用于:

  • 定义公共接口:强制子类实现特定方法
  • 提供部分实现:包含一些通用的具体方法
  • 建立继承体系:作为相关类的共同基础
// 抽象动物类 - 定义动物的基本特征
abstract class Animal {
  constructor(public name: string, public species: string) {}
  
  // 抽象方法 - 必须由子类实现
  abstract makeSound(): void;
  abstract getFood(): string;
  
  // 具体方法 - 所有动物共有的行为
  move(distance: number = 0): void {
    console.log(`${this.name} 移动了 ${distance}米`);
  }
  
  sleep(): void {
    console.log(`${this.name} 正在睡觉...`);
  }
  
  // 模板方法 - 定义固定流程,调用抽象方法
  dailyRoutine(): void {
    console.log(`=== ${this.name} 的一天 ===`);
    this.makeSound();
    console.log(`${this.name} 在寻找 ${this.getFood()}`);
    this.move(Math.floor(Math.random() * 100));
    this.sleep();
    console.log("=== 一天结束 ===\n");
  }
}

// 具体子类 - 狗
class Dog extends Animal {
  constructor(name: string) {
    super(name, "犬科");
  }
  
  // 实现抽象方法
  makeSound(): void {
    console.log(`${this.name} 汪汪叫!`);
  }
  
  getFood(): string {
    return "骨头和狗粮";
  }
  
  // 狗特有的方法
  wagTail(): void {
    console.log(`${this.name} 摇尾巴表示开心`);
  }
}

// 具体子类 - 猫
class Cat extends Animal {
  constructor(name: string) {
    super(name, "猫科");
  }
  
  makeSound(): void {
    console.log(`${this.name} 喵喵叫!`);
  }
  
  getFood(): string {
    return "鱼和猫粮";
  }
  
  // 猫特有的方法
  purr(): void {
    console.log(`${this.name} 发出呼噜声`);
  }
}

// 具体子类 - 鸟
class Bird extends Animal {
  constructor(name: string) {
    super(name, "鸟类");
  }
  
  makeSound(): void {
    console.log(`${this.name} 啾啾叫!`);
  }
  
  getFood(): string {
    return "虫子和种子";
  }
  
  fly(): void {
    console.log(`${this.name} 在天空中飞翔`);
  }
}

// 使用示例
const dog = new Dog("阿黄");
const cat = new Cat("小白");
const bird = new Bird("小鸟");

// 调用模板方法 - 展示多态性
dog.dailyRoutine();
cat.dailyRoutine();
bird.dailyRoutine();

// 调用特有方法
dog.wagTail();
cat.purr();
bird.fly();

// 错误示例:不能实例化抽象类
// const animal = new Animal("抽象动物", "未知"); // 编译错误!

🎯 抽象类使用场景

  • 框架设计:定义插件或组件的基础结构
  • 模板方法模式:固定算法骨架,变化具体实现
  • 强制规范:确保子类实现关键方法
  • 代码复用:提供通用功能,避免重复实现

5.5 类与接口的关系——签订"实现合同"

类可以实现接口,就像签订合同一样承诺提供接口定义的所有功能。

📜 接口实现:行为契约

// 定义行为接口
interface Loggable {
  log(message: string): void;
}

interface Serializable {
  serialize(): string;
  deserialize(data: string): void;
}

interface Timestamped {
  readonly createdAt: Date;
  readonly updatedAt: Date;
  updateTimestamp(): void;
}

// 用户类实现多个接口
class User implements Loggable, Serializable, Timestamped {
  readonly createdAt: Date;
  readonly updatedAt: Date;
  
  constructor(
    public name: string, 
    public email: string,
    private password: string
  ) {
    this.createdAt = new Date();
    this.updatedAt = new Date();
  }
  
  // 实现Loggable接口
  log(message: string): void {
    const timestamp = new Date().toISOString();
    console.log(`[用户日志 ${timestamp}] ${this.name}: ${message}`);
  }
  
  // 实现Serializable接口
  serialize(): string {
    return JSON.stringify({
      name: this.name,
      email: this.email,
      createdAt: this.createdAt,
      updatedAt: this.updatedAt
    });
  }
  
  deserialize(data: string): void {
    const parsed = JSON.parse(data);
    this.name = parsed.name;
    this.email = parsed.email;
    // 注意:createdAt和updatedAt是readonly,实际应用中需要特殊处理
  }
  
  // 实现Timestamped接口
  updateTimestamp(): void {
    // 由于updatedAt是readonly,这里用Object.defineProperty绕过限制
    Object.defineProperty(this, 'updatedAt', {
      value: new Date(),
      writable: false
    });
  }
  
  // 用户特有方法
  changePassword(newPassword: string): void {
    this.password = newPassword;
    this.updateTimestamp();
    this.log("密码已更新");
  }
  
  validatePassword(password: string): boolean {
    return this.password === password;
  }
}

// 产品类也实现相同接口
class Product implements Loggable, Serializable, Timestamped {
  readonly createdAt: Date;
  readonly updatedAt: Date;
  
  constructor(
    public name: string,
    public price: number,
    public category: string
  ) {
    this.createdAt = new Date();
    this.updatedAt = new Date();
  }
  
  log(message: string): void {
    console.log(`[产品日志] ${this.name}: ${message}`);
  }
  
  serialize(): string {
    return JSON.stringify({
      name: this.name,
      price: this.price,
      category: this.category,
      createdAt: this.createdAt,
      updatedAt: this.updatedAt
    });
  }
  
  deserialize(data: string): void {
    const parsed = JSON.parse(data);
    Object.assign(this, parsed);
  }
  
  updateTimestamp(): void {
    Object.defineProperty(this, 'updatedAt', {
      value: new Date(),
      writable: false
    });
  }
  
  updatePrice(newPrice: number): void {
    this.price = newPrice;
    this.updateTimestamp();
    this.log(`价格更新为 ${newPrice}`);
  }
}

// 通用函数:处理实现了特定接口的对象
function logAndSerialize(item: Loggable & Serializable): string {
  item.log("正在序列化对象");
  return item.serialize();
}

// 使用示例
const user = new User("张三", "zhang@example.com", "secret123");
const product = new Product("笔记本电脑", 5999, "电子产品");

// 多态使用 - 同一函数处理不同类型
console.log(logAndSerialize(user));
console.log(logAndSerialize(product));

user.changePassword("newSecret456");
product.updatePrice(5499);

🆚 接口 vs 抽象类:选择指南

特性接口 (Interface)抽象类 (Abstract Class)
实例化不能实例化不能实例化
方法实现只能声明,不能实现可以包含具体实现
构造函数没有构造函数可以有构造函数
访问修饰符所有成员默认public可以使用各种访问修饰符
多重继承类可以实现多个接口类只能继承一个抽象类
使用场景定义行为契约提供部分实现的基类
扩展性接口可以继承接口抽象类可以继承类或抽象类

选择建议:

  • 优先使用接口:当你需要定义对象应该有什么能力时
  • 使用抽象类:当你需要为一组相关类提供共同实现时
  • 组合使用:抽象类实现接口,提供部分默认实现

5.6 静态成员——共享"类级别资源"

静态成员属于类本身而不是实例,就像公司的公共资源一样被所有员工共享。

⚡ 静态属性与方法

// 数学工具类 - 展示静态成员的使用
class MathUtils {
  // 静态常量
  static readonly PI: number = 3.14159265359;
  static readonly E: number = 2.71828182846;
  
  // 静态计数器
  private static calculationCount: number = 0;
  
  // 静态方法 - 计算圆面积
  static calculateCircleArea(radius: number): number {
    this.calculationCount++;
    return this.PI * radius * radius;
  }
  
  // 静态方法 - 计算圆周长
  static calculateCirclePerimeter(radius: number): number {
    this.calculationCount++;
    return 2 * this.PI * radius;
  }
  
  // 静态方法 - 获取计算次数
  static getCalculationCount(): number {
    return this.calculationCount;
  }
  
  // 静态方法 - 重置计数器
  static resetCounter(): void {
    this.calculationCount = 0;
  }
  
  // 静态初始化块(TypeScript 4.4+)
  static {
    console.log("MathUtils类已加载");
    console.log(`π = ${this.PI}, e = ${this.E}`);
  }
}

// 字符串工具类
class StringUtils {
  // 防止实例化的私有构造函数
  private constructor() {}
  
  static toUpperCase(str: string): string {
    return str.toUpperCase();
  }
  
  static toLowerCase(str: string): string {
    return str.toLowerCase();
  }
  
  static reverse(str: string): string {
    return str.split("").reverse().join("");
  }
  
  static isPalindrome(str: string): boolean {
    const cleaned = str.toLowerCase().replace(/[^a-z0-9]/g, '');
    return cleaned === this.reverse(cleaned);
  }
  
  static truncate(str: string, maxLength: number): string {
    return str.length > maxLength ? str.substring(0, maxLength) + '...' : str;
  }
}

// 使用静态成员(无需实例化)
console.log(`圆周率:${MathUtils.PI}`);
console.log(`半径5的圆面积:${MathUtils.calculateCircleArea(5)}`);
console.log(`半径3的圆周长:${MathUtils.calculateCirclePerimeter(3)}`);
console.log(`计算次数:${MathUtils.getCalculationCount()}`);

console.log(StringUtils.toUpperCase("hello world")); // "HELLO WORLD"
console.log(StringUtils.reverse("TypeScript")); // "tpircSepyT"
console.log(StringUtils.isPalindrome("A man a plan a canal Panama")); // true

// 错误示例:实例无法访问静态成员
// const math = new MathUtils(); // 如果构造函数不是私有的
// math.PI; // 错误!实例无法访问静态属性
// const str = new StringUtils(); // 错误!构造函数是私有的

🏭 单例模式:静态成员的经典应用

// 配置管理器 - 单例模式
class ConfigManager {
  private static instance: ConfigManager;
  private config: Map<string, any> = new Map();
  
  // 私有构造函数防止外部实例化
  private constructor() {
    this.loadDefaultConfig();
  }
  
  // 静态方法获取单例实例
  static getInstance(): ConfigManager {
    if (!ConfigManager.instance) {
      ConfigManager.instance = new ConfigManager();
    }
    return ConfigManager.instance;
  }
  
  private loadDefaultConfig(): void {
    this.config.set('theme', 'light');
    this.config.set('language', 'zh-CN');
    this.config.set('autoSave', true);
  }
  
  get(key: string): any {
    return this.config.get(key);
  }
  
  set(key: string, value: any): void {
    this.config.set(key, value);
  }
  
  getAll(): Record<string, any> {
    return Object.fromEntries(this.config);
  }
}

// 使用单例
const config1 = ConfigManager.getInstance();
const config2 = ConfigManager.getInstance();

console.log(config1 === config2); // true - 同一个实例

config1.set('theme', 'dark');
console.log(config2.get('theme')); // 'dark' - 共享状态

⚡ 静态成员使用场景

  1. 工具函数:不依赖实例状态的通用方法
  2. 常量定义:类相关的常量值
  3. 单例模式:确保类只有一个实例
  4. 计数器/缓存:跨实例共享的数据
  5. 工厂方法:创建实例的静态方法

5.7 类实战:智能家居控制系统

让我们通过一个完整的实战案例,综合运用本章学到的所有类特性:

// 基础设备接口
interface Device {
  readonly id: string;
  readonly name: string;
  isOnline: boolean;
  turnOn(): void;
  turnOff(): void;
  getStatus(): string;
}

// 可控制接口
interface Controllable {
  executeCommand(command: string): boolean;
}

// 可监控接口
interface Monitorable {
  getMetrics(): Record<string, any>;
}

// 抽象智能设备基类
abstract class SmartDevice implements Device {
  readonly id: string;
  readonly name: string;
  isOnline: boolean = false;
  protected lastUpdate: Date;
  
  constructor(name: string) {
    this.id = SmartDevice.generateId();
    this.name = name;
    this.lastUpdate = new Date();
  }
  
  // 静态方法生成设备ID
  private static generateId(): string {
    return `DEV-${Date.now()}-${Math.random().toString(36).substring(2, 8)}`;
  }
  
  // 抽象方法 - 子类必须实现
  abstract turnOn(): void;
  abstract turnOff(): void;
  abstract getStatus(): string;
  
  // 通用方法
  protected updateTimestamp(): void {
    this.lastUpdate = new Date();
  }
  
  getLastUpdate(): Date {
    return this.lastUpdate;
  }
}

// 智能灯泡类
class SmartBulb extends SmartDevice implements Controllable {
  private brightness: number = 0; // 0-100
  private color: string = "#FFFFFF";
  
  constructor(name: string) {
    super(name);
  }
  
  turnOn(): void {
    this.isOnline = true;
    this.brightness = 100;
    this.updateTimestamp();
    console.log(`${this.name} 已开启,亮度:${this.brightness}%`);
  }
  
  turnOff(): void {
    this.isOnline = false;
    this.brightness = 0;
    this.updateTimestamp();
    console.log(`${this.name} 已关闭`);
  }
  
  getStatus(): string {
    return `灯泡 ${this.name} - 在线:${this.isOnline},亮度:${this.brightness}%,颜色:${this.color}`;
  }
  
  executeCommand(command: string): boolean {
    const [action, value] = command.split(':');
    
    switch (action) {
      case 'brightness':
        this.setBrightness(parseInt(value));
        return true;
      case 'color':
        this.setColor(value);
        return true;
      default:
        return false;
    }
  }
  
  private setBrightness(level: number): void {
    this.brightness = Math.max(0, Math.min(100, level));
    this.updateTimestamp();
    console.log(`${this.name} 亮度调节至:${this.brightness}%`);
  }
  
  private setColor(color: string): void {
    this.color = color;
    this.updateTimestamp();
    console.log(`${this.name} 颜色调节至:${this.color}`);
  }
}

// 智能温控器类
class SmartThermostat extends SmartDevice implements Controllable, Monitorable {
  private targetTemperature: number = 22;
  private currentTemperature: number = 20;
  private mode: 'heat' | 'cool' | 'auto' = 'auto';
  
  constructor(name: string) {
    super(name);
    this.startTemperatureSimulation();
  }
  
  turnOn(): void {
    this.isOnline = true;
    this.updateTimestamp();
    console.log(`${this.name} 已开启,目标温度:${this.targetTemperature}°C`);
  }
  
  turnOff(): void {
    this.isOnline = false;
    this.updateTimestamp();
    console.log(`${this.name} 已关闭`);
  }
  
  getStatus(): string {
    return `温控器 ${this.name} - 在线:${this.isOnline},当前:${this.currentTemperature}°C,目标:${this.targetTemperature}°C,模式:${this.mode}`;
  }
  
  executeCommand(command: string): boolean {
    const [action, value] = command.split(':');
    
    switch (action) {
      case 'temperature':
        this.setTargetTemperature(parseInt(value));
        return true;
      case 'mode':
        this.setMode(value as 'heat' | 'cool' | 'auto');
        return true;
      default:
        return false;
    }
  }
  
  getMetrics(): Record<string, any> {
    return {
      currentTemperature: this.currentTemperature,
      targetTemperature: this.targetTemperature,
      mode: this.mode,
      isHeating: this.currentTemperature < this.targetTemperature,
      isCooling: this.currentTemperature > this.targetTemperature,
      lastUpdate: this.lastUpdate
    };
  }
  
  private setTargetTemperature(temp: number): void {
    this.targetTemperature = Math.max(10, Math.min(35, temp));
    this.updateTimestamp();
    console.log(`${this.name} 目标温度设置为:${this.targetTemperature}°C`);
  }
  
  private setMode(mode: 'heat' | 'cool' | 'auto'): void {
    this.mode = mode;
    this.updateTimestamp();
    console.log(`${this.name} 模式切换为:${this.mode}`);
  }
  
  private startTemperatureSimulation(): void {
    setInterval(() => {
      if (this.isOnline) {
        // 模拟温度变化
        const diff = this.targetTemperature - this.currentTemperature;
        this.currentTemperature += diff * 0.1;
        this.currentTemperature = Math.round(this.currentTemperature * 10) / 10;
      }
    }, 2000);
  }
}

// 智能家居控制中心
class SmartHomeController {
  private devices: Map<string, SmartDevice> = new Map();
  private static instance: SmartHomeController;
  
  private constructor() {}
  
  static getInstance(): SmartHomeController {
    if (!SmartHomeController.instance) {
      SmartHomeController.instance = new SmartHomeController();
    }
    return SmartHomeController.instance;
  }
  
  addDevice(device: SmartDevice): void {
    this.devices.set(device.id, device);
    console.log(`设备 ${device.name} (${device.id}) 已添加到系统`);
  }
  
  removeDevice(deviceId: string): boolean {
    const device = this.devices.get(deviceId);
    if (device) {
      this.devices.delete(deviceId);
      console.log(`设备 ${device.name} 已从系统移除`);
      return true;
    }
    return false;
  }
  
  getDevice(deviceId: string): SmartDevice | undefined {
    return this.devices.get(deviceId);
  }
  
  getAllDevices(): SmartDevice[] {
    return Array.from(this.devices.values());
  }
  
  getOnlineDevices(): SmartDevice[] {
    return this.getAllDevices().filter(device => device.isOnline);
  }
  
  executeCommand(deviceId: string, command: string): boolean {
    const device = this.devices.get(deviceId);
    if (device && 'executeCommand' in device) {
      return (device as any).executeCommand(command);
    }
    return false;
  }
  
  getSystemStatus(): void {
    console.log("\n=== 智能家居系统状态 ===");
    console.log(`总设备数:${this.devices.size}`);
    console.log(`在线设备数:${this.getOnlineDevices().length}`);
    
    this.getAllDevices().forEach(device => {
      console.log(device.getStatus());
    });
    console.log("========================\n");
  }
}

// 使用示例
const homeController = SmartHomeController.getInstance();

// 创建设备
const livingRoomLight = new SmartBulb("客厅灯");
const bedroomLight = new SmartBulb("卧室灯");
const thermostat = new SmartThermostat("客厅温控器");

// 添加设备到系统
homeController.addDevice(livingRoomLight);
homeController.addDevice(bedroomLight);
homeController.addDevice(thermostat);

// 控制设备
livingRoomLight.turnOn();
bedroomLight.turnOn();
thermostat.turnOn();

// 执行命令
homeController.executeCommand(livingRoomLight.id, "brightness:75");
homeController.executeCommand(livingRoomLight.id, "color:#FF6B6B");
homeController.executeCommand(thermostat.id, "temperature:24");
homeController.executeCommand(thermostat.id, "mode:cool");

// 查看系统状态
homeController.getSystemStatus();

// 获取监控数据
if ('getMetrics' in thermostat) {
  console.log("温控器监控数据:", thermostat.getMetrics());
}

5.8 类设计最佳实践——构建优雅代码

🎯 SOLID原则在TypeScript中的应用

  1. 单一职责原则(SRP):每个类只负责一个功能领域
  2. 开放封闭原则(OCP):对扩展开放,对修改封闭
  3. 里氏替换原则(LSP):子类应该能够替换父类
  4. 接口隔离原则(ISP):不应该强迫类依赖它们不使用的接口
  5. 依赖倒置原则(DIP):依赖抽象而非具体实现

💡 类设计指导原则

// ✅ 好的设计示例

// 1. 单一职责 - 每个类只做一件事
class EmailValidator {
  static isValid(email: string): boolean {
    return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
  }
}

class PasswordHasher {
  static hash(password: string): string {
    // 实际应用中使用bcrypt等安全库
    return btoa(password);
  }
  
  static verify(password: string, hash: string): boolean {
    return btoa(password) === hash;
  }
}

// 2. 开放封闭 - 通过继承扩展功能
abstract class NotificationSender {
  abstract send(message: string, recipient: string): Promise<boolean>;
}

class EmailSender extends NotificationSender {
  async send(message: string, recipient: string): Promise<boolean> {
    console.log(`发送邮件到 ${recipient}: ${message}`);
    return true;
  }
}

class SMSSender extends NotificationSender {
  async send(message: string, recipient: string): Promise<boolean> {
    console.log(`发送短信到 ${recipient}: ${message}`);
    return true;
  }
}

// 3. 依赖注入 - 依赖抽象而非具体实现
class UserService {
  constructor(private notificationSender: NotificationSender) {}
  
  async notifyUser(userId: string, message: string): Promise<void> {
    // 获取用户联系方式的逻辑...
    const userContact = "user@example.com";
    await this.notificationSender.send(message, userContact);
  }
}

// 使用
const emailService = new UserService(new EmailSender());
const smsService = new UserService(new SMSSender());

⚠️ 常见设计陷阱

// ❌ 避免的反模式

// 1. 上帝类 - 承担过多职责
class BadUserManager {
  // 用户管理
  createUser() {}
  deleteUser() {}
  
  // 邮件发送
  sendEmail() {}
  
  // 数据库操作
  saveToDatabase() {}
  
  // 日志记录
  logActivity() {}
  
  // 文件处理
  uploadFile() {}
}

// 2. 紧耦合 - 直接依赖具体实现
class BadOrderService {
  processOrder() {
    const emailSender = new EmailSender(); // 紧耦合
    emailSender.send("订单确认", "user@example.com");
  }
}

// 3. 违反封装 - 暴露内部状态
class BadBankAccount {
  public balance: number; // 应该是私有的
  
  constructor(balance: number) {
    this.balance = balance;
  }
}

5.9 类与JavaScript原型链的关系

TypeScript的类本质上是JavaScript原型继承的语法糖,理解这一点有助于更好地使用类:

// TypeScript类
class Animal {
  constructor(public name: string) {}
  
  speak() {
    console.log(`${this.name} makes a sound`);
  }
}

class Dog extends Animal {
  speak() {
    console.log(`${this.name} barks`);
  }
}

// 编译后的JavaScript(简化版)
/*
function Animal(name) {
  this.name = name;
}

Animal.prototype.speak = function() {
  console.log(this.name + ' makes a sound');
};

function Dog(name) {
  Animal.call(this, name);
}

Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;

Dog.prototype.speak = function() {
  console.log(this.name + ' barks');
};
*/

// 原型链关系
const dog = new Dog("旺财");
console.log(dog instanceof Dog);    // true
console.log(dog instanceof Animal); // true
console.log(Object.getPrototypeOf(dog) === Dog.prototype); // true

🔗 关键理解

  • class关键字创建构造函数
  • 方法定义在原型对象上
  • 继承使用原型链机制
  • 静态成员直接添加到构造函数
  • instanceof检查原型链关系

本章核心收获

通过这一章的学习,你已经掌握了TypeScript面向对象编程的精髓:

  1. 类即蓝图:用类定义对象的结构和行为,创建可重用的代码模板
  2. 继承体系:通过继承建立类型层次,实现代码复用和多态性
  3. 访问控制:使用修饰符精确控制成员可见性,保护数据安全
  4. 抽象设计:用抽象类定义半成品蓝图,强制子类实现关键方法
  5. 接口契约:让类实现接口,确保提供承诺的功能
  6. 静态资源:使用静态成员共享类级别的数据和方法
  7. 设计原则:遵循SOLID原则,构建可维护、可扩展的代码架构
  8. 实战应用:综合运用各种特性解决复杂的实际问题

成就解锁:面向对象编程让你的代码像精心规划的城市一样井然有序。下一章我们将探索函数与泛型,学习如何创建灵活可复用的代码组件。