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}`);
}
}
实现规则:
- 类必须实现接口中所有非可选属性和方法
- 方法的参数类型和返回类型必须匹配
- 类可以有额外的属性和方法
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 { /* ... */ }
};
语法顺序: 必须先 extends 后 implements
// ✅ 正确
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 |
| 构造函数 | 可以有 | 不能有 |
| 成员变量 | 可以有 | 只能声明属性类型 |
| 默认实现 | 可以有 | 不能有 |

浙公网安备 33010602011771号