MST

星途 面试题库

面试题:TypeScript类继承与组合在复杂业务场景下的选择

假设你正在开发一个电商平台,其中商品模块存在多种类型的商品(如实体商品、虚拟商品等),每种商品有不同的属性和行为。在这种复杂业务场景下,从代码维护性、扩展性以及性能方面考虑,你会如何选择使用类继承还是组合设计模式来构建商品模块?请详细说明理由,并给出相应的TypeScript代码示例。
11.5万 热度难度
前端开发TypeScript

知识考点

AI 面试

面试题答案

一键面试

选择组合设计模式

  1. 代码维护性
    • 继承:继承会导致类之间紧密耦合。如果基类发生变化,所有子类可能受到影响,需要大量修改代码。例如,若商品基类添加一个新属性,所有继承自它的实体商品类和虚拟商品类等都要重新审视和调整。
    • 组合:组合将不同功能封装在各自独立的类中,通过组合关系将这些类组合在一起。当某个功能需要修改时,只需要修改对应的类,不会影响其他无关类,代码维护更加容易。
  2. 扩展性
    • 继承:在增加新的商品类型时,需要创建新的子类,若继承体系复杂,新子类的创建可能需要考虑多重继承关系等复杂情况,扩展性受限。
    • 组合:可以轻松添加新的功能类,并通过组合方式将其添加到商品类中,而不需要修改现有类的继承结构,扩展性强。
  3. 性能
    • 继承:在继承体系较深时,可能存在内存浪费和性能开销,因为子类可能继承了一些不需要的属性和方法。
    • 组合:按需组合所需功能,不会产生不必要的继承开销,性能更好。

TypeScript代码示例

// 定义实体商品的属性和行为类
class PhysicalProductDetails {
    weight: number;
    constructor(weight: number) {
        this.weight = weight;
    }
    getWeight() {
        return this.weight;
    }
}

// 定义虚拟商品的属性和行为类
class DigitalProductDetails {
    fileSize: number;
    constructor(fileSize: number) {
        this.fileSize = fileSize;
    }
    getFileSize() {
        return this.fileSize;
    }
}

// 商品基类,使用组合方式
class Product {
    name: string;
    price: number;
    private details: PhysicalProductDetails | DigitalProductDetails | null;
    constructor(name: string, price: number) {
        this.name = name;
        this.price = price;
        this.details = null;
    }

    setPhysicalDetails(weight: number) {
        this.details = new PhysicalProductDetails(weight);
    }

    setDigitalDetails(fileSize: number) {
        this.details = new DigitalProductDetails(fileSize);
    }

    getDetails() {
        return this.details;
    }
}

// 使用示例
let physicalProduct = new Product('Book', 20);
physicalProduct.setPhysicalDetails(0.5);
console.log(physicalProduct.getDetails()?.getWeight());

let digitalProduct = new Product('E - book', 15);
digitalProduct.setDigitalDetails(5);
console.log(digitalProduct.getDetails()?.getFileSize());

对比继承的简单示例(仅作对比参考)

// 商品基类
class BaseProduct {
    name: string;
    price: number;
    constructor(name: string, price: number) {
        this.name = name;
        this.price = price;
    }
}

// 实体商品子类
class PhysicalProduct extends BaseProduct {
    weight: number;
    constructor(name: string, price: number, weight: number) {
        super(name, price);
        this.weight = weight;
    }
    getWeight() {
        return this.weight;
    }
}

// 虚拟商品子类
class DigitalProduct extends BaseProduct {
    fileSize: number;
    constructor(name: string, price: number, fileSize: number) {
        super(name, price);
        this.fileSize = fileSize;
    }
    getFileSize() {
        return this.fileSize;
    }
}

从上述代码可以看出,继承方式下子类紧密依赖基类,而组合方式更灵活,便于维护和扩展。