零基础鸿蒙应用开发第二十五节:接口的行为契约能力
【学习目标】
- 彻底剥离商品属性与促销规则的耦合,掌握“固有属性契约+动态促销规则契约”的分层设计思路;
- 突破抽象类单继承限制,掌握ArkTS多接口实现技巧,让商品子类按需组合“可打折、可满减、可退换”行为;
- 掌握“通用促销规则+具体促销类型规则”的契约设计,实现促销活动的动态配置(生效时间、折扣/满减规则);
- 落地“抽象类(属性复用)+ 行为契约(能力规范)+ 促销规则(动态配置)”的三层电商架构;
- 理解“行为与规则解耦”的核心价值,适配真实电商场景中多促销活动并行、规则动态调整的需求。
【学习重点】
- 核心重构:将上一节分散的
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 运行效果

五、核心优势对比(第二十二节 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 关键点回顾
- 架构核心重构:剥离商品属性与促销规则的耦合,属性契约仅保留固有属性,促销逻辑迁移至独立的规则契约和行为契约,实现职责解耦;
- 扩展能力突破:通过ArkTS多接口实现,打破抽象类单继承限制,商品子类可按需组合“可打折、可满减、可退换”等行为能力;
- 规则动态配置:设计“通用+具体”的促销规则契约,支持生效时间、启用状态配置,规则失效自动恢复原价,适配真实电商场景;
- 工程规范升级:收拢接口至
model/interface,新增entity(实体)、utils(工具)目录,统一管理核心逻辑,提升代码可维护性。
7.2 核心价值
- 解耦提效:属性、规则、行为三层解耦,新增促销类型仅需扩展契约,无需修改原有代码,符合“开闭原则”;
- 动态适配:促销规则支持时间校验、启用状态切换,规则失效自动恢复原价,适配电商平台多活动并行的业务场景;
- 工程化落地:完善的目录分层和工具类设计,符合大型电商项目的代码组织规范,便于团队协作和后期维护。
八、代码仓库
- 工程名称:
ClassObjectDemo_4 - 仓库地址:https://gitee.com/juhetianxia321/harmony-os-code-base.git
九、下节预告
本节核心目标是帮大家掌握「行为契约解耦 + 多接口突破单继承」的核心编码思路,而非覆盖所有电商促销场景。从实际业务视角看,当前实现确实仅适配了基础的打折、满减规则,其规则很粗陋。基础阶段的重点是先吃透 “属性 - 行为 - 规则” 三层解耦的架构逻辑,而非堆砌业务功能。等大家掌握了核心思路,后续拓展复杂促销规则会水到渠成。
下一节将聚焦泛型与商品容器的灵活适配,基于我们已掌握的泛型类 / 泛型方法知识,开发通用的商品容器(支持任意商品类型),整合 “抽象类 + 属性契约 + 行为契约 + 泛型” 的四层架构,实现电商商品模块的全链路工程化开发,重点完成商品的统一增删查改、多维度排序筛选等核心商品管理能力落地。
浙公网安备 33010602011771号