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

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

相关推荐
WeiXiao_Hyy3 分钟前
成为 Top 1% 的工程师
java·开发语言·javascript·经验分享·后端
吃杠碰小鸡20 分钟前
高中数学-数列-导数证明
前端·数学·算法
kingwebo'sZone26 分钟前
C#使用Aspose.Words把 word转成图片
前端·c#·word
xjt_09011 小时前
基于 Vue 3 构建企业级 Web Components 组件库
前端·javascript·vue.js
我是伪码农1 小时前
Vue 2.3
前端·javascript·vue.js
夜郎king1 小时前
HTML5 SVG 实现日出日落动画与实时天气可视化
前端·html5·svg 日出日落
辰风沐阳1 小时前
JavaScript 的宏任务和微任务
javascript
夏幻灵2 小时前
HTML5里最常用的十大标签
前端·html·html5
冰暮流星2 小时前
javascript之二重循环练习
开发语言·javascript·数据库
Mr Xu_3 小时前
Vue 3 中 watch 的使用详解:监听响应式数据变化的利器
前端·javascript·vue.js