零基础鸿蒙应用开发第二十五节:接口的行为契约能力

零基础鸿蒙应用开发学习计划表

【学习目标】

  1. 彻底剥离商品属性与促销规则的耦合,掌握“固有属性契约+动态促销规则契约”的分层设计思路;
  2. 突破抽象类单继承限制,掌握ArkTS多接口实现技巧,让商品子类按需组合“可打折、可满减、可退换”行为;
  3. 掌握“通用促销规则+具体促销类型规则”的契约设计,实现促销活动的动态配置(生效时间、折扣/满减规则);
  4. 落地“抽象类(属性复用)+ 行为契约(能力规范)+ 促销规则(动态配置)”的三层电商架构;
  5. 理解“行为与规则解耦”的核心价值,适配真实电商场景中多促销活动并行、规则动态调整的需求。

【学习重点】

  • 核心重构:将上一节分散的interface目录收拢至model内,按“属性/促销/行为”维度细分接口;
  • 新增目录:model/interface/behavior(行为契约)、model/utils(规则校验工具)、model/entity(实体统一管理);
  • 多接口实现:数码商品组合IDiscountable+IReturnable,图书商品组合IDiscountable+IFullReductionable
  • 动态规则:促销规则支持生效时间、启用状态配置,规则失效自动恢复原价;
  • 工程规范:工具类统一收敛至utils目录,实体类收敛至entity目录,接口按业务维度分层管理。

一、工程结构:三层架构的工程化落地

基于上一节ClassObjectDemo_3复制并重命名为ClassObjectDemo_4,核心调整如下(新增/调整目录已标注):

ClassObjectDemo_4/
├── entry/                          # 应用主模块
│   ├── src/
│   │   ├── main/
│   │   │   ├── ets/                # ArkTS代码根目录
│   │   │   │   ├── pages/          # 表现层:仅存放页面UI/交互(三层架构-视图层)
│   │   │   │   │   └── Index.ets   # 测试页面(验证动态促销+多接口)
│   │   │   │   ├── model/          # 业务模型层:聚合商品核心业务逻辑(三层架构-模型层)
│   │   │   │   │   ├── entity/     # 【新增】实体层:统一管理核心数据结构
│   │   │   │   │   │   ├── AbstractGoods.ets      # 迁移:抽象商品基类(重构:剥离促销)
│   │   │   │   │   │   ├── DigitalGoods.ets       # 迁移:数码商品子类(IDiscountable+IReturnable)
│   │   │   │   │   │   ├── BookGoods.ets          # 迁移:图书商品子类(IDiscountable+IFullReductionable)
│   │   │   │   │   │   └── GoodsStatus.ets        # 迁移:商品状态枚举(复用)
│   │   │   │   │   ├── interface/  # 【调整】契约层:原interface目录收拢至model内,按维度细分
│   │   │   │   │   │   ├── attribute/ # 【新增】属性契约:商品固有属性规范(迁移原interface内容)
│   │   │   │   │   │   │   ├── IBaseGoods.ets             # 迁移+重构:纯商品固有属性(删除促销字段)
│   │   │   │   │   │   │   ├── IDigitalGoods.ets          # 迁移+重构:数码商品固有属性
│   │   │   │   │   │   │   └── IBookGoods.ets             # 迁移+重构:图书商品固有属性
│   │   │   │   │   │   ├── promotion/ # 【新增】促销契约:促销规则规范
│   │   │   │   │   │   │   ├── IPromotionRule.ets         # 【新增】通用促销规则契约(时间/启用状态)
│   │   │   │   │   │   │   ├── IDiscountRule.ets          # 【新增】打折促销规则契约(折扣系数/会员专属)
│   │   │   │   │   │   │   └── IFullReductionRule.ets     # 【新增】满减促销规则契约(满减阈值/减免金额)
│   │   │   │   │   │   └── behavior/  # 【新增】行为契约:商品行为能力规范
│   │   │   │   │   │       ├── IDiscountable.ets          # 【新增】可打折行为契约(定义打折行为签名)
│   │   │   │   │   │       ├── IFullReductionable.ets     # 【新增】可满减行为契约(定义满减行为签名)
│   │   │   │   │   │       └── IReturnable.ets            # 【新增】可退换行为契约(定义退换行为签名)
│   │   │   │   │   └── utils/      # 【新增】业务工具层:商品业务专属工具(非通用)
│   │   │   │   │       └── PromotionValidator.ets # 【新增】促销规则校验工具类(统一校验规则合法性)
│   │   │   │   └── common/         # 【可选】通用工具层:跨业务域的通用工具(当前暂空)
│   │   │   ├── resources/          # 资源目录(非本节重点)
│   │   │   └── module.json5        # 模块配置文件
│   │   └── oh-package.json5        # 工程依赖配置
└── hvigorfile.ts                   # 构建脚本(默认生成)

二、核心重构与契约设计

2.1 第一步:重构商品属性契约(model/interface/attribute

仅保留商品“固有属性”,彻底删除discount等促销相关字段,回归属性契约核心职责:

// model/interface/attribute/IBaseGoods.ets(重构后)
import { GoodsStatus } from '../../entity/GoodsStatus';

/**
 * 纯商品固有属性契约:仅描述商品本身的属性,无任何促销相关字段
 */
export interface IBaseGoods {
  // 商品名称(固有)
  name: string;
  // 商品原价(固有,促销不修改原价)
  price: number;
  // 库存(固有)
  stock: number;
  // 成本价(固有)
  costPrice: number;
  // 商品分类(可选,固有)
  category?: string;
  // 上下架状态(可选,固有)
  status?: GoodsStatus;
}
// model/interface/attribute/IDigitalGoods.ets(重构后)
import { IBaseGoods } from './IBaseGoods';

/**
 * 数码商品固有属性契约:继承通用属性,补充数码商品特有固有属性
 */
export interface IDigitalGoods extends IBaseGoods {
  // 品牌(固有)
  brand: string;
  // 保修期(年,固有)
  warranty: number;
}
// model/interface/attribute/IBookGoods.ets(重构后)
import { IBaseGoods } from './IBaseGoods';

/**
 * 图书商品固有属性契约:继承通用属性,补充图书商品特有固有属性
 */
export interface IBookGoods extends IBaseGoods {
  // 作者(固有)
  author: string;
  // 出版日期(固有)
  publishDate: string;
}

2.2 第二步:设计促销规则契约(model/interface/promotion

按“通用规则+具体规则”分层,支持促销活动的动态配置(时间、启用状态、专属规则):

// model/interface/promotion/IPromotionRule.ets
/**
 * 通用促销规则契约:所有促销活动的公共配置
 */
export interface IPromotionRule {
  // 促销活动唯一ID
  promotionId: string;
  // 促销活动名称(如“双十二数码折扣季”)
  promotionName: string;
  // 促销开始时间(Date类型,支持时间校验)
  startTime: Date;
  // 促销结束时间
  endTime: Date;
  // 是否启用该促销
  isEnabled: boolean;
}
// model/interface/promotion/IDiscountRule.ets
import { IPromotionRule } from './IPromotionRule';
/**
 * 打折促销规则契约:继承通用规则,补充打折专属配置
 */
export interface IDiscountRule extends IPromotionRule {
  // 折扣系数(0-1,如0.8=8折)
  discountRate: number;
  // 是否仅会员可用
  isMemberExclusive: boolean;
}
// model/interface/promotion/IFullReductionRule.ets
import { IPromotionRule } from './IPromotionRule';
/**
 * 满减促销规则契约:继承通用规则,补充满减专属配置
 */
export interface IFullReductionRule extends IPromotionRule {
  // 满减阈值(如50元)
  fullAmount: number;
  // 减免金额(如10元)
  reductionAmount: number;
}

2.3 第三步:设计行为契约(model/interface/behavior

行为契约定义“商品能做什么”,具体逻辑依赖促销规则契约,突破单继承限制:

// model/interface/behavior/IDiscountable.ets
import { IDiscountRule } from '../promotion/IDiscountRule';
/**
 * 可打折行为契约:定义商品打折行为的标准化签名
 * 核心:行为与规则解耦,通过注入规则实现动态打折
 */
export interface IDiscountable {
  /**
   * 设置打折促销规则
   * @param rule 打折规则契约对象
   */
  setDiscountRule(rule: IDiscountRule): void;

  /**
   * 根据生效规则计算折后价
   * 规则失效/未配置 → 返回原价
   * @returns 折后价(保留2位小数)
   */
  calculateDiscountPrice(): number;

  /**
   * 获取当前生效的打折规则
   * @returns 生效规则/undefined
   */
  getCurrentDiscountRule(): IDiscountRule | undefined;
}
// model/interface/behavior/IFullReductionable.ets
import { IFullReductionRule } from '../promotion/IFullReductionRule';
/**
 * 可满减行为契约:定义商品满减行为的标准化签名
 */
export interface IFullReductionable {
  /**
   * 设置满减促销规则
   * @param rule 满减规则契约对象
   */
  setFullReductionRule(rule: IFullReductionRule): void;

  /**
   * 根据生效规则计算满减价
   * @param basePrice 满减前基础价(通常为折后价)
   * @returns 满减后价格(保留2位小数)
   */
  calculateFullReductionPrice(basePrice: number): number;

  /**
   * 获取当前生效的满减规则
   * @returns 生效规则/undefined
   */
  getCurrentFullReductionRule(): IFullReductionRule | undefined;
}
// model/interface/behavior/IReturnable.ets
/**
 * 可退换行为契约:定义商品退换行为的标准化签名
 * 无规则依赖,属于商品固有服务属性
 */
export interface IReturnable {
  /**
   * 判断商品是否支持退换
   * @returns true-支持,false-不支持
   */
  isSupportReturn(): boolean;

  /**
   * 获取退换规则说明
   * @returns 退换规则文本
   */
  getReturnRule(): string;
}

2.4 第四步:新增促销规则校验工具类(model/utils

统一校验规则合法性,避免逻辑散落在子类中,提升代码复用性和可维护性:

// model/utils/PromotionValidator.ets
import { IPromotionRule } from '../interface/promotion/IPromotionRule';
import { IDiscountRule } from '../interface/promotion/IDiscountRule';
import { IFullReductionRule } from '../interface/promotion/IFullReductionRule';

/**
 * 促销规则校验工具类:统一校验各类促销规则的合法性
 * 核心:集中管理规则校验逻辑,提高复用性,降低子类耦合
 */
export class PromotionValidator {
  /**
   * 校验通用促销规则(启用状态+时间范围)
   * @param rule 通用促销规则
   * @returns true-规则生效,false-规则失效
   */
  public static validatePromotionRule(rule: IPromotionRule): boolean {
    // 未启用 → 失效
    if (!rule.isEnabled) return false;
    // 时间校验(当前时间在[startTime, endTime]范围内)
    const now = new Date();
    return now >= rule.startTime && now <= rule.endTime;
  }

  /**
   * 校验打折规则(通用规则+折扣系数范围)
   * @param rule 打折规则
   * @returns true-规则合法且生效,false-无效/失效
   */
  public static validateDiscountRule(rule: IDiscountRule): boolean {
    // 先校验通用规则
    if (!PromotionValidator.validatePromotionRule(rule)) return false;
    // 折扣系数需在0-1之间
    return rule.discountRate > 0 && rule.discountRate <= 1;
  }

  /**
   * 校验满减规则(通用规则+金额合法性)
   * @param rule 满减规则
   * @returns true-规则合法且生效,false-无效/失效
   */
  public static validateFullReductionRule(rule: IFullReductionRule): boolean {
    // 先校验通用规则
    if (!PromotionValidator.validatePromotionRule(rule)) return false;
    // 满减阈值和减免金额需为正数,且减免金额<满减阈值
    return rule.fullAmount > 0 
      && rule.reductionAmount > 0 
      && rule.reductionAmount < rule.fullAmount;
  }
}

三、重构核心实体类(model/entity

3.1 抽象商品基类(重构:剥离促销,聚焦固有属性)

// model/entity/AbstractGoods.ets
import { IBaseGoods } from '../interface/attribute/IBaseGoods';
import { GoodsStatus } from './GoodsStatus';
import { util } from '@kit.ArkTS';

/**
 * 抽象商品基类:仅负责商品固有属性的复用与基础逻辑
 * 核心调整:删除discount相关逻辑,专注固有属性管理
 */
export abstract class AbstractGoods {
  // 商品唯一ID(只读,UUID生成)
  public readonly goodsId: string;
  // 公共属性
  public name: string;
  public category: string = "商品分类";
  public status: GoodsStatus;
  // 保护属性
  protected costPrice: number;
  // 私有属性(仅固有属性)
  private _price: number;
  private _stock: number;

  /**
   * 构造函数:接收纯固有属性契约对象
   * @param props 符合IBaseGoods规范的结构化参数
   */
  constructor(props: IBaseGoods) {
    // 数据校验
    this.validateProps(props);
    
    // 初始化固有属性
    this.name = props.name.trim() || "未命名商品";
    this._price = Math.max(props.price, 0);
    this._stock = Math.max(props.stock, 0);
    this.costPrice = Math.max(props.costPrice, 0);
    this.category = props.category?.trim() || this.category;
    // 库存为0时强制下架(不修改入参props)
    this.status = (props.status === GoodsStatus.ON_SHELF && props.stock === 0) 
      ? GoodsStatus.OFF_SHELF 
      : (props.status ?? GoodsStatus.OFF_SHELF);
    // 生成唯一商品ID(模拟器/真机有效,预览模式忽略)
    this.goodsId = util.generateRandomUUID() || `goods_${Date.now()}`;
    console.log(`【${this.name}】商品ID:${this.goodsId}`);
  }

  /**
   * 固有属性校验:仅校验商品本身属性
   */
  private validateProps(props: IBaseGoods): void {
    if (!props.name || props.name.trim() === '') {
      throw new Error('商品名称不能为空');
    }
    if (props.price < 0 || props.stock < 0 || props.costPrice < 0) {
      throw new Error('价格、库存、成本价不能为负数');
    }
  }

  // 售价setter(仅修改原价,促销不影响原价)
  set price(newPrice: number) {
    if (newPrice < 0) {
      console.warn(`【${this.name}】售价不能为负,修改失败`);
      return;
    }
    this._price = newPrice;
    console.log(`【${this.name}】原价修改为:${newPrice}元`);
  }

  // 售价getter(返回原价)
  get price(): number {
    return this._price;
  }

  // 库存setter(库存为0自动下架)
  set stock(num: number) {
    if (num < 0) {
      console.warn(`【${this.name}】库存不能为负,修改失败`);
      return;
    }
    this._stock = num;
    console.log(`【${this.name}】库存修改为:${num}件`);
    if (this._stock === 0) {
      this.status = GoodsStatus.OFF_SHELF;
      console.log(`【${this.name}】库存为0,自动下架`);
    }
  }

  // 库存getter
  get stock(): number {
    return this._stock;
  }

  /**
   * 打印商品固有信息(不含促销)
   */
  public printBaseInfo(): void {
    console.log(`
    ===== 商品基础信息 =====
    商品ID:${this.goodsId}
    商品名称:${this.name}
    商品分类:${this.category}
    商品原价:${this._price}元
    库存数量:${this._stock}件
    商品状态:${this.status === GoodsStatus.ON_SHELF ? '上架' : '下架'}
    `);
  }

  /**
   * 抽象方法:计算最终售价(由子类结合促销规则实现)
   * 核心:子类需组合行为契约,实现动态促销后的最终价格
   */
  public abstract calculateFinalPrice(): number;
}

3.2 数码商品子类(实现IDiscountable+IReturnable)

// model/entity/DigitalGoods.ets
import { AbstractGoods } from './AbstractGoods';
import { IDigitalGoods } from '../interface/attribute/IDigitalGoods';
import { IDiscountable } from '../interface/behavior/IDiscountable';
import { IReturnable } from '../interface/behavior/IReturnable';
import { IDiscountRule } from '../interface/promotion/IDiscountRule';
import { PromotionValidator } from '../utils/PromotionValidator';

/**
 * 数码商品子类:继承抽象类 + 实现多行为契约
 * 核心:突破单继承,组合"可打折+可退换"行为,动态注入促销规则
 */
export class DigitalGoods extends AbstractGoods implements IDiscountable, IReturnable {
  // 数码商品固有属性
  private _brand: string;
  private _warranty: number;
  // 打折规则(动态注入)
  private _discountRule?: IDiscountRule;

  /**
   * 构造函数:接收数码商品固有属性契约
   * @param props 符合IDigitalGoods规范的结构化参数
   */
  constructor(props: IDigitalGoods) {
    super(props);
    // 初始化固有属性
    this._brand = props.brand.trim() || "未知品牌";
    this._warranty = Math.max(props.warranty, 1); // 保修至少1年
  }

  // ======== 实现IDiscountable接口 ========
  setDiscountRule(rule: IDiscountRule): void {
    // 空值校验
    if (!rule) {
      console.warn("打折规则不能为空");
      return;
    }
    if (PromotionValidator.validateDiscountRule(rule)) {
      this._discountRule = rule;
      console.log(`【${this.name}】已配置打折规则:${rule.promotionName}`);
    } else {
      console.warn(`【${this.name}】促销规则【${rule.promotionName}】无效或已过期`);
    }
  }

  calculateDiscountPrice(): number {
    // 无生效规则 → 返回原价
    if (!this._discountRule || !PromotionValidator.validateDiscountRule(this._discountRule)) {
      return this.price;
    }
    // 仅会员可用:非会员返回原价,会员享受折扣
    if (this._discountRule.isMemberExclusive) {
      // 此处可扩展“是否为会员”的判断逻辑(示例默认模拟会员)
      const isMember = true; // 实际开发中可从用户信息获取
      if (!isMember) return this.price;
    }
    // 基础折扣价 = 原价 * 折扣系数
    const finalPrice = this.price * this._discountRule.discountRate;
    return parseFloat(finalPrice.toFixed(2));
  }

  getCurrentDiscountRule(): IDiscountRule | undefined {
    // 仅返回生效的规则
    if (this._discountRule && PromotionValidator.validateDiscountRule(this._discountRule)) {
      return this._discountRule;
    }
    return undefined;
  }

  // ======== 实现IReturnable接口 ========
  isSupportReturn(): boolean {
    // 数码商品默认支持退换(可扩展激活状态判断)
    return true;
  }

  getReturnRule(): string {
    return `【${this.brand}${this.name}】支持七天无理由退换,激活后不支持退换,保修期${this._warranty}年`;
  }

  // ======== 固有属性getter/setter ========
  set brand(newBrand: string) {
    this._brand = newBrand.trim() || "未知品牌";
  }

  get brand(): string {
    return this._brand;
  }

  set warranty(newWarranty: number) {
    if (newWarranty < 1) {
      console.warn(`【${this.name}】保修期至少1年,修改失败`);
      return;
    }
    this._warranty = newWarranty;
  }

  get warranty(): number {
    return this._warranty;
  }

  // ======== 实现抽象方法 ========
  public override calculateFinalPrice(): number {
    // 数码商品最终价 = 打折价(无规则则原价)
    return this.calculateDiscountPrice();
  }

  // ======== 扩展方法 ========
  /**
   * 计算单件利润(基于最终售价)
   */
  calculateProfit(): number {
    const finalPrice = this.calculateFinalPrice();
    return parseFloat((finalPrice - this.costPrice).toFixed(2));
  }

  /**
   * 打印完整信息(基础信息+促销+退换)
   */
  public override printBaseInfo(): void {
    super.printBaseInfo();
    // 打印打折规则
    const discountRule = this.getCurrentDiscountRule();
    const discountInfo = discountRule 
      ? `当前生效打折规则:${discountRule.promotionName}(${discountRule.discountRate * 10}折,${discountRule.isMemberExclusive ? '会员专属' : '全用户'})`
      : "无生效打折规则";
    // 打印退换规则
    const returnInfo = this.getReturnRule();
    
    console.log(`
    品牌信息:${this._brand}
    保修年限:${this._warranty}年
    ${discountInfo}
    折后价:${this.calculateDiscountPrice()}元
    单件利润:${this.calculateProfit()}元
    退换规则:${returnInfo}
    `);
  }
}

3.3 图书商品子类(实现IDiscountable+IFullReductionable)

// model/entity/BookGoods.ets
import { AbstractGoods } from './AbstractGoods';
import { IBookGoods } from '../interface/attribute/IBookGoods';
import { IDiscountable } from '../interface/behavior/IDiscountable';
import { IFullReductionable } from '../interface/behavior/IFullReductionable';
import { IDiscountRule } from '../interface/promotion/IDiscountRule';
import { IFullReductionRule } from '../interface/promotion/IFullReductionRule';
import { PromotionValidator } from '../utils/PromotionValidator';

/**
 * 图书商品子类:继承抽象类 + 实现多行为契约
 * 核心:突破单继承,组合"可打折+可满减"行为,支持规则叠加
 */
export class BookGoods extends AbstractGoods implements IDiscountable, IFullReductionable {
  // 图书固有属性
  private _author: string;
  private _publishDate: string;
  // 动态促销规则
  private _discountRule?: IDiscountRule;
  private _fullReductionRule?: IFullReductionRule;

  /**
   * 构造函数:接收图书固有属性契约
   * @param props 符合IBookGoods规范的结构化参数
   */
  constructor(props: IBookGoods) {
    super(props);
    // 初始化固有属性
    this._author = props.author.trim() || "未知作者";
    this._publishDate = props.publishDate.trim() || "未知日期";
  }

  // ======== 实现IDiscountable接口 ========
  setDiscountRule(rule: IDiscountRule): void {
    if (!rule) {
      console.warn("打折规则不能为空");
      return;
    }
    if (PromotionValidator.validateDiscountRule(rule)) {
      this._discountRule = rule;
      console.log(`【${this.name}】已配置打折规则:${rule.promotionName}`);
    } else {
      console.warn(`【${this.name}】促销规则【${rule.promotionName}】无效或已过期`);
    }
  }

  calculateDiscountPrice(): number {
    if (!this._discountRule || !PromotionValidator.validateDiscountRule(this._discountRule)) {
      return this.price;
    }
    // 基础折扣价
    return parseFloat((this.price * this._discountRule.discountRate).toFixed(2));
  }

  getCurrentDiscountRule(): IDiscountRule | undefined {
    if (this._discountRule && PromotionValidator.validateDiscountRule(this._discountRule)) {
      return this._discountRule;
    }
    return undefined;
  }

  // ======== 实现IFullReductionable接口 ========
  setFullReductionRule(rule: IFullReductionRule): void {
    if (!rule) {
      console.warn("满减规则不能为空");
      return;
    }
    if (PromotionValidator.validateFullReductionRule(rule)) {
      this._fullReductionRule = rule;
      console.log(`【${this.name}】已配置满减规则:${rule.promotionName}`);
    } else {
      console.warn(`【${this.name}】促销规则【${rule.promotionName}】无效或已过期`);
    }
  }

  calculateFullReductionPrice(basePrice: number): number {
    // 边界校验:基础价非负
    if (basePrice <= 0) return 0;
    // 无生效规则 → 返回基础价
    if (!this._fullReductionRule || !PromotionValidator.validateFullReductionRule(this._fullReductionRule)) {
      return basePrice;
    }
    const rule = this._fullReductionRule;
    let finalPrice = basePrice;
    // 满减计算
    if (finalPrice >= rule.fullAmount) {
      // 计算减免次数(如满50减10,120元减20)
      const reductionTimes = Math.floor(finalPrice / rule.fullAmount);
      const totalReduction = reductionTimes * rule.reductionAmount;
      finalPrice -= totalReduction;
    }
    return parseFloat(finalPrice.toFixed(2));
  }

  getCurrentFullReductionRule(): IFullReductionRule | undefined {
    if (this._fullReductionRule && PromotionValidator.validateFullReductionRule(this._fullReductionRule)) {
      return this._fullReductionRule;
    }
    return undefined;
  }

  // ======== 固有属性getter/setter ========
  set author(newAuthor: string) {
    this._author = newAuthor.trim() || "未知作者";
  }

  get author(): string {
    return this._author;
  }

  set publishDate(newDate: string) {
    this._publishDate = newDate.trim() || "未知日期";
  }

  get publishDate(): string {
    return this._publishDate;
  }

  // ======== 实现抽象方法 ========
  public override calculateFinalPrice(): number {
    // 图书最终价 = 打折价 → 满减价(规则叠加)
    const discountPrice = this.calculateDiscountPrice();
    return this.calculateFullReductionPrice(discountPrice);
  }

  // ======== 扩展方法 ========
  /**
   * 计算单件利润
   */
  calculateProfit(): number {
    const finalPrice = this.calculateFinalPrice();
    return parseFloat((finalPrice - this.costPrice).toFixed(2));
  }

  /**
   * 获取作者信息
   */
  getAuthorInfo(): string {
    return `《${this.name}》的作者是${this._author},出版于${this._publishDate}`;
  }

  /**
   * 打印完整信息(基础+打折+满减)
   */
  public override printBaseInfo(): void {
    super.printBaseInfo();
    // 打折规则信息
    const discountRule = this.getCurrentDiscountRule();
    const discountInfo = discountRule 
      ? `打折规则:${discountRule.promotionName}(${discountRule.discountRate * 10}折)`
      : "无生效打折规则";
    // 满减规则信息
    const fullReductionRule = this.getCurrentFullReductionRule();
    const fullReductionInfo = fullReductionRule
      ? `满减规则:${fullReductionRule.promotionName}(满${fullReductionRule.fullAmount}减${fullReductionRule.reductionAmount})`
      : "无生效满减规则";
    
    console.log(`
    作者信息:${this._author}
    出版日期:${this._publishDate}
    ${discountInfo}
    ${fullReductionInfo}
    折后价:${this.calculateDiscountPrice()}元
    满减后价:${this.calculateFinalPrice()}元
    单件利润:${this.calculateProfit()}元
    `);
  }
}

3.4 商品状态枚举(迁移至entity目录)

// model/entity/GoodsStatus.ets
/**
 * 商品状态枚举:规范商品上下架状态
 */
export enum GoodsStatus {
  /** 上架状态 */
  ON_SHELF = "ON_SHELF",
  /** 下架状态 */
  OFF_SHELF = "OFF_SHELF"
}

四、实战验证(动态促销+多接口能力)

4.1 测试页面(pages/Index.ets)

验证多行为接口实现、动态促销规则注入、规则生效/失效等核心场景:

import { AbstractGoods } from '../model/entity/AbstractGoods';
import { DigitalGoods } from '../model/entity/DigitalGoods';
import { BookGoods } from '../model/entity/BookGoods';
import { IDigitalGoods } from '../model/interface/attribute/IDigitalGoods';
import { IBookGoods } from '../model/interface/attribute/IBookGoods';
import { GoodsStatus } from '../model/entity/GoodsStatus';
import { IDiscountRule } from '../model/interface/promotion/IDiscountRule';
import { IFullReductionRule } from '../model/interface/promotion/IFullReductionRule';
import { prompt } from '@kit.ArkUI';

@Entry
@Component
struct Index {
  // 商品列表(抽象类引用,多态)
  @State goodsList: AbstractGoods[] = [];

  // 页面初始化:创建商品+注入促销规则
  aboutToAppear(): void {
    // 1. 创建数码商品实例(固有属性)
    const digitalProps: IDigitalGoods = {
      name: "鸿蒙Mate70手机",
      price: 5999,
      stock: 100,
      costPrice: 4000,
      category: "数码产品",
      status: GoodsStatus.ON_SHELF,
      brand: "华为",
      warranty: 2
    };
    const digitalGoods = new DigitalGoods(digitalProps);

    // 2. 创建图书商品实例(固有属性)
    const bookProps: IBookGoods = {
      name: "鸿蒙开发实战",
      price: 69,
      stock: 500,
      costPrice: 30,
      category: "图书",
      author: "散修",
      publishDate: "2025-01"
    };
    const bookGoods = new BookGoods(bookProps);

    // 3. 注入数码商品促销规则
    const digitalDiscountRule: IDiscountRule = {
      promotionId: "discount_digital_001",
      promotionName: "双十二数码会员8折",
      startTime: new Date(2025,11,31),
      endTime:  new Date(2026,11,31),
      isEnabled: true,
      discountRate: 0.8,
      isMemberExclusive: true
    };
    digitalGoods.setDiscountRule(digitalDiscountRule);

    // 4. 注入图书商品促销规则
    // 4.1 图书打折规则(7折,全品类)
    const bookDiscountRule: IDiscountRule = {
      promotionId: "discount_book_001",
      promotionName: "图书全场7折",
     startTime: new Date(2025,11,31),
      endTime:  new Date(2026,11,31),
      isEnabled: true,
      discountRate: 0.7,
      isMemberExclusive: false
    };
    // 4.2 图书满减规则(满50减10)
    const bookFullReductionRule: IFullReductionRule = {
      promotionId: "full_reduction_book_001",
      promotionName: "图书满50减10",
      startTime: new Date(2025,11,31),
      endTime:  new Date(2026,11,31),
      isEnabled: true,
      fullAmount: 50,
      reductionAmount: 10
    };
    bookGoods.setDiscountRule(bookDiscountRule);
    bookGoods.setFullReductionRule(bookFullReductionRule);

    // 5. 验证核心功能
    this.goodsList = [digitalGoods, bookGoods];
    this.goodsList.forEach(goods => {
      goods.printBaseInfo();
      console.log(`【${goods.name}】最终售价:${goods.calculateFinalPrice()}元`);
    });
  }

  // 批量更新库存
  private batchUpdateStock(num: number): void {
    this.goodsList.forEach(goods => {
      goods.stock += num;
    });
    this.goodsList = [...this.goodsList];
  }

  // 切换商品上下架
  private toggleStatus(index: number): void {
    const newGoodsList = [...this.goodsList];
    if (newGoodsList[index].status === GoodsStatus.OFF_SHELF && newGoodsList[index].stock === 0) {
      prompt.showToast({ message: `${newGoodsList[index].name}库存为0,无法上架` });
      return;
    }
    newGoodsList[index].status = newGoodsList[index].status === GoodsStatus.ON_SHELF
      ? GoodsStatus.OFF_SHELF
      : GoodsStatus.ON_SHELF;
    this.goodsList = newGoodsList;
  }

  // 清空库存
  private clearStock(index: number): void {
    const newGoodsList = [...this.goodsList];
    newGoodsList[index].stock = 0;
    this.goodsList = [...newGoodsList];
  }

  // 切换促销规则启用状态(演示动态规则)
  private togglePromotionRule(index: number): void {
    const goods = this.goodsList[index];
    if (goods instanceof DigitalGoods) {
      const rule = goods.getCurrentDiscountRule();
      if (rule) {
        rule.isEnabled = !rule.isEnabled;
        goods.setDiscountRule(rule);
        prompt.showToast({ message: `${goods.name}打折规则已${rule.isEnabled ? '启用' : '禁用'}` });
      }
    }
    if (goods instanceof BookGoods) {
      const rule = goods.getCurrentFullReductionRule();
      if (rule) {
        rule.isEnabled = !rule.isEnabled;
        goods.setFullReductionRule(rule);
        prompt.showToast({ message: `${goods.name}满减规则已${rule.isEnabled ? '启用' : '禁用'}` });
      }
    }
    // 刷新UI
    this.goodsList = [...this.goodsList];
  }

  build() {
    Column() {
      // 页面标题
      Text("鸿蒙电商商品管理-动态促销+行为契约")
        .fontSize(24)
        .fontWeight(FontWeight.Bold)
        .margin({ top: 20, bottom: 20 });

      // 批量更新库存按钮
      Button("批量增加50件库存")
        .width(200)
        .height(40)
        .margin({ bottom: 20 })
        .onClick(() => this.batchUpdateStock(50));

      // 商品列表渲染(展示多行为能力)
      List({ space: 10 }) {
        ForEach(
          this.goodsList,
          (goods: AbstractGoods, index: number) => {
            ListItem() {
              Column({ space: 8 }) {
                // 通用信息
                Text(`商品名称:${goods.name}`)
                  .fontSize(18)
                  .fontWeight(FontWeight.Medium);
                Text(`商品ID:${goods.goodsId}`)
                  .fontSize(12)
                  .fontColor(Color.Grey);
                Text(`分类:${goods.category} | 原价:${goods.price}元 | 库存:${goods.stock}件`)
                  .fontSize(14);
                Text(`状态:${goods.status === GoodsStatus.ON_SHELF ? '在售' : '下架'} | 最终售价:${goods.calculateFinalPrice()}元`)
                  .fontSize(14)
                  .fontColor(goods.status === GoodsStatus.ON_SHELF ? Color.Green : Color.Red);

                // 数码商品专属信息(打折+退换)
                if (goods instanceof DigitalGoods) {
                  Text(`品牌:${goods.brand} | 保修:${goods.warranty}年`)
                    .fontSize(14)
                    .fontColor(Color.Orange);
                  Text(`退换规则:${goods.getReturnRule()}`)
                    .fontSize(12)
                    .fontColor(Color.Blue)
                    .maxLines(2)
                    .textOverflow({ overflow: TextOverflow.Ellipsis });
                  if (goods.getCurrentDiscountRule()){
                    Text(`当前折扣:${`${(goods.getCurrentDiscountRule()?.discountRate ?? 1) * 10}折(${goods.getCurrentDiscountRule()?.isMemberExclusive ? '会员专属' : '全用户'})`}`)
                      .fontSize(12)
                      .fontColor(Color.Red);
                  }
                }

                // 图书商品专属信息(打折+满减)
                if (goods instanceof BookGoods) {
                  Text(goods.getAuthorInfo())
                    .fontSize(14)
                    .fontColor(Color.Blue);
                  if (goods.getCurrentFullReductionRule()) {
                    Text(`打折规则:${`${(goods.getCurrentDiscountRule()?.discountRate ?? 1) * 10}折`} | 满减规则:${`满${goods.getCurrentFullReductionRule()?.fullAmount}减${goods.getCurrentFullReductionRule()?.reductionAmount}`}`)
                      .fontSize(12)
                      .fontColor(Color.Red);
                  }
                }

                // 操作按钮
                Row({ space: 10 }) {
                  Button(goods.status === GoodsStatus.OFF_SHELF ? "上架商品" : "下架商品")
                    .onClick(() => this.toggleStatus(index))
                    .backgroundColor(goods.status === GoodsStatus.OFF_SHELF ? Color.Red : Color.Green);
                  Button("清空库存")
                    .onClick(() => this.clearStock(index));

                  Button("关闭促销活动")
                    .onClick(() => this.togglePromotionRule(index))
                    .backgroundColor(Color.Orange);
                }.width('100%')
                .justifyContent(FlexAlign.Center)
                .margin({ top: 8 });
              }
              .padding(15)
              .backgroundColor(Color.White)
              .borderRadius(8)
              .shadow({ radius: 3, color: Color.Grey, offsetX: 2, offsetY: 2 })
              .width('100%');
            }.padding(15)
            .width('100%');
          }
        );
      }
      .width('100%')
      .layoutWeight(1);
    }
    .width('100%')
    .height('100%')
    .backgroundColor("#f5f5f5");
  }
}

4.2 核心测试场景验证

测试场景 预期结果
数码商品注入8折规则 会员:5999 * 0.8 = 4799.2元;非会员:返回原价5999元
图书商品注入7折+满50减10 折后价=69*0.7=48.3元(未满50)→ 最终售价48.3元;若价格改为80元,折后56元→满减后46元
禁用促销规则 商品返回原价(数码5999元,图书69元)
规则时间过期 自动失效,返回原价
关闭促销活动 手动关闭活动,返回原价

4.3 运行效果

行为契约接口_20260108111917_613_125

五、核心优势对比(第二十二节 vs 第二十三节)

对比维度 第二十二节(属性契约+单继承) 第二十三节(行为契约+动态促销)
架构分层 仅属性契约分层,行为依赖抽象类单继承 三层架构(属性+规则+行为),职责完全解耦
促销规则 硬编码(如满50减10),无法动态调整 动态配置(时间、系数),支持规则切换
行为扩展 单继承限制,无法组合多行为 多接口实现,按需组合“打折+满减+退换”
规则校验 无统一校验,逻辑散落在子类 utils工具类统一校验,规则合法性有保障
真实场景适配 仅支持固定促销,无法适配多活动并行 支持多规则叠加、时间控制
维护成本 新增促销类型需修改子类逻辑 新增促销类型仅需扩展规则+行为契约
代码复用性 仅属性复用 属性+规则+行为全维度复用

六、架构整合逻辑

6.1 目录分层核心价值

子目录/文件 核心职责 解决的核心问题
model/entity 统一管理商品抽象类、子类、枚举,聚焦核心数据结构与业务逻辑 实体文件分散,工程结构混乱
model/interface/attribute 定义商品固有属性规范,仅描述“商品是什么”,不含任何行为/规则逻辑 属性与行为耦合,契约职责不清晰
model/interface/promotion 定义促销规则规范,仅描述“促销规则是什么”,与商品属性完全解耦 促销规则硬编码,无法动态配置
model/interface/behavior 定义商品行为能力规范,仅描述“商品能做什么”,具体逻辑依赖规则契约 抽象类单继承限制,无法扩展多行为
model/utils/PromotionValidator 统一校验促销规则合法性(时间、启用状态、系数范围) 规则校验逻辑散落在子类,易出错

6.2 架构逻辑流程图

graph LR %% 基础契约层 A[属性契约层<br/>IBaseGoods/IDigitalGoods等<br/>定义:商品固有属性规范] --> B[抽象类层<br/>AbstractGoods<br/>封装:通用属性复用/基础逻辑校验/抽象方法定义] C[促销规则契约层<br/>IPromotionRule/IDiscountRule等<br/>定义:促销规则数据规范] --> D[规则校验工具层<br/>PromotionValidator<br/>统一:规则合法性校验] %% 行为契约层(直接关联商品子类) E[行为契约层<br/>IDiscountable/IFullReductionable等<br/>定义:商品行为能力规范] --> F[商品子类层<br/>DigitalGoods/BookGoods<br/>实现:多行为契约+动态规则注入+差异化业务逻辑] %% 商品子类层(多接口实现) B --> F D --> F %% 最终交互层 F --> G[UI交互层<br/>Index.ets<br/>可视化:商品列表展示/促销规则切换/库存操作/价格计算]

七、内容总结

7.1 关键点回顾

  1. 架构核心重构:剥离商品属性与促销规则的耦合,属性契约仅保留固有属性,促销逻辑迁移至独立的规则契约和行为契约,实现职责解耦;
  2. 扩展能力突破:通过ArkTS多接口实现,打破抽象类单继承限制,商品子类可按需组合“可打折、可满减、可退换”等行为能力;
  3. 规则动态配置:设计“通用+具体”的促销规则契约,支持生效时间、启用状态配置,规则失效自动恢复原价,适配真实电商场景;
  4. 工程规范升级:收拢接口至model/interface,新增entity(实体)、utils(工具)目录,统一管理核心逻辑,提升代码可维护性。

7.2 核心价值

  • 解耦提效:属性、规则、行为三层解耦,新增促销类型仅需扩展契约,无需修改原有代码,符合“开闭原则”;
  • 动态适配:促销规则支持时间校验、启用状态切换,规则失效自动恢复原价,适配电商平台多活动并行的业务场景;
  • 工程化落地:完善的目录分层和工具类设计,符合大型电商项目的代码组织规范,便于团队协作和后期维护。

八、代码仓库

九、下节预告

本节核心目标是帮大家掌握「行为契约解耦 + 多接口突破单继承」的核心编码思路,而非覆盖所有电商促销场景。从实际业务视角看,当前实现确实仅适配了基础的打折、满减规则,其规则很粗陋。基础阶段的重点是先吃透 “属性 - 行为 - 规则” 三层解耦的架构逻辑,而非堆砌业务功能。等大家掌握了核心思路,后续拓展复杂促销规则会水到渠成。

下一节将聚焦泛型与商品容器的灵活适配,基于我们已掌握的泛型类 / 泛型方法知识,开发通用的商品容器(支持任意商品类型),整合 “抽象类 + 属性契约 + 行为契约 + 泛型” 的四层架构,实现电商商品模块的全链路工程化开发,重点完成商品的统一增删查改、多维度排序筛选等核心商品管理能力落地。

posted @ 2026-01-24 14:10  鸿蒙-散修  阅读(1)  评论(0)    收藏  举报