散修带你入门鸿蒙应用开发基础第十四节:抽象与面向接口编程

第十四节:抽象与面向接口编程

炼气十四重天

【学习目标】

  1. 理解接口(interface)的核心价值(能力规范+多维度扩展),掌握ArkTS中接口的定义与实现语法;
  2. 掌握“多接口实现”的标准写法,解决ArkTS“单继承”的核心限制;
  3. 吃透接口的进阶特性(可选属性、接口继承),适配鸿蒙复杂业务场景;
  4. 掌握“面向接口编程”的核心思想,实现“依赖抽象而非具体实现”的工程化设计;
  5. 明确抽象类与接口的选型原则,结合鸿蒙电商场景落地“抽象类定基类、接口定能力”的架构。

【学习重点】

  • 接口核心语法:interface定义规范、implements实现接口、多接口实现的语法规则;
  • 接口进阶特性:可选属性(?)、接口继承(extends);
  • 面向接口编程:接口作为参数/返回值类型,统一调用逻辑,降低代码耦合;
  • 抽象类 vs 接口:“是不是”用抽象类(继承),“有没有”用接口(实现);
  • ArkTS语法约束:instanceof/typeof支持、is/in/&禁用、类型别名(type)的使用限制;
  • 鸿蒙实战:为商品扩展“可秒杀”“可退换”能力,复用抽象类基础,实现抽象类+多接口的灵活架构。

一、工程结构

复制第十三节的ClassObjectDemo_2工程,重命名为ClassObjectDemo_3,新增接口(IReturnable、IPromotion、ISeckill),核心目录如下:

ClassObjectDemo_3/
├── entry/                          # 应用主模块
│   ├── src/main/ets/
│   │   ├── pages/
│   │   │   └── Index.ets           # 测试页面(接口+面向接口编程)
│   │   └── model/
│   │       ├── AbstractGoods.ets   # 抽象父类:商品基类(定基类)
│   │       ├── capability/         # 鸿蒙规范:能力接口目录
│   │       │   ├── IReturnable.ets # 接口:可退换能力
│   │       │   ├── IPromotion.ets  # 接口:基础促销能力
│   │       │   └── ISeckill.ets    # 接口:可秒杀能力(继承IPromotion)
│   │       ├── PhysicalGoods.ets   # 子类:实体商品(实现IReturnable)
│   │       ├── VirtualGoods.ets    # 子类:虚拟商品(无额外能力)
│   │       └── SeckillGoods.ets    # 子类:秒杀商品(实现ISeckill+IReturnable)
│   ├── resources/                  # 资源目录
│   └── module.json5                # 模块配置(API12适配)
└── hvigorfile.ts                   # 构建脚本

二、为什么需要接口?突破单继承限制

2.1 单继承的核心痛点

ArkTS仅支持单继承,即一个类只能继承一个父类。在电商场景中,商品的能力维度是多样的:实体商品需要“可退换”能力,秒杀商品需要“可秒杀+可退换”能力,仅依赖继承无法实现这种多能力组合扩展。

2.2 接口的核心定位

接口的核心价值是定义能力规范,不关注类的具体类型,只关注类具备的能力。一个类可以同时实现多个接口,从而突破单继承的限制,灵活扩展多维度能力。

  • 接口仅定义“能力的规范”(属性名/类型、方法名/参数/返回值),仅支持方法声明,无任何实现逻辑;
  • 类通过implements关键字实现接口,必须严格遵守规范,实现所有必选的属性和方法;
  • 类实现接口时,可同时复用父类(抽象类)的已有实现逻辑。

三、声明接口核心语法(interface)

3.1 接口定义语法

// ArkTS 接口名采用大驼峰命名,以I开头避免重名
export interface ICapabilityName {
  // 必选属性:无初始值,仅约束类型
  属性名称: 数据类型;
  // 可选属性(?):非必须实现,适配灵活场景
  属性名称?: 数据类型;
  // 普通方法:仅定义规范(声明),无实现体
  methodName(param: 数据类型): 返回值;
}

// 接口继承:整合多个接口规范,形成更完整的能力定义
export interface IChildCapability extends IParentCapability1, IParentCapability2 {
  // 新增专属的属性/方法规范
}

3.2 类实现接口语法(implements)

// 实现单个接口(继承抽象基类+实现接口,复用父类已有逻辑)
export class ClassName extends AbstractBaseClass implements ICapability {
  // 实现抽象类抽象方法
  abstractMethod(): void {}
  // 必须实现接口的所有必选属性
  propertyName: 数据类型;
  // 必须实现接口的所有方法(实现具体逻辑)
  methodName(param: 数据类型): 返回值 {

  }
}

// 实现多个接口(核心:突破单继承限制)
export class ClassName extends AbstractBaseClass implements ICapability1, ICapability2 {
  // 实现抽象类的抽象方法
  abstractMethod(): void {}
  // 实现接口1的所有必选成员
  // 实现接口2的所有必选成员
}

3.3 实战1:定义商品能力接口

新增:基础促销能力(IPromotion)

// model/capability/IPromotion.ets
/**
 * 基础促销能力接口
 * 定义:所有促销商品的通用规范
 * 场景:秒杀、满减等促销商品的基础能力
 */
export interface IPromotion {
  // 必选属性:促销折扣率(0~1)
  discountRate: number;
  // 必选属性:促销开始时间
  promoStartTime: Date;
  // 方法声明:校验促销时间是否有效
  checkPromoTimeValid(): boolean;
}

新增:可退换能力(IReturnable)

// model/capability/IReturnable.ets
/**
 * 可退换能力接口
 * 定义:具备退换能力的商品需实现的规范
 * 场景:实体商品/秒杀商品需实现此接口
 */
export interface IReturnable {
  // 必选属性:退换期限(天)
  returnDays: number;
  // 可选属性:是否支持无理由退换
  isSevenDaysReturn?: boolean;
  // 方法声明:校验商品是否可退换
  checkReturnable(): boolean;
  // 方法声明:获取退换说明
  getReturnDesc(): string;
}

新增:可秒杀能力(ISeckill,继承IPromotion)

// model/capability/ISeckill.ets
import { IPromotion } from "./IPromotion";

/**
 * 可秒杀能力接口
 * 定义:具备秒杀能力的商品需实现的规范
 * 场景:秒杀商品专属能力(继承基础促销能力)
 */
export interface ISeckill extends IPromotion {
  // 必选属性:秒杀库存
  seckillStock: number;
  // 方法声明:计算商品秒杀价
  calculateSeckillPrice(): number;
}

四、实战2:类实现多接口

4.1 抽象商品基类(AbstractGoods.ets)

// model/AbstractGoods.ets
/**
 * 商品抽象基类
 * 核心:封装商品通用属性和方法,强制子类实现核心业务逻辑
 */
export abstract class AbstractGoods {
  public name: string; // 商品名称
  public category: string = "鸿蒙周边";
  protected _basePrice: number; // 商品底价(protected仅子类可访问)
  public profitRate: number; // 利润率

  /**
   * 构造函数
   * @param name 商品名称
   * @param basePrice 商品底价
   * @param category 商品分类(可选)
   * @param profitRate 利润率(默认10%)
   */
  constructor(
    name: string,
    basePrice: number,
    category?: string,
    profitRate: number = 0.1
  ) {
    this.name = name;
    this._basePrice = basePrice > 0 ? basePrice : 0; // 底价非负校验
    this.profitRate = profitRate > 0 ? profitRate : 0.1; // 利润率非负校验
    this.category = category?.trim() || this.category;
  }

  // 底价读写器:控制底价的修改逻辑
  get basePrice() {
    return this._basePrice;
  }

  set basePrice(newPrice: number) {
    if (newPrice < 0) {
      console.log(`【${this.name}】商品底价不能为负,修改失败`);
      return;
    }
    this._basePrice = newPrice;
  }

  // 基础信息打印方法
  printBaseInfo(): void {
    console.log(`
      商品名称:${this.name}
      商品分类:${this.category}
      商品底价:${this._basePrice}元
      利润利率:${this.profitRate * 100}%
    `);
  }

  // 抽象方法:强制子类实现售价计算
  abstract calculateSellingPrice(): number;

  // 抽象方法:强制子类实现库存校验
  abstract checkStock(amount: number): boolean;
}

4.2 实体商品(PhysicalGoods.ets)

// model/PhysicalGoods.ets
import { AbstractGoods } from './AbstractGoods';
import { IReturnable } from './capability/IReturnable';

/**
 * 实体商品类
 * 继承:AbstractGoods(商品基类)
 * 实现:IReturnable(可退换能力)
 */
export class PhysicalGoods extends AbstractGoods implements IReturnable {
  public stock: number; // 库存(实体商品专属属性)
  public weight: number; // 重量(kg,实体商品专属属性)

  // 实现IReturnable接口必选属性
  returnDays: number = 7; // 默认7天退换
  isSevenDaysReturn: boolean = true; // 默认支持无理由退换

  /**
   * 构造函数
   * @param name 商品名称
   * @param basePrice 商品底价
   * @param stock 库存数量
   * @param weight 商品重量
   * @param category 商品分类(可选)
   * @param profitRate 利润率(默认10%)
   */
  constructor(
    name: string,
    basePrice: number,
    stock: number,
    weight: number,
    category?: string,
    profitRate: number = 0.1
  ) {
    super(name, basePrice, category, profitRate);
    this.stock = Math.max(stock, 0); // 库存非负处理
    this.weight = Math.max(weight, 0); // 重量非负处理
  }

  // 实现抽象类方法:计算实体商品售价(底价+利润率)
  override calculateSellingPrice(): number {
    return parseFloat((this._basePrice * (1 + this.profitRate)).toFixed(2));
  }

  // 重写父类的printBaseInfo方法:整合基础信息+实体专属信息+退换信息
  override printBaseInfo(): void {
    super.printBaseInfo();
    // 实体商品专属基础信息
    console.log(`
      库存数量:${this.stock}件
      商品重量:${this.weight.toFixed(2)}kg
    `);
    // 实体商品退换能力信息
    const returnDesc = this.getReturnDesc();
    console.log(`
      【实体商品退换信息】
      退换规则:${returnDesc}
      当前可退换:${this.checkReturnable() ? "✅ 是" : "❌ 否"}
    `);
  }

  // 实现IReturnable接口方法:校验是否可退换(库存>0则可退换)
  public checkReturnable(): boolean {
    return this.stock > 0;
  }

  // 实现IReturnable接口方法:生成退换说明
  public getReturnDesc(): string {
    const reasonDesc = this.isSevenDaysReturn ? "支持无理由" : "不支持无理由";
    return `${this.name}:${reasonDesc},退换期限${this.returnDays}天`;
  }

  /**
   * 实现抽象类的checkStock方法:实体商品库存校验
   * @param amount 购买数量
   */
  override checkStock(amount: number): boolean {
    if (amount <= 0) {
      console.log(`【${this.name}】校验失败:购买数量必须大于0`);
      return false;
    }
    if (amount > this.stock) {
      console.log(`【${this.name}】校验失败:库存不足,当前库存${this.stock}件,请求${amount}件`);
      return false;
    }
    console.log(`【${this.name}】库存校验通过,剩余库存${this.stock}件`);
    return true;
  }

  /**
   * 个性方法:基于库存校验的扣减逻辑
   * @param amount 要扣减的库存数量
   */
  reduceStock(amount: number): void {
    if (this.checkStock(amount)) {
      this.stock -= amount;
      console.log(`【${this.name}】库存扣减成功,剩余库存:${this.stock}件`);
    }
  }
}

4.3 秒杀商品(SeckillGoods.ets)

// model/SeckillGoods.ets
import { AbstractGoods } from './AbstractGoods';
import { IReturnable } from './capability/IReturnable';
import { ISeckill } from './capability/ISeckill';

/**
 * 秒杀商品类
 * 继承:AbstractGoods(商品基类)
 * 实现:ISeckill(可秒杀能力)+ IReturnable(可退换能力)
 * 核心:突破单继承,实现双能力扩展
 */
export class SeckillGoods extends AbstractGoods implements ISeckill, IReturnable {
  // 实现ISeckill接口属性(含继承自IPromotion的属性)
  discountRate: number;
  seckillStock: number;
  promoStartTime: Date; // 秒杀开始时间(IPromotion接口属性)
  seckillEndTime: Date; // 秒杀结束时间(配套属性,增强业务逻辑)

  // 实现IReturnable接口属性(秒杀商品特殊规则)
  returnDays: number = 3; // 仅3天退换期
  isSevenDaysReturn: boolean = false; // 不支持无理由退换

  /**
   * 构造函数
   * @param name 商品名称
   * @param basePrice 商品底价
   * @param seckillStock 秒杀库存
   * @param discountRate 秒杀折扣率
   * @param promoStartTime 秒杀开始时间
   * @param seckillEndTime 秒杀结束时间
   * @param category 商品分类(可选,默认“秒杀商品”)
   * @param profitRate 利润率(默认10%)
   */
  constructor(
    name: string,
    basePrice: number,
    seckillStock: number,
    discountRate: number,
    promoStartTime: Date,
    seckillEndTime: Date,
    category?: string,
    profitRate: number = 0.1
  ) {
    super(name, basePrice, category || "秒杀商品", profitRate);
    // 库存非负处理
    this.seckillStock = Math.max(seckillStock, 0);
    // 折扣率约束:0.1~1(最低1折,最高原价)
    this.discountRate = Math.max(0.1, Math.min(discountRate, 1));
    // 秒杀时间校验:开始时间不能早于当前时间,结束时间不能早于开始时间
    const now = new Date();
    this.promoStartTime = promoStartTime < now ? now : promoStartTime;
    this.seckillEndTime = seckillEndTime < this.promoStartTime ? new Date(this.promoStartTime.getTime() + 24 * 3600 * 1000) : seckillEndTime;
  }

  // 实现抽象类方法:计算秒杀商品常规售价
  override calculateSellingPrice(): number {
    return parseFloat((this._basePrice * (1 + this.profitRate)).toFixed(2));
  }

  // 重写父类的printBaseInfo方法:整合基础信息+秒杀专属信息+能力信息
  override printBaseInfo(): void {
    super.printBaseInfo();
    // 秒杀商品专属基础信息
    console.log(`
      秒杀库存:${this.seckillStock}件
      秒杀折扣:${(this.discountRate * 10).toFixed(1)}折
      秒杀时段:${this.promoStartTime.toLocaleString()} - ${this.seckillEndTime.toLocaleString()}
    `);
    // 秒杀商品能力+退换信息
    const returnDesc = this.getReturnDesc();
    const promoValid = this.checkPromoTimeValid();
    const seckillPrice = this.calculateSeckillPrice();
    console.log(`
      【秒杀商品能力信息】
      秒杀价:${seckillPrice.toFixed(2)}元(原价:${this.calculateSellingPrice().toFixed(2)}元)
      秒杀时间有效:${promoValid ? "✅ 是" : "❌ 否"}
      退换规则:${returnDesc}
      当前可退换:${this.checkReturnable() ? "✅ 是" : "❌ 否"}
    `);
  }

  // 实现IReturnable接口方法:秒杀商品仅非秒杀时段可退换
  public checkReturnable(): boolean {
    return !this.checkPromoTimeValid() && this.seckillStock > 0;
  }

  // 实现IReturnable接口方法:生成秒杀商品退换说明
  public getReturnDesc(): string {
    const reasonDesc = this.isSevenDaysReturn ? "支持无理由" : "不支持无理由";
    return `${this.name}:${reasonDesc},退换期限${this.returnDays}天`;
  }

  // 实现ISeckill接口方法:计算秒杀价
  public calculateSeckillPrice(): number {
    const normalPrice = this.calculateSellingPrice();
    return parseFloat((normalPrice * this.discountRate).toFixed(2));
  }

  // 实现IPromotion接口方法:校验促销(秒杀)时间是否有效
  public checkPromoTimeValid(): boolean {
    const now = new Date();
    const timeValid = now >= this.promoStartTime && now <= this.seckillEndTime;
    
    if (!timeValid) {
      if (now < this.promoStartTime) {
        console.log(`【${this.name}】秒杀尚未开始:距离开始还有${Math.ceil((this.promoStartTime.getTime() - now.getTime()) / (60 * 1000))}分钟`);
      } else {
        console.log(`【${this.name}】秒杀已结束:已超过结束时间${Math.ceil((now.getTime() - this.seckillEndTime.getTime()) / (60 * 1000))}分钟`);
      }
    }
    return timeValid;
  }

  /**
   * 实现抽象类的checkStock方法:秒杀商品库存校验
   * @param amount 购买数量
   */
  override checkStock(amount: number): boolean {
    if (amount <= 0) {
      console.log(`【${this.name}】秒杀库存校验失败:购买数量必须大于0`);
      return false;
    }
    // 先校验秒杀时间是否有效
    if (!this.checkPromoTimeValid()) {
      console.log(`【${this.name}】秒杀库存校验失败:当前非秒杀时段`);
      return false;
    }
    if (amount > this.seckillStock) {
      console.log(`【${this.name}】秒杀库存校验失败:库存不足,当前秒杀库存${this.seckillStock}件,请求${amount}件`);
      return false;
    }
    console.log(`【${this.name}】秒杀库存校验通过,剩余秒杀库存${this.seckillStock}件`);
    return true;
  }

  /**
   * 秒杀商品专属方法:扣减秒杀库存
   * @param amount 扣减数量
   */
  public reduceSeckillStock(amount: number): void {
    if (this.checkStock(amount)) {
      this.seckillStock -= amount;
      console.log(`【${this.name}】秒杀库存扣减成功:剩余${this.seckillStock}件`);
    } else {
      console.log(`【${this.name}】秒杀库存扣减失败`);
    }
  }
}

4.4 虚拟商品(VirtualGoods.ets)

// model/VirtualGoods.ets
import { AbstractGoods } from './AbstractGoods';

/**
 * 虚拟商品类
 * 继承:AbstractGoods(商品基类)
 * 特点:无额外能力接口实现
 */
export class VirtualGoods extends AbstractGoods {
  public validDays: number; // 有效期(天)
  public isAutoRenew: boolean; // 是否自动续费

  /**
   * 构造函数
   * @param name 商品名称
   * @param basePrice 商品底价
   * @param validDays 有效期
   * @param isAutoRenew 是否自动续费
   * @param category 商品分类(可选)
   * @param profitRate 利润率(默认15%)
   */
  constructor(
    name: string,
    basePrice: number,
    validDays: number,
    isAutoRenew: boolean,
    category?: string,
    profitRate: number = 0.15
  ) {
    super(name, basePrice, category, profitRate);
    this.validDays = Math.max(validDays, 1); // 有效期至少1天
    this.isAutoRenew = isAutoRenew;
  }

  // 实现抽象类方法:计算虚拟商品售价(底价+利润率)
  override calculateSellingPrice(): number {
    return parseFloat((this._basePrice * (1 + this.profitRate)).toFixed(2));
  }

  // 重写父类的printBaseInfo方法:整合基础信息+虚拟专属信息+提示信息
  override printBaseInfo(): void {
    super.printBaseInfo();
    // 虚拟商品专属基础信息
    console.log(`
      有效期:${this.validDays}天
      自动续费:${this.isAutoRenew ? "✅ 开启" : "❌ 关闭"}
    `);
    // 虚拟商品提示信息
    console.log(`
      【虚拟商品提示】
      温馨提示:虚拟商品不支持退换,购买前请确认
    `);
  }

  /**
   * 实现抽象类的checkStock方法:虚拟商品无库存限制
   * @param amount 购买数量
   */
  override checkStock(amount: number): boolean {
    if (amount <= 0) {
      console.log(`【${this.name}】校验失败:购买数量必须大于0`);
      return false;
    }
    console.log(`【${this.name}】虚拟商品无库存限制,校验通过`);
    return true;
  }
}

五、面向接口编程:依赖抽象而非具体实现

5.1 核心思想

“面向接口编程”是将接口作为代码交互的“契约”,让业务逻辑依赖接口而非具体的类,核心优势:

  • 降低耦合:修改类内部实现无需改动调用方;
  • 易扩展:新增类实现接口即可兼容现有逻辑;
  • 易测试:可通过模拟类快速完成单元测试;
  • 鸿蒙工程化价值:适配大型项目的模块化、可插拔设计。

5.2 实战:接口作为参数/返回值(鸿蒙电商场景)

以下是完整的UI测试页面代码,适配API12语法规范:

// pages/Index.ets
import { PhysicalGoods } from '../model/PhysicalGoods';
import { VirtualGoods } from '../model/VirtualGoods';
import { SeckillGoods } from '../model/SeckillGoods';
import { AbstractGoods } from '../model/AbstractGoods';
import { LengthMetrics } from '@kit.ArkUI';

/**
 * 商品管理页面(接口+面向接口编程)
 */
@Entry
@Component
struct Index {
  @State goodsList: AbstractGoods[] = [
    new PhysicalGoods("鸿蒙Mate70手机", 6999, 200, 0.2, "智能手机"),
    new VirtualGoods("鸿蒙VIP会员", 99, 365, true),
    new SeckillGoods("鸿蒙智能手表GT5(秒杀款)", 1299, 50, 0.7, new Date(), new Date(Date.now() + 3600 * 1000 * 2))];
  

  /**
   * 统一执行商品核心逻辑
   */
  private callSubclassMethods(): void {
    console.log("===== 鸿蒙电商商品核心逻辑执行 =====");

    this.goodsList.forEach((goods, index) => {
      console.log(`\n【商品${index + 1}:${goods.name}】`);
      goods.printBaseInfo();
      console.log(`最终售价:${goods.calculateSellingPrice()}元`);

      // 库存校验
      const checkResult = goods.checkStock(2);
      console.log(`购买2件库存校验结果:${checkResult ? "✅ 通过" : "❌ 失败"}`);
      // 调用子类专属方法(instanceof 判断)
      if (goods instanceof PhysicalGoods) {
        goods.reduceStock(2);
      } else if (goods instanceof SeckillGoods) {
        goods.reduceSeckillStock(2);
      }
    });
    // 触发UI刷新
    this.goodsList = [...this.goodsList];
  }

  /**
   * 扣减实体商品库存
   */
  private handleReduceStock(goods: AbstractGoods, amount: number = 1): void {
    if (goods instanceof PhysicalGoods) {
      console.log(`\n===== 执行${goods.name}:扣减库存${amount}件 =====`);
      goods.reduceStock(amount);
      this.goodsList = [...this.goodsList];
    } else {
      console.log(`\n❌ ${goods.name}不是实体商品,无法扣减库存`);
    }
  }

  /**
   * 扣减秒杀商品库存
   */
  private handleReduceSeckillStock(goods: AbstractGoods, amount: number = 1): void {
    if (goods instanceof SeckillGoods) {
      console.log(`\n===== 执行${goods.name}:扣减秒杀库存${amount}件 =====`);
      goods.reduceSeckillStock(amount);
      this.goodsList = [...this.goodsList];
    } else {
      console.log(`\n❌ ${goods.name}不是秒杀商品,无法扣减秒杀库存`);
    }
  }

  /**
   * 购买虚拟商品
   */
  private handleBuyVirtualGoods(goods: AbstractGoods): void {
    if (goods instanceof VirtualGoods) {
      console.log(`\n===== 执行${goods.name}:购买操作 =====`);
      console.log(`${goods.name}购买成功!有效期:${goods.validDays}天 | 自动续费:${goods.isAutoRenew ? '开启' : '关闭'}`);
    } else {
      console.log(`\n❌ ${goods.name}不是虚拟商品,无法执行购买操作`);
    }
  }

  /**
   * 格式化日期为友好的字符串
   */
  private formatDate(date: Date): string {

    return date.toLocaleString('zh-CN', {
      year: 'numeric',
      month: '2-digit',
      day: '2-digit',
      hour: '2-digit',
      minute: '2-digit',
      second: '2-digit'
    });
  }

  build() {
    Column() {
      // 页面标题
      Text("抽象类+接口 商品管理实战")
        .fontSize(22)
        .fontWeight(FontWeight.Bold)
        .margin(20);

      // 全局操作按钮
      Button("执行所有商品核心操作")
        .width('90%')
        .height(40)
        .backgroundColor(Color.Blue)
        .fontColor(Color.White)
        .borderRadius(8)
        .margin({ bottom: 10 })
        .onClick(() => this.callSubclassMethods());

      Scroll() {
        Column() {
          // 统一渲染所有商品(多态渲染)
          ForEach(this.goodsList, (goods: AbstractGoods) => {
            Column() {
              // 商品基础信息
              Text(goods.name)
                .fontSize(18)
                .fontWeight(FontWeight.Medium);
              Text(`分类:${goods.category}`)
                .fontSize(14)
                .margin(2)
                .fontColor('#666666');
              Text(`底价:¥${goods.basePrice} | 利润率:${(goods.profitRate * 100).toFixed(1)}%`)
                .fontSize(14)
                .margin(2)
                .fontColor('#666666');
              Text(`售价:¥${goods.calculateSellingPrice().toFixed(2)}`)
                .fontSize(16)
                .fontColor(Color.Red)
                .margin(5);

              // 渲染子类专属信息
              if (goods instanceof PhysicalGoods) {
                Text(`库存:${goods.stock}件 | 重量:${goods.weight.toFixed(2)}kg`)
                  .fontSize(12)
                  .margin(2)
                  .fontColor('#999999');
              } else if (goods instanceof VirtualGoods) {
                Text(`有效期:${goods.validDays}天 | 自动续费:${goods.isAutoRenew ? '开启' : '关闭'}`)
                  .fontSize(12)
                  .margin(2)
                  .fontColor('#999999');
              } else if (goods instanceof SeckillGoods) {
                Text(`秒杀折扣:${(goods.discountRate * 10).toFixed(1)}折 | 剩余库存:${goods.seckillStock}件`)
                  .fontSize(12)
                  .margin(2)
                  .fontColor('#999999');
                Text(`秒杀时间:${this.formatDate(goods.promoStartTime)} - ${this.formatDate(goods.seckillEndTime)}`)
                  .fontSize(10)
                  .margin(2)
                  .fontColor('#ff6600');
              }

              // Flex布局(API12语法)
              Flex({ space: { main: LengthMetrics.vp(10), cross: LengthMetrics.vp(10) }, justifyContent: FlexAlign.Center, wrap: FlexWrap.Wrap }) {
                // 按钮1:扣减库存
                if (goods instanceof PhysicalGoods) {
                  Button("扣减库存")
                    .fontSize(10)
                    .padding({ left: 12, right: 12, top: 5, bottom: 5 })
                    .backgroundColor(Color.Green)
                    .fontColor(Color.White)
                    .borderRadius(4)
                    .onClick(() => this.handleReduceStock(goods, 1));
                }

                // 按钮2:扣减秒杀库存
                if (goods instanceof SeckillGoods) {
                  Button("扣减秒杀库存")
                    .fontSize(10)
                    .padding({ left: 12, right: 12, top: 5, bottom: 5 })
                    .backgroundColor(Color.Orange)
                    .fontColor(Color.White)
                    .borderRadius(4)
                    .onClick(() => this.handleReduceSeckillStock(goods, 1));
                }

                // 按钮3:立即购买
                if (goods instanceof VirtualGoods) {
                  Button("购买Vip")
                    .fontSize(10)
                    .padding({ left: 12, right: 12, top: 5, bottom: 5 })
                    .backgroundColor(Color.Pink)
                    .fontColor(Color.White)
                    .borderRadius(4)
                    .onClick(() => this.handleBuyVirtualGoods(goods));
                }
              }.margin({ top: 10 })

            }
            .width('90%')
            .padding(15)
            .backgroundColor(Color.White)
            .borderRadius(10)
            .shadow({ radius: 5, color: '#cccccc', offsetX: 2, offsetY: 2 })
            .margin(10);
          })
        }.width('100%')
      }.layoutWeight(1)
      .width('100%')
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#f5f5f5')
    .padding(10);
  }
}

5.2 预览运行

抽象+接口

六、抽象类 vs 接口:核心区别与选型原则

6.1 核心区别对比表

特性 抽象类(abstract class) 接口(interface) 电商场景示例
核心定位 定义“基类规范”(是不是) 定义“能力规范”(有什么) 所有商品都是AbstractGoods;商品有IReturnable能力
继承/实现 单继承 多实现 秒杀商品仅能继承AbstractGoods,可实现双接口
成员类型 支持普通/抽象方法、可设属性初始值 仅定义类型规范,无任何实现逻辑 printBaseInfo有实现;checkReturnable仅声明
访问修饰符 public/protected/private 仅public _basePrice为protected;returnDays为public
构造函数 支持 不支持 抽象类初始化商品名称;接口无构造函数
运行时类型判断 支持instanceof 通过instanceof判断实现类实例 goods instanceof PhysicalGoods ✅
代码复用 强(可复用具体实现) 无(仅复用类型契约) 复用基础信息打印;统一方法签名
工程适配 基类层(model核心) 能力层(capability目录) 抽象类放model根目录;接口放capability子目录

6.2 选型原则

  1. 优先用抽象类

    • 定义“一类事物的共性基类”且需复用实现逻辑时;
    • 需要私有/受保护成员、构造函数时;
    • 子类需要重写方法并复用父类逻辑(通过super调用)时;
    • 示例:商品基类AbstractGoods、用户基类AbstractUser。
  2. 优先用接口

    • 为类扩展多维度能力且仅需定义规范时;
    • 突破单继承限制,实现多能力组合时;
    • 作为方法参数/返回值,降低模块耦合时;
    • 示例:IReturnable(可退换)、ISeckill(可秒杀)、IPayable(可支付)。
  3. 最佳实践

    • 抽象类定基类规范,接口扩灵活能力;
    • 一个核心抽象类 + N个能力接口,适配复杂业务场景;
    • 子类重写抽象类方法时,通过super调用父类实现,保证代码复用。

七、常见问题解答

7.1 多接口方法名冲突如何处理?

子类只需实现一次,逻辑需同时兼容所有接口规范,示例:

interface IA { doAction(): void }
interface IB { doAction(): void }
class C implements IA, IB {
  doAction(): void {
    console.log("统一处理IA/IB接口的doAction逻辑,兼容双接口规范");
  }
}

7.2 接口方法声明中能否引用类的属性?

接口仅做类型声明,无法直接访问类的属性,但类实现接口方法时,可在方法体内访问自身或继承的属性(如SeckillGoods的checkPromoTimeValid方法访问this.name)。

7.3 ArkTS 核心语法约束

  • 支持instanceof(类实例类型判断)、typeof(基础类型判断);
  • 不支持is类型谓词、in类型守卫,禁止使用&实现接口交叉;
  • 强依赖静态类型检查,禁止动态类型操作(如动态访问对象属性);
  • 类型别名(type)仅在基础类型别名场景有限支持,优先用接口定义对象类型。

八、课堂小结

  1. 接口是“能力契约”,通过interface定义、implements实现,可突破ArkTS单继承限制,是鸿蒙开发中扩展类能力的核心手段;
  2. 接口支持可选属性、接口继承,能灵活适配电商场景的多维度能力扩展需求;
  3. 面向接口编程通过“接口作为参数/返回值+instanceof类实例判断”降低代码耦合,同时可复用已有实现逻辑;
  4. 抽象类与接口的选型核心是“是不是”(抽象类)和“有没有”(接口)的区别,子类可通过super调用父类实现保证代码复用;
  5. ArkTS核心约束:支持instanceof/typeof、禁用is/in/&、禁止动态类型操作,接口为对象类型定义的首选;
  6. 鸿蒙电商场景中,抽象类定义商品基类,接口扩展秒杀/退换能力,实现“基类稳定+能力灵活”的高内聚低耦合架构。

九、仓库代码

工程名称:ClassObjectDemo_3
代码地址:https://gitee.com/juhetianxia321/harmony-os-code-base.git

十、下节预告

下一节将聚焦泛型与类的灵活适配,掌握ArkTS泛型类/方法语法、泛型约束规则,开发通用商品容器与筛选工具,整合“抽象类+接口+泛型”三层架构,打通鸿蒙电商商品模块的工程化链路。

十一、鸿蒙开发者学习与认证指引

(一)官方学习班级报名(免费)

  1. 班级链接:HarmonyOS赋能资源丰富度建设(第四期)
  2. 学号填写规则:填写个人手机号码即可完成班级信息登记

(二)HarmonyOS应用开发者认证考试(免费)

  1. 考试链接:HarmonyOS开发者能力认证入口
  2. 认证等级及适配人群
    • 基础认证:适配软件工程师、移动应用开发人员,需掌握HarmonyOS基础概念、DevEco Studio基础使用、ArkTS及ArkUI基础开发等能力;
    • 高级认证:适配项目经理、工程架构师,需掌握系统核心技术理念、应用架构设计、关键技术开发及应用上架运维等能力;
    • 专家认证:适配研发经理、解决方案专家,需掌握分布式技术原理、端云一体化开发、跨端迁移及性能优化等高级能力。
  3. 认证权益:通过认证可获得电子版证书以及其他专属权益。
posted @ 2025-12-12 17:29  鸿蒙-散修  阅读(2)  评论(0)    收藏  举报