第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方法
🌟 继承的核心优势:
- 代码复用:子类自动获得父类的属性和方法
- 层次结构:建立清晰的类型分类体系
- 多态性:统一接口处理不同类型的对象
- 扩展性:在不修改原有代码的基础上添加新功能
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' - 共享状态
⚡ 静态成员使用场景:
- 工具函数:不依赖实例状态的通用方法
- 常量定义:类相关的常量值
- 单例模式:确保类只有一个实例
- 计数器/缓存:跨实例共享的数据
- 工厂方法:创建实例的静态方法
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中的应用
- 单一职责原则(SRP):每个类只负责一个功能领域
- 开放封闭原则(OCP):对扩展开放,对修改封闭
- 里氏替换原则(LSP):子类应该能够替换父类
- 接口隔离原则(ISP):不应该强迫类依赖它们不使用的接口
- 依赖倒置原则(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面向对象编程的精髓:
- 类即蓝图:用类定义对象的结构和行为,创建可重用的代码模板
- 继承体系:通过继承建立类型层次,实现代码复用和多态性
- 访问控制:使用修饰符精确控制成员可见性,保护数据安全
- 抽象设计:用抽象类定义半成品蓝图,强制子类实现关键方法
- 接口契约:让类实现接口,确保提供承诺的功能
- 静态资源:使用静态成员共享类级别的数据和方法
- 设计原则:遵循SOLID原则,构建可维护、可扩展的代码架构
- 实战应用:综合运用各种特性解决复杂的实际问题
成就解锁:面向对象编程让你的代码像精心规划的城市一样井然有序。下一章我们将探索函数与泛型,学习如何创建灵活可复用的代码组件。