TypeScript类:面向对象编程的超级武器

前言

大家好,我是小杨。还记得我刚接触编程时,面对"类"这个概念总是觉得有些抽象。直到在TypeScript中深入使用类之后,我才真正体会到面向对象编程的魅力。今天,我想和大家分享我在实际项目中运用TypeScript类的一些心得和技巧。

类的基础:从蓝图到实物

想象一下你要建房子,首先需要一张蓝图,这张蓝图定义了房子的结构:有几个房间、门窗的位置、水电布局等。然后根据这张蓝图,你可以建造出很多具体的房子。

TypeScript中的类就是这样的"蓝图",而根据类创建的对象就是一栋栋具体的"房子"。

最简单的类

让我从一个最简单的例子开始:

typescript 复制代码
class Car { 
  brand: string;
  color: string;
  
  constructor(brand: string, color: string) {
    this.brand = brand;
    this.color = color;
  }
  
  drive() {
    console.log(`The ${this.color} ${this.brand} is driving.`);
  }
}

// 创建类的实例
const myCar = new Car("Toyota", "red");
myCar.drive(); // 输出: The red Toyota is driving.

这就是最基本的类:它有属性(brand、color)和方法(drive)。

类的进阶特性

1. 访问修饰符:控制可见性

访问修饰符就像房子的访问权限:有些区域对所有访客开放,有些只对家人开放,有些则是私密空间。

typescript 复制代码
class BankAccount {
  public readonly accountNumber: string;  // 公开但只读
  private balance: number;                // 完全私有
  protected owner: string;                // 受保护的
  
  constructor(accountNumber: string, initialBalance: number, owner: string) {
    this.accountNumber = accountNumber;
    this.balance = initialBalance;
    this.owner = owner;
  }
  
  public deposit(amount: number): void {
    if (amount > 0) {
      this.balance += amount;
      console.log(`Deposited $${amount}. New balance: $${this.balance}`);
    }
  }
  
  public withdraw(amount: number): boolean {
    if (amount > 0 && amount <= this.balance) {
      this.balance -= amount;
      console.log(`Withdrew $${amount}. Remaining balance: $${this.balance}`);
      return true;
    }
    console.log("Insufficient funds");
    return false;
  }
  
  public getBalance(): number {
    return this.balance;
  }
}

// 使用示例
const myAccount = new BankAccount("123456", 1000, "Alice");
myAccount.deposit(500);    // ✅ 允许
console.log(myAccount.accountNumber); // ✅ 允许
// myAccount.balance = 9999; // ❌ 编译错误:balance是私有的
// console.log(myAccount.balance); // ❌ 编译错误

2. 继承:代码复用的艺术

继承让类可以建立在现有类的基础上,就像子承父业一样。

typescript 复制代码
class Animal {
  constructor(public name: string, protected age: number) {}
  
  speak() {
    console.log(`${this.name} makes a sound.`);
  }
  
  sleep() {
    console.log(`${this.name} is sleeping.`);
  }
}

class Dog extends Animal {
  private breed: string;
  
  constructor(name: string, age: number, breed: string) {
    super(name, age); // 调用父类的constructor
    this.breed = breed;
  }
  
  // 重写父类方法
  speak() {
    console.log(`${this.name} barks!`);
  }
  
  // 子类特有方法
  fetch() {
    console.log(`${this.name} is fetching the ball.`);
  }
  
  getInfo() {
    return `${this.name} is a ${this.breed} and ${this.age} years old.`;
  }
}

// 使用示例
const myDog = new Dog("Buddy", 3, "Golden Retriever");
myDog.speak();  // 输出: Buddy barks!
myDog.sleep();  // 输出: Buddy is sleeping.
myDog.fetch();  // 输出: Buddy is fetching the ball.
console.log(myDog.getInfo());

3. 抽象类:定义框架

抽象类就像建筑的设计规范,它定义了结构但不提供完整实现。

typescript 复制代码
abstract class Shape {
  constructor(public name: string) {}
  
  // 抽象方法,必须在子类中实现
  abstract calculateArea(): number;
  
  // 具体方法
  displayInfo() {
    console.log(`Shape: ${this.name}, Area: ${this.calculateArea()}`);
  }
}

class Circle extends Shape {
  constructor(private radius: number) {
    super("Circle");
  }
  
  calculateArea(): number {
    return Math.PI * this.radius * this.radius;
  }
}

class Rectangle extends Shape {
  constructor(private width: number, private height: number) {
    super("Rectangle");
  }
  
  calculateArea(): number {
    return this.width * this.height;
  }
}

// 使用示例
const circle = new Circle(5);
const rectangle = new Rectangle(4, 6);

circle.displayInfo();    // 输出: Shape: Circle, Area: 78.53981633974483
rectangle.displayInfo(); // 输出: Shape: Rectangle, Area: 24

实战场景:类在项目中的应用

场景1:数据模型层

在我的电商项目中,类帮助我们构建了清晰的数据模型:

typescript 复制代码
class Product {
  constructor(
    public id: number,
    public name: string,
    public price: number,
    private stock: number
  ) {}
  
  // 业务方法
  reduceStock(quantity: number): boolean {
    if (this.stock >= quantity) {
      this.stock -= quantity;
      return true;
    }
    return false;
  }
  
  addStock(quantity: number): void {
    this.stock += quantity;
  }
  
  getStock(): number {
    return this.stock;
  }
  
  // 静态方法
  static createFromData(data: any): Product {
    return new Product(data.id, data.name, data.price, data.stock);
  }
}

class ShoppingCart {
  private items: Map<number, { product: Product; quantity: number }> = new Map();
  
  addItem(product: Product, quantity: number): void {
    if (product.reduceStock(quantity)) {
      const existingItem = this.items.get(product.id);
      if (existingItem) {
        existingItem.quantity += quantity;
      } else {
        this.items.set(product.id, { product, quantity });
      }
    }
  }
  
  removeItem(productId: number): void {
    const item = this.items.get(productId);
    if (item) {
      item.product.addStock(item.quantity);
      this.items.delete(productId);
    }
  }
  
  getTotal(): number {
    let total = 0;
    for (const [_, item] of this.items) {
      total += item.product.price * item.quantity;
    }
    return total;
  }
  
  checkout(): Order {
    return new Order(this);
  }
}

class Order {
  private orderId: string;
  private orderDate: Date;
  
  constructor(private cart: ShoppingCart) {
    this.orderId = this.generateOrderId();
    this.orderDate = new Date();
  }
  
  private generateOrderId(): string {
    return `ORD-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
  }
}

场景2:UI组件系统

在React项目中,类组件虽然现在较少使用,但在某些场景下仍然很有价值:

typescript 复制代码
abstract class BaseComponent<P = {}, S = {}> {
  protected state: S;
  protected props: P;
  
  constructor(props: P) {
    this.props = props;
    this.state = {} as S;
  }
  
  abstract render(): string;
  
  setState(newState: Partial<S>): void {
    this.state = { ...this.state, ...newState };
    this.onStateUpdate();
  }
  
  protected onStateUpdate(): void {
    // 触发重新渲染的逻辑
    console.log("State updated, triggering re-render");
  }
}

class CounterComponent extends BaseComponent<{ initialCount: number }, { count: number }> {
  constructor(props: { initialCount: number }) {
    super(props);
    this.state = { count: props.initialCount };
  }
  
  increment = (): void => {
    this.setState({ count: this.state.count + 1 });
  };
  
  decrement = (): void => {
    this.setState({ count: this.state.count - 1 });
  };
  
  render(): string {
    return `
      <div class="counter">
        <h2>Count: ${this.state.count}</h2>
        <button onclick="counter.increment()">+</button>
        <button onclick="counter.decrement()">-</button>
      </div>
    `;
  }
}

// 使用示例
const counter = new CounterComponent({ initialCount: 0 });
console.log(counter.render());

场景3:服务层和工具类

在服务层设计中,类提供了很好的封装:

typescript 复制代码
class ApiService {
  private baseURL: string;
  private token: string | null = null;
  
  constructor(baseURL: string) {
    this.baseURL = baseURL;
  }
  
  setToken(token: string): void {
    this.token = token;
  }
  
  private async request<T>(endpoint: string, options: RequestInit = {}): Promise<T> {
    const url = `${this.baseURL}${endpoint}`;
    const headers: HeadersInit = {
      'Content-Type': 'application/json',
      ...options.headers,
    };
    
    if (this.token) {
      headers['Authorization'] = `Bearer ${this.token}`;
    }
    
    try {
      const response = await fetch(url, {
        ...options,
        headers,
      });
      
      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
      }
      
      return await response.json();
    } catch (error) {
      console.error('API request failed:', error);
      throw error;
    }
  }
  
  async get<T>(endpoint: string): Promise<T> {
    return this.request<T>(endpoint);
  }
  
  async post<T>(endpoint: string, data: any): Promise<T> {
    return this.request<T>(endpoint, {
      method: 'POST',
      body: JSON.stringify(data),
    });
  }
}

// 单例模式的应用
class ConfigManager {
  private static instance: ConfigManager;
  private config: Map<string, any> = new Map();
  
  private constructor() {
    // 私有构造函数,防止外部实例化
  }
  
  static getInstance(): ConfigManager {
    if (!ConfigManager.instance) {
      ConfigManager.instance = new ConfigManager();
    }
    return ConfigManager.instance;
  }
  
  set(key: string, value: any): void {
    this.config.set(key, value);
  }
  
  get(key: string): any {
    return this.config.get(key);
  }
}

最佳实践和注意事项

1. 组合优于继承

typescript 复制代码
// 不推荐:过度使用继承
class AnimalWithSwim extends Animal {
  swim() { /* ... */ }
}

class AnimalWithFly extends Animal {
  fly() { /* ... */ }
}

class AnimalWithBoth extends Animal {
  swim() { /* ... */ }
  fly() { /* ... */ }
}

// 推荐:使用组合
class Swimmer {
  swim() {
    console.log("Swimming...");
  }
}

class Flyer {
  fly() {
    console.log("Flying...");
  }
}

class Duck extends Animal {
  private swimmer = new Swimmer();
  private flyer = new Flyer();
  
  swim() {
    this.swimmer.swim();
  }
  
  fly() {
    this.flyer.fly();
  }
}

2. 合理使用Getter和Setter

typescript 复制代码
class User {
  private _email: string = "";
  
  constructor(public name: string) {}
  
  get email(): string {
    return this._email;
  }
  
  set email(value: string) {
    if (this.isValidEmail(value)) {
      this._email = value;
    } else {
      throw new Error("Invalid email address");
    }
  }
  
  private isValidEmail(email: string): boolean {
    const emailRegex = /^[^\s@]+@[^\s@]+.[^\s@]+$/;
    return emailRegex.test(email);
  }
}

const user = new User("Alice");
user.email = "alice@example.com"; // ✅ 有效
// user.email = "invalid-email";   // ❌ 抛出错误

3. 接口与类的结合

typescript 复制代码
interface IRepository<T> {
  findById(id: number): T | null;
  save(entity: T): void;
  delete(id: number): boolean;
}

class UserRepository implements IRepository<User> {
  private users: Map<number, User> = new Map();
  private nextId: number = 1;
  
  findById(id: number): User | null {
    return this.users.get(id) || null;
  }
  
  save(user: User): void {
    if (!user.id) {
      user.id = this.nextId++;
    }
    this.users.set(user.id, user);
  }
  
  delete(id: number): boolean {
    return this.users.delete(id);
  }
}

结语

TypeScript的类为我们提供了强大的面向对象编程能力,从简单的数据封装到复杂的系统架构,类都能发挥重要作用。通过合理的类设计,我们可以创建出更加健壮、可维护的应用程序。

记住,类的设计不是越多越好,而是要找到适合项目需求的平衡点。希望今天的分享能帮助你在实际项目中更好地运用TypeScript类!

⭐ 写在最后

请大家不吝赐教,在下方评论或者私信我,十分感谢🙏🙏🙏.

✅ 认为我某个部分的设计过于繁琐,有更加简单或者更高逼格的封装方式

✅ 认为我部分代码过于老旧,可以提供新的API或最新语法

✅ 对于文章中部分内容不理解

✅ 解答我文章中一些疑问

✅ 认为某些交互,功能需要优化,发现BUG

✅ 想要添加新功能,对于整体的设计,外观有更好的建议

✅ 一起探讨技术加qq交流群:906392632

最后感谢各位的耐心观看,既然都到这了,点个 👍赞再走吧!

相关推荐
鹏多多3 小时前
React项目使用useMemo优化性能指南和应用场景
前端·javascript·react.js
dllxhcjla3 小时前
css第一天
java·前端·css
charlie1145141913 小时前
CSS学习笔记6:定位与布局
前端·css·笔记·学习·css3·教程
自由日记3 小时前
css学习盒模型:
前端·css·学习
午安~婉3 小时前
浏览器与网络
前端·javascript·网络·http·浏览器
岁月宁静3 小时前
大规模图片列表性能优化:基于 IntersectionObserver 的懒加载与滚动加载方案
前端·javascript·vue.js
爆爆凯3 小时前
Spring Boot Web上下文工具类详解:获取Request、Response和参数
前端·spring boot·后端
IT_陈寒3 小时前
7个Java Stream API的隐藏技巧,让你的代码效率提升50%
前端·人工智能·后端
一 乐3 小时前
医疗保健|医疗养老|基于Java+vue的医疗保健系统(源码+数据库+文档)
java·前端·数据库·vue.js·毕设