Day 11 - Interface 与类型契约

目标:掌握ArkTS接口的定义、实现、继承,理解接口与抽象类的区别
预计时间:1.5-2小时


课前思考

回顾Day 10我们学习的抽象类:

abstract class Animal {
    abstract name: string;
    abstract makeSound(): void;
    
    move(): void {
        console.log("动物在移动");
    }
}

思考一个问题:

在C++中,一个类可以同时继承多个基类(多重继承)。但ArkTS的类只能extends一个父类。
如果需要让SmartPhone同时具有Camera的能力、GPS的能力、MusicPlayer的能力,该怎么办?

这就引出了今天的主题——Interface(接口)


第一部分:Interface 基础(对标 C++ 纯虚基类)

1.1 为什么需要接口 —— 抽象类的局限性

抽象类的局限:

// 假设我们有多个能力维度
abstract class Camera {
    abstract takePhoto(): void;
}

abstract class GPS {
    abstract getLocation(): string;
}

// ❌ 错误:ArkTS类只能继承一个父类
// class SmartPhone extends Camera, GPS {}  // 语法错误!

对比C++:

// C++支持多重继承(但有菱形继承问题)
class SmartPhone : public Camera, public GPS {
    // ...
};

接口解决方案:

// 定义接口(纯契约,无实现)
interface Camera {
    takePhoto(): void;
}

interface GPS {
    getLocation(): string;
}

interface MusicPlayer {
    playMusic(song: string): void;
}

// ✅ 类可以实现多个接口
class SmartPhone implements Camera, GPS, MusicPlayer {
    takePhoto(): void {
        console.log("拍照:咔嚓!");
    }
    
    getLocation(): string {
        return "GPS坐标:39.9, 116.4";
    }
    
    playMusic(song: string): void {
        console.log(`正在播放:${song}`);
    }
}

核心区别:

特性 C++纯虚基类 ArkTS接口
定义 class + 纯虚函数 interface 关键字
多继承 支持但有风险 类可多实现接口,安全
实现 可包含成员变量和具体方法 只能声明属性和方法签名
构造函数 可以有 不能有

注意:Interface 是结构契约(Structure Contract),不是简单的"类型契约"。它要求实现类必须具有完全匹配的结构(属性名、类型、可选性完全一致)。


1.2 interface 定义语法

基本语法:

// 定义接口
interface Person {
    // 属性声明(只有类型,没有初始值)
    name: string;
    age: number;
    
    // 方法签名(无实现,没有 { ... } 代码块)
    sayHello(): void;
    getInfo(): string;
}

// 实际用法:类实现接口
class Student implements Person {
    name: string;
    age: number;
    
    constructor(name: string, age: number) {
        this.name = name;
        this.age = age;
    }
    
    sayHello(): void {
        console.log(`你好,我是${this.name}`);
    }
    
    getInfo(): string {
        return `${this.name}, ${this.age}岁`;
    }
}

对比C++纯虚基类:

class IPerson {
public:
    std::string name;
    int age;
    
    virtual void sayHello() = 0;
    virtual std::string getInfo() = 0;
    virtual ~IPerson() = default;
};

// 实现类必须继承
class PersonImpl : public IPerson {
public:
    void sayHello() override {
        std::cout << "你好,我是" << name << std::endl;
    }
    
    std::string getInfo() override {
        return name + ", " + std::to_string(age) + "岁";
    }
};

关键差异:

  • C++纯虚基类可以包含成员变量,ArkTS接口只能声明属性类型
  • C++需要虚析构函数,ArkTS接口无此概念
  • ArkTS接口更轻量,纯粹是类型契约

1.3 可选属性

使用 ? 标记可选属性:

interface Product {
    id: number;
    name: string;
    price: number;
    description?: string;      // 可选属性
    discountPrice?: number;    // 可选属性
}

// 可以不带可选属性
let product1: Product = {
    id: 1001,
    name: "手机",
    price: 2999
};

// 也可以带可选属性
let product2: Product = {
    id: 1002,
    name: "笔记本",
    price: 5999,
    description: "高性能轻薄本",
    discountPrice: 5499
};

对比C++:

// C++没有直接的可选属性语法,通常用默认值或std::optional(C++17)
class Product {
public:
    int id;
    std::string name;
    double price;
    std::optional<std::string> description;  // C++17
    std::optional<double> discountPrice;
};

ArkTS特点:

  • ? 语法简洁直观
  • 编译器会检查可选属性的存在性

1.4 函数类型属性

接口中可以声明函数类型的属性:

interface Calculator {
    // 普通属性
    model: string;
    
    // 函数类型属性
    add: (a: number, b: number) => number;
    subtract: (a: number, b: number) => number;
    
    // 可选的函数类型属性
    multiply?: (a: number, b: number) => number;
}

// 实现
let simpleCalc: Calculator = {
    model: "基础版",
    add: (a: number, b: number): number => a + b,
    subtract: (a: number, b: number): number => a - b
    // multiply 可选,可以不提供
};

let advancedCalc: Calculator = {
    model: "高级版",
    add: (a: number, b: number): number => a + b,
    subtract: (a: number, b: number): number => a - b,
    multiply: (a: number, b: number): number => a * b
};

对比C++:

// C++使用std::function或函数指针
class Calculator {
public:
    std::string model;
    std::function<int(int, int)> add;
    std::function<int(int, int)> subtract;
    std::function<int(int, int)> multiply;  // 可空
};

第二部分:Class 实现 Interface

2.1 implements 语法

类使用 implements 关键字实现接口:

interface Drawable {
    draw(): void;
}

interface Resizable {
    resize(width: number, height: number): void;
}

// 类实现单个接口
class Circle implements Drawable {
    private radius: number;
    
    constructor(radius: number) {
        this.radius = radius;
    }
    
    // 必须实现接口方法
    draw(): void {
        console.log(`绘制圆形,半径:${this.radius}`);
    }
}

// 类实现多个接口
class Rectangle implements Drawable, Resizable {
    private width: number;
    private height: number;
    
    constructor(width: number, height: number) {
        this.width = width;
        this.height = height;
    }
    
    draw(): void {
        console.log(`绘制矩形:${this.width} x ${this.height}`);
    }
    
    resize(width: number, height: number): void {
        this.width = width;
        this.height = height;
        console.log(`调整大小为:${width} x ${height}`);
    }
}

实现规则:

  1. 类必须实现接口中所有非可选属性和方法
  2. 方法的参数类型和返回类型必须匹配
  3. 类可以有额外的属性和方法

2.2 实现多个接口

对标C++多重继承纯虚基类:

interface Flyable {
    fly(): void;
    maxAltitude: number;
}

interface Swimmable {
    swim(): void;
    maxDepth: number;
}

interface Walkable {
    walk(): void;
    speed: number;
}

// 类实现多个接口(类似C++多重继承多个纯虚基类)
class Duck implements Flyable, Swimmable, Walkable {
    maxAltitude: number = 1000;
    maxDepth: number = 5;
    speed: number = 10;
    
    fly(): void {
        console.log("鸭子飞翔,最高" + String(this.maxAltitude) + "米");
    }
    
    swim(): void {
        console.log("鸭子游泳,最深" + String(this.maxDepth) + "米");
    }
    
    walk(): void {
        console.log("鸭子走路,速度" + String(this.speed) + "km/h");
    }
}

对比C++:

class IFlyable {
public:
    int maxAltitude;
    virtual void fly() = 0;
    virtual ~IFlyable() = default;
};

class ISwimmable {
public:
    int maxDepth;
    virtual void swim() = 0;
    virtual ~ISwimmable() = default;
};

class IWalkable {
public:
    int speed;
    virtual void walk() = 0;
    virtual ~IWalkable() = default;
};

// C++多重继承(注意:可能有菱形继承问题)
class Duck : public IFlyable, public ISwimmable, public IWalkable {
public:
    Duck() {
        maxAltitude = 1000;
        maxDepth = 5;
        speed = 10;
    }
    
    void fly() override { /* ... */ }
    void swim() override { /* ... */ }
    void walk() override { /* ... */ }
};

ArkTS优势:

  • 没有菱形继承问题(接口无实现,无状态)
  • 编译器强制检查所有接口的实现
  • 更清晰、更安全的契约机制

2.3 实现多个接口时的成员合并规则

当类实现多个接口时,如果多个接口有同名成员(属性或方法),必须满足类型兼容,合并后取最严格的要求。

属性合并

情况1:同名同类型,都必需

interface A { value: number; }      // 必需
interface B { value: number; }      // 必需

// ✅ 编译通过,合并后为必需
class C implements A, B {
    value: number = 0;  // 必需
}

情况2:同名同类型,都可选

interface A { value?: number; }      // 可选
interface B { value?: number; }      // 可选

// ✅ 编译通过,合并后为可选
class C implements A, B {
    value?: number = 0;  // 可选(或必需也可以)
}

情况3:同名同类型,一个必需一个可选

interface A { value: number; }       // 必需
interface B { value?: number; }      // 可选

// ✅ 编译通过,合并后为必需(取最严格)
class C implements A, B {
    value: number = 0;  // 必需,同时满足两者
}

情况4:同名不同类型

interface A { value: number; }       // number
interface B { value: string; }       // string

// ❌ 编译错误:value类型不兼容(number vs string)
// class C implements A, B {}

方法合并

方法的合并规则与属性相同:同名方法的参数类型返回类型必须完全一致。

interface A {
    process(data: string): number;   // 参数 string,返回 number
}

interface B {
    process(data: string): number;   // 必须完全一致
}

// ✅ 编译通过
class C implements A, B {
    process(data: string): number {
        return data.length;
    }
}

方法签名不一致会报错:

interface A {
    process(data: string): number;
}

interface B {
    process(data: number): string;   // ❌ 参数和返回类型都不同
}

// ❌ 编译错误:方法签名不兼容
// class C implements A, B {}

可选方法

接口中的方法也可以是可选的:

interface A {
    requiredMethod(): void;          // 必需方法
    optionalMethod?(): void;         // 可选方法(带 ?)
}

class B implements A {
    requiredMethod(): void {
        console.log("必须实现");
    }
    // optionalMethod 可以不实现
}

合并原则:

  • 类型(属性类型 / 方法签名):必须完全相同
  • 可选性:任一必需 → 合并后必需;全部可选 → 合并后可选

2.4 同时继承和实现

类可以 extends 一个父类,同时 implements 多个接口:

// 基类
class Animal {
    name: string;
    
    constructor(name: string) {
        this.name = name;
    }
    
    eat(): void {
        console.log(`${this.name} 在吃东西`);
    }
}

// 接口定义额外能力
interface Flyable {
    fly(): void;
}

interface EggLayer {
    layEggs(): void;
}

// 类继承Animal,同时实现多个接口
class Bird extends Animal implements Flyable, EggLayer {
    wingSpan: number;
    
    constructor(name: string, wingSpan: number) {
        super(name);
        this.wingSpan = wingSpan;
    }
    
    // 实现Flyable接口
    fly(): void {
        console.log(`${this.name} 展翅飞翔,翼展${this.wingSpan}米`);
    }
    
    // 实现EggLayer接口
    layEggs(): void {
        console.log(`${this.name} 在下蛋`);
    }
    
    // Bird特有的方法
    chirp(): void {
        console.log(`${this.name}:叽叽叽`);
    }
}

// 使用
let sparrow = new Bird("麻雀", 0.25);
sparrow.eat();      // 继承自Animal
sparrow.fly();      // 实现自Flyable
sparrow.layEggs();  // 实现自EggLayer
sparrow.chirp();    // Bird特有

对比C++:

class Animal {
public:
    std::string name;
    Animal(const std::string& name) : name(name) {}
    virtual void eat() { /* ... */ }
};

class IFlyable {
public:
    virtual void fly() = 0;
    virtual ~IFlyable() = default;
};

class IEggLayer {
public:
    virtual void layEggs() = 0;
    virtual ~IEggLayer() = default;
};

// C++:继承一个类,实现多个纯虚基类
class Bird : public Animal, public IFlyable, public IEggLayer {
public:
    double wingSpan;
    
    Bird(const std::string& name, double wingSpan) 
        : Animal(name), wingSpan(wingSpan) {}
    
    void fly() override { /* ... */ }
    void layEggs() override { /* ... */ }
};

语法顺序: 必须先 extendsimplements

// ✅ 正确
class MyClass extends Base implements A, B {}

// ❌ 错误
// class MyClass implements A, B extends Base {}

第三部分:Interface 继承

3.1 接口继承接口

接口可以使用 extends 继承其他接口:

// 基础接口
interface Shape {
    color: string;
    draw(): void;
}

// 接口继承接口
interface Circle extends Shape {
    radius: number;
    getArea(): number;
}

// Circle 包含:color, radius, draw(), getArea()
let myCircle: Circle = {
    color: "red",
    radius: 10,
    draw(): void {
        console.log(`绘制${this.color}圆形`);
    },
    getArea(): number {
        return 3.14 * this.radius * this.radius;
    }
};

对比C++:

// C++接口继承也是用public继承
class IShape {
public:
    std::string color;
    virtual void draw() = 0;
    virtual ~IShape() = default;
};

class ICircle : public IShape {
public:
    double radius;
    virtual double getArea() = 0;
};

3.2 接口的多重继承

接口可以多继承(类不行):

interface HasName {
    name: string;
}

interface HasId {
    id: number;
}

interface HasTimestamp {
    createdAt: number;
    updatedAt: number;
}

// 接口继承多个接口(这是允许的!)
interface Entity extends HasName, HasId, HasTimestamp {
    isActive: boolean;
}

// Entity 现在包含所有属性
let user: Entity = {
    name: "张三",
    id: 1001,
    createdAt: Date.now(),
    updatedAt: Date.now(),
    isActive: true
};

为什么接口可以多继承?

  • 接口只有类型声明,没有实现
  • 没有状态(成员变量初始化值)
  • 不存在菱形继承的歧义问题


第四部分:Abstract Class vs Interface

4.1 能力对比表

特性 Abstract Class Interface
构造函数 可以有 不能有
具体方法 可以有(带实现) 不能有(纯签名)
具体属性 可以有(带初始值) 只能声明类型
抽象方法 可以有 所有方法都是抽象的
访问修饰符 支持 public/private/protected 不能写修饰符,但实现类中对应成员必须是 public
状态保持 可以维护状态 无状态
多继承 类只能继承一个 接口可以多继承/多实现
实例化 不能直接实例化 不能直接实例化

4.2 如何选择

决策口诀: "有实现用abstract,纯契约用interface"

使用 Abstract Class 的场景:

// 有共享实现,需要复用代码
abstract class DataSource {
    protected cache: Map<string, object> = new Map();
    
    // 具体方法:共享实现
    protected getFromCache(key: string): object | undefined {
        return this.cache.get(key);
    }
    
    protected addToCache(key: string, value: object): void {
        this.cache.set(key, value);
    }
    
    // 抽象方法:子类必须实现
    abstract connect(): void;
    abstract fetchData(query: string): object[];
}

class DatabaseSource extends DataSource {
    connect(): void {
        // 实现数据库连接
        console.log("连接数据库");
    }
    
    fetchData(query: string): object[] {
        // 使用父类的缓存机制
        let cached = this.getFromCache(query);
        if (cached !== undefined) {
            return cached as object[];
        }
        
        let result: object[] = []; // 从数据库获取
        this.addToCache(query, result);
        return result;
    }
}

使用 Interface 的场景:

// 纯契约定义,跨类族的能力规范
interface Persistable {
    toJSON(): string;
    fromJSON(json: string): void;
}

interface Validatable {
    validate(): boolean;
    getErrors(): string[];
}

interface Serializable {
    serialize(): Uint8Array;
}

// 不同类族的类可以实现相同接口
class User implements Persistable, Validatable {
    // User的实现...
    toJSON(): string { return ""; }
    fromJSON(json: string): void {}
    validate(): boolean { return true; }
    getErrors(): string[] { return []; }
}

class Product implements Persistable, Validatable, Serializable {
    // Product的实现...
    toJSON(): string { return ""; }
    fromJSON(json: string): void {}
    validate(): boolean { return true; }
    getErrors(): string[] { return []; }
    serialize(): Uint8Array { return new Uint8Array(); }
}

4.3 实际场景演示

场景:设计一个插件系统

// 插件基础类:提供通用功能
abstract class Plugin {
    readonly name: string;
    readonly version: string;
    private isEnabled: boolean = false;
    
    constructor(name: string, version: string) {
        this.name = name;
        this.version = version;
    }
    
    // 模板方法:定义初始化流程
    initialize(): void {
        console.log(`初始化插件:${this.name} v${this.version}`);
        this.onInitialize();
        this.isEnabled = true;
    }
    
    destroy(): void {
        console.log(`销毁插件:${this.name}`);
        this.onDestroy();
        this.isEnabled = false;
    }
    
    // 抽象方法:子类必须实现
    abstract onInitialize(): void;
    abstract onDestroy(): void;
    
    // 具体方法
    getStatus(): string {
        return this.isEnabled ? "运行中" : "已停止";
    }
}

// 接口定义特定能力
interface DataProcessor {
    processData(input: object): object;
    supportsFormat(format: string): boolean;
}

interface UIComponent {
    render(): void;
    getUIConfig(): object;
}

// 具体插件:继承Plugin基类,实现特定接口
class CSVProcessorPlugin extends Plugin implements DataProcessor {
    onInitialize(): void {
        console.log("CSV处理器准备就绪");
    }
    
    onDestroy(): void {
        console.log("CSV处理器已清理");
    }
    
    processData(input: object): object {
        console.log("处理CSV数据");
        return input;
    }
    
    supportsFormat(format: string): boolean {
        return format === "csv";
    }
}

class DashboardPlugin extends Plugin implements UIComponent, DataProcessor {
    onInitialize(): void {
        console.log("仪表盘插件准备就绪");
    }
    
    onDestroy(): void {
        console.log("仪表盘插件已清理");
    }
    
    render(): void {
        console.log("渲染仪表盘UI");
    }
    
    getUIConfig(): object {
        return { type: "dashboard" };
    }
    
    processData(input: object): object {
        console.log("仪表盘数据处理");
        return input;
    }
    
    supportsFormat(format: string): boolean {
        return format === "json" || format === "csv";
    }
}

// 插件管理器
class PluginManager {
    private plugins: Plugin[] = [];
    private dataProcessors: DataProcessor[] = [];
    
    registerPlugin(plugin: Plugin): void {
        this.plugins.push(plugin);
        plugin.initialize();
        
        // 如果插件实现了DataProcessor,额外注册
        if (this.isDataProcessor(plugin)) {
            this.dataProcessors.push(plugin);
        }
    }
    
    private isDataProcessor(plugin: Plugin): plugin is Plugin & DataProcessor {
        return "processData" in plugin && "supportsFormat" in plugin;
    }
    
    processDataByFormat(format: string, data: object): object | null {
        for (let processor of this.dataProcessors) {
            if (processor.supportsFormat(format)) {
                return processor.processData(data);
            }
        }
        return null;
    }
}

第五部分:小结与练习

知识点对比总结表

概念 核心语法 适用场景
Interface interface A { ... } 定义对象结构、类契约
实现接口 class C implements A, B 给类添加能力
接口继承 interface A extends B 扩展接口定义
抽象类 abstract class A 共享代码实现
type别名 type ID = number 基本类型、联合类型、函数类型

Interface vs Abstract Class 速记:

  • Interface = "能做什么"(能力清单)
  • Abstract Class = "是什么 + 能做什么"(身份 + 能力 + 默认实现)

练习题

练习1:基础接口定义

// 1. 定义Vehicle接口:
//    - 属性:brand(字符串), model(字符串), year(数字)
//    - 可选属性:color(字符串)
//    - 方法:start(): void, stop(): void

// 2. 定义Electric接口(电动车特性):
//    - 属性:batteryCapacity(数字,单位kWh)
//    - 方法:charge(hours: number): void

// 3. 定义Gasoline接口(燃油车特性):
//    - 属性:tankCapacity(数字,单位L)
//    - 方法:refuel(liters: number): void

// 4. 创建Car类实现Vehicle接口
// 5. 创建ElectricCar类继承Car并实现Electric接口
// 6. 创建HybridCar类继承Car并实现Electric和Gasoline接口
点击查看答案
interface Vehicle {
    brand: string;
    model: string;
    year: number;
    color?: string;
    start(): void;
    stop(): void;
}

interface Electric {
    batteryCapacity: number;
    charge(hours: number): void;
}

interface Gasoline {
    tankCapacity: number;
    refuel(liters: number): void;
}

class Car implements Vehicle {
    brand: string;
    model: string;
    year: number;

    constructor(b: string, m: string, y: number) {
        this.brand = b;
        this.model = m;
        this.year = y;
    }

    start(): void { console.log("car start"); }
    stop(): void { console.log("car stop"); }
}

class ElectricCar extends Car implements Electric {
    batteryCapacity: number;

    constructor(b: string, m: string, y: number, bc: number) {
        super(b, m, y);
        this.batteryCapacity = bc;
    }

    charge(hours: number): void {}
}

class HybridCar extends Car implements Electric, Gasoline {
    batteryCapacity: number;
    tankCapacity: number;

    constructor(b: string, m: string, y: number, bc: number, tc: number) {
        super(b, m, y);
        this.batteryCapacity = bc;
        this.tankCapacity = tc;
    }

    charge(hours: number): void {}
    refuel(liters: number): void {}
}

练习2:接口继承与合并

// 1. 定义基础接口BaseEntity:
//    - 属性:id(数字), createdAt(数字)

// 2. 定义SoftDelete接口:
//    - 属性:isDeleted(布尔值), deletedAt?(数字)

// 3. 定义Versioned接口:
//    - 属性:version(数字)

// 4. 定义Auditable接口继承BaseEntity,并包含:
//    - 属性:createdBy(字符串), updatedAt(数字)

// 5. 定义FullEntity接口继承Auditable、SoftDelete、Versioned
//    思考:这些接口的属性会合并吗?有没有冲突?

// 6. 实现一个Document类实现FullEntity接口
点击查看答案
interface BaseEntity {
    id: number;
    createAt: number;
}

interface SoftDelete {
    isDeleted: boolean;
    deleteAt?: number;
}

interface Versioned {
    version: number;
}

interface Auditable extends BaseEntity {
    createBy: string;
    updateAt: number;
}

interface FullEntity extends Auditable, SoftDelete, Versioned {}

class Document implements FullEntity {
    createBy: string;
    updateAt: number;
    id: number;
    createAt: number;
    isDeleted: boolean;
    version: number;
    deleteAt?: number;

    constructor() {
        this.createBy = "thomas";
        this.updateAt = 1;
        this.id = 1;
        this.createAt = 1;
        this.isDeleted = false;
        this.version = 1;
    }
}

分析:

  • 属性会正常合并,没有冲突
  • FullEntity 最终包含:id, createAt, createBy, updateAt, isDeleted, deleteAt?, version
  • deleteAt 保持可选,其他都是必需的

练习3:接口与抽象类结合

// 1. 定义Logger抽象类:
//    - 属性:name(字符串), level(字符串,默认"info")
//    - 方法:log(message: string): void(具体方法,打印日志)
//    - 抽象方法:writeOutput(message: string): void

// 2. 定义Rotatable接口:
//    - 方法:rotate(): void

// 3. 定义FileLogger类继承Logger:
//    - 实现writeOutput方法(模拟写入文件)

// 4. 定义RotatingFileLogger类继承FileLogger实现Rotatable接口:
//    - 实现rotate方法(模拟日志轮转)

// 5. 创建实例测试所有功能
点击查看答案
abstract class Logger {
    name: string = "";
    level: string = "info";

    abstract writeOutput(message: string): void;

    log(message: string): void {
        console.log(`[${this.level}]${message}`);
    }
}

interface Rotatable {
    rotate(): void;
}

class FileLogger extends Logger {
    writeOutput(message: string): void {}
}

class RotatingFileLogger extends FileLogger implements Rotatable {
    rotate(): void {}
}

练习4:多接口实现设计

// 设计一个多媒体播放器系统:

// 1. 定义Playable接口(可播放):
//    - 方法:play(), pause(), stop()
//    - 属性:isPlaying(布尔值)

// 2. 定义Recordable接口(可录制):
//    - 方法:startRecording(), stopRecording()
//    - 属性:isRecording(布尔值)

// 3. 定义Streamable接口(可流媒体):
//    - 方法:connect(url: string), disconnect()
//    - 属性:bufferLevel(数字)

// 4. 实现以下类:
//    - AudioPlayer:实现Playable
//    - VideoPlayer:实现Playable和Streamable
//    - ScreenRecorder:实现Recordable和Streamable
//    - LiveStreamer:实现Playable、Recordable、Streamable

// 5. 编写函数统一处理Playable设备
点击查看答案
interface Playable {
    isPlaying: boolean;
    play(): void;
    pause(): void;
    stop(): void;
}

interface Recordable {
    isRecording: boolean;
    startRecording(): void;
    stopRecording(): void;
}

interface Streamable {
    bufferLevel: number;
    connect(url: string): void;
    disconnect(): void;
}

class AudioPlayer implements Playable {
    isPlaying: boolean = false;
    play(): void {}
    pause(): void {}
    stop(): void {}
}

class VideoPlayer implements Playable, Streamable {
    isPlaying: boolean = false;
    bufferLevel: number = 0;
    play(): void {}
    pause(): void {}
    stop(): void {}
    connect(url: string): void {}
    disconnect(): void {}
}

class ScreenRecorder implements Recordable, Streamable {
    isRecording: boolean = false;
    bufferLevel: number = 0;
    startRecording(): void {}
    stopRecording(): void {}
    connect(url: string): void {}
    disconnect(): void {}
}

class LiveStreamer implements Playable, Recordable, Streamable {
    bufferLevel: number = 15;
    isPlaying: boolean = false;
    isRecording: boolean = false;
    connect(url: string): void {}
    disconnect(): void {}
    startRecording(): void {}
    stopRecording(): void {}
    play(): void {}
    pause(): void {}
    stop(): void {}
}

// 统一处理Playable设备
function controlPlayback(device: Playable): void {
    console.log("控制播放设备");
    device.play();
}

// 使用示例
let audio = new AudioPlayer();
let video = new VideoPlayer();
controlPlayback(audio);
controlPlayback(video);

练习5:接口成员合并规则应用

// 分析以下代码,找出问题并修正:

interface ConfigA {
    timeout: number;
    retry: number;        // 必需属性
}

interface ConfigB {
    timeout: number;
    retry?: number;       // 可选属性
}

// 类实现时报错,为什么?如何修正?
class Settings implements ConfigA, ConfigB {
    timeout: number = 5000;
    retry?: number = 3;   // ❌ 报错!
}

// 请提供修正方案

提示:

  • ConfigA 要求 retry 是必需的
  • ConfigB 要求 retry 是可选的
  • 类实现时 retry 的可选性必须满足更严格的要求
点击查看答案

错误原因:
ConfigA 要求 retry 是必需属性,但 Settings 类中将其声明为可选属性 retry?: number,不满足 ConfigA 的要求。

修正方案:

class Settings implements ConfigA, ConfigB {
    timeout: number = 5000;
    retry: number = 3;    // ✅ 正确,改为必需属性
}

核心规则:

  • 接口合并时,同名属性按最严格要求处理
  • 类实现时,属性必须满足所有接口的要求
  • 可选性冲突时,实现类必须使用更严格的(必需)

练习6:设计模式应用

// 使用接口实现策略模式(Strategy Pattern):

// 1. 定义PaymentStrategy接口:
//    - 方法:pay(amount: number): boolean
//    - 方法:getName(): string

// 2. 实现以下策略:
//    - CreditCardStrategy:信用卡支付
//    - AlipayStrategy:支付宝支付
//    - WeChatPayStrategy:微信支付

// 3. 定义PaymentContext类:
//    - 属性:strategy(PaymentStrategy)
//    - 方法:setStrategy(strategy: PaymentStrategy): void
//    - 方法:executePayment(amount: number): void

// 4. 演示运行时切换支付策略
点击查看答案
interface PaymentStrategy {
    pay(amount: number): boolean;
    getName(): string;
}

class CreditCardStrategy implements PaymentStrategy {
    pay(amount: number): boolean {
        console.log(`use ${this.getName()} pay:${amount}`);
        return true;
    }

    getName(): string {
        return "CreditCard";
    }
}

class AlipayStrategy implements PaymentStrategy {
    pay(amount: number): boolean {
        console.log(`use ${this.getName()} pay:${amount}`);
        return true;
    }

    getName(): string {
        return "Alipay";
    }
}

class WeChatPayStrategy implements PaymentStrategy {
    pay(amount: number): boolean {
        console.log(`use ${this.getName()} pay:${amount}`);
        return true;
    }

    getName(): string {
        return "WeChatPay";
    }
}

class PaymentContext {
    strategy: PaymentStrategy;

    constructor(strategy: PaymentStrategy) {
        this.strategy = strategy;
    }

    setStrategy(strategy: PaymentStrategy): void {
        this.strategy = strategy;
    }

    executePayment(amount: number): void {
        this.strategy.pay(amount);
    }
}

// 演示运行时切换
function test(): void {
    let cc = new CreditCardStrategy();
    let ap = new AlipayStrategy();
    let wp = new WeChatPayStrategy();
    
    let pc = new PaymentContext(cc);
    pc.executePayment(12);    // 信用卡支付
    
    pc.setStrategy(ap);
    pc.executePayment(45);    // 支付宝支付
    
    pc.setStrategy(wp);
    pc.executePayment(13);    // 微信支付
}

设计模式优势:

  • 开闭原则:新增支付方式只需添加新类
  • 解耦:PaymentContext 不依赖具体支付方式
  • 运行时切换:动态改变行为

附录:C++ vs ArkTS 接口对比速查

概念 C++ ArkTS
接口定义 纯虚基类 class I { virtual void f() = 0; }; interface I { f(): void; }
实现接口 class C : public I class C implements I
多实现 多重继承(有风险) implements A, B, C(安全)
接口继承 class IA : public IB interface IA extends IB
可选成员 无直接语法 prop?: type
构造函数 可以有 不能有
成员变量 可以有 只能声明属性类型
默认实现 可以有 不能有
posted @ 2026-04-15 16:29  thammer  阅读(2)  评论(0)    收藏  举报