零基础鸿蒙应用开发第二十七节:全局商品管理之单利模式
【学习目标】
- 掌握静态成员(静态属性/静态方法)核心特性,理解“类级复用”与“实例级复用”的本质区别,封装全局通用的商品工具能力;
- 深度理解单例模式底层逻辑(私有化构造器+全局唯一实例),基于泛型实现
GoodsManager单例类,保障全应用商品数据唯一性; - 整合“泛型+静态成员+单例模式”,构建“全局数据唯一+操作逻辑统一+类型安全”的商品管理体系;
- 解决应用商品数据多实例不同步问题,实现库存修改的全局实时同步;
【学习重点】
- 静态核心:区分静态成员(类所有)与实例成员(对象所有)的内存分配,封装全局复用的商品校验/转换工具;
- 单例实现:通过
private constructor()+静态方法getInstance()确保GoodsManager全应用唯一; - 数据同步:单例的商品操作会同步到所有引用处,从根源解决多实例数据不一致;
- 功能演示:通过“双实例对比+单例同步”的代码案例,直观验证单例模式的全局数据一致性;
- 架构升级:以单例
GoodsManager为核心,承接泛型容器能力,形成“单例中枢→多场景消费”的工程化架构。
一、工程结构
复制上一节的ClassObjectDemo_5工程,重命名为ClassObjectDemo_6,新增全局单例管理类,工程目录如下:
ClassObjectDemo_6/
├── entry/ # 应用主模块
│ ├── src/main/ets/
│ │ ├── pages/ # 视图层
│ │ │ └── Index.ets # 核心演示页(单文件验证逻辑)
│ │ ├── model/ # 核心模型层
│ │ │ ├── entity/ # 实体层(复用原有)
│ │ │ ├── interface/ # 契约层(复用原有)
│ │ │ ├── comparator/ # 比较器实现(复用原有)
│ │ │ └── utils/ # 工具层:新增静态/单例类
│ │ │ ├── GoodsList.ets # 复用:上一节的泛型商品容器
│ │ │ ├── GoodsTool.ets # 补充:静态工具方法
│ │ │ └── GoodsManager.ets # 新增:单例商品管理中枢
│ ├── resources/ # 资源目录
│ └── module.json5 # 模块配置
└── hvigorfile.ts # 构建脚本
二、核心痛点回顾与技术选型
2.1 多实例数据不一致的本质问题
上一节实现的GoodsList泛型容器,每次new都会创建独立实例:
// 实例A(模拟页面1)
const goodsContainerA = new GoodsList<AbstractGoods>();
// 实例B(模拟页面2)
const goodsContainerB = new GoodsList<AbstractGoods>();
- 内存中存在多份独立的商品数据,修改实例A的库存,实例B数据无变化;
- 重复编写增删查改逻辑,代码冗余且数据状态混乱。
2.2 技术选型:静态成员+单例模式
| 技术特性 | 解决的核心问题 | 适用场景 |
|---|---|---|
| 静态成员 | 工具方法/常量的全局复用,无需创建实例 | 商品校验、格式转换、常量定义 |
| 单例模式 | 保证类实例唯一,数据全局共享 | 商品数据的统一管理 |
| 泛型 | 保障商品类型安全,适配所有商品子类 | 单例类的类型约束 |
三、静态成员:全局复用的工具能力
3.1 静态成员核心逻辑
- 静态属性/方法:属于类本身,内存仅一份,通过
类名.属性/方法直接调用; - 实例属性/方法:属于每个实例对象,每个实例有独立副本,需通过
实例名调用; - 核心优势:工具类无需实例化,全局复用无冗余;
- 修饰符:通过
static修饰。
3.2 重构GoodsTool静态工具类
// model/utils/GoodsTool.ets
import { AbstractGoods } from '../entity/AbstractGoods';
import { IGoodsComparator } from '../interface/behavior/IGoodsComparator';
/**
* 通用商品工具类(静态成员实现)
* 核心:全局复用,无需创建实例,封装无状态工具能力
*/
export class GoodsTool {
// 新增:全局常量:价格/库存阈值(静态属性)
public static readonly MIN_PRICE = 0;
public static readonly MAX_STOCK = 9999;
// 静态方法:校验库存合法性
public static validateStock(stock: number): boolean {
return typeof stock === 'number' && stock >= 0 && stock <= GoodsTool.MAX_STOCK;
}
// 静态方法:校验商品完整性(适配抽象类自动生成的goodsId)
public static validateGoods<T extends AbstractGoods>(goods: T): boolean {
if (!goods) return false;
const hasBasicProps = !!goods.name && !!goods.category;
const hasValidPrice = typeof goods.price === 'number' && goods.price >= GoodsTool.MIN_PRICE;
const hasValidStock = GoodsTool.validateStock(goods.stock);
return hasBasicProps && hasValidPrice && hasValidStock;
}
// 静态方法:价格格式化
public static formatPrice(price: number): string {
if (typeof price !== 'number' || price < GoodsTool.MIN_PRICE) return '¥0.00';
return `¥${price.toFixed(2)}`;
}
/** 筛选有库存商品 */
static filterInStock<T extends AbstractGoods>(goodsList: T[]): T[] {
if (!Array.isArray(goodsList)) return [];
return goodsList.filter(g => g && g.stock > 0);
}
/** 价格区间筛选 */
static filterByPriceRange<T extends AbstractGoods>(goodsList: T[], minPrice: number, maxPrice: number): T[] {
if (!Array.isArray(goodsList) || minPrice > maxPrice) return [];
const validMin = Math.max(minPrice, 0);
const validMax = Math.max(maxPrice, 0);
return goodsList.filter(g => g && g.price >= validMin && g.price <= validMax);
}
/** 自定义比较器排序(返回副本,不修改原数据) */
static sortByComparator<T extends AbstractGoods>(goodsList: T[], comparator: IGoodsComparator<T>): T[] {
const validGoods = goodsList.filter(g => g);
return [...validGoods].sort((a, b) => comparator.compare(a, b));
}
}
3.3 静态成员调用示例
// 调用静态方法
const isValidStock = GoodsTool.validateStock(100); // 输出:true
const priceStr = GoodsTool.formatPrice(5999); // 输出:¥5999.00
// 访问静态属性
console.log(GoodsTool.MAX_STOCK); // 输出:9999
四、单例模式:全局唯一的商品管理中枢
4.1 单例模式核心实现逻辑
目标:保证类仅有一个实例,提供全局访问点,核心步骤:
- 私有化构造器:
private constructor()禁止外部new创建实例; - 私有静态属性:存储唯一实例(初始为
null); - 公共静态方法:
getInstance()提供全局访问入口(懒加载创建实例)。
4.2 实现GoodsManager单例类
// model/utils/GoodsManager.ets
import { AbstractGoods } from '../entity/AbstractGoods';
import { GoodsList } from './GoodsList';
import { GoodsTool } from './GoodsTool';
/**
* 商品管理单例类(泛型+单例模式)
* 核心:全应用仅一个实例,统一管理商品数据,代理GoodsList全部能力
*/
export class GoodsManager<T extends AbstractGoods> {
// 1. 私有静态属性:存储唯一实例
private static instance: GoodsManager<AbstractGoods> | null = null;
// 2. 私有实例属性:复用上一节的泛型商品容器
private readonly goodsContainer: GoodsList<AbstractGoods>;
// 3. 私有化构造器:禁止外部创建实例
private constructor() {
this.goodsContainer = new GoodsList<AbstractGoods>();
console.log("【GoodsManager】单例实例初始化完成");
}
// 4. 公共静态方法:全局唯一访问入口(懒加载+泛型适配)
public static getInstance<T extends AbstractGoods>(): GoodsManager<T> {
if (!GoodsManager.instance) {
GoodsManager.instance = new GoodsManager<AbstractGoods>();
}
return GoodsManager.instance as GoodsManager<T>;
}
/** 添加商品(带合法性校验) */
public addGoods(goods: T): boolean {
if (!GoodsTool.validateGoods(goods)) {
console.log(`【单例】添加失败:${goods.name}信息不合法`);
return false;
}
const result = this.goodsContainer.addGoods(goods);
result && console.log(`【单例】成功添加商品:${goods.name}(ID: ${goods.goodsId})`);
return result;
}
/** 修改商品库存(带合法性校验) */
public updateGoodsStock(id: string, newStock: number): boolean {
if (!GoodsTool.validateStock(newStock)) {
console.log(`【单例】修改失败:库存${newStock}不合法(范围0-${GoodsTool.MAX_STOCK})`);
return false;
}
const result = this.goodsContainer.updateGoodsStock(id, newStock);
if (result) {
console.log(`【单例】库存修改成功,新库存:${newStock}`);
} else {
console.log(`【单例】修改失败:未找到goodsId=${id}的商品`);
}
return result;
}
/** 获取完整商品列表(返回副本,避免外部篡改) */
public getGoodsList(): T[] {
return this.goodsContainer.getGoodsList() as T[];
}
/** 获取商品总数 */
public getCount(): number {
return this.goodsContainer.getCount();
}
/** 删除商品 */
public removeGoodsById(id: string): boolean {
const result = this.goodsContainer.removeGoodsById(id);
if (result) {
console.log(`【单例】成功删除goodsId=${id}的商品`);
} else {
console.log(`【单例】删除失败:未找到goodsId=${id}的商品`);
}
return result;
}
/** 批量添加商品 */
public addGoodsBatch(goodsList: T[]): number {
// 批量添加前先过滤非法商品(复用GoodsTool校验)
const validGoods = goodsList.filter(goods => GoodsTool.validateGoods(goods));
const count = this.goodsContainer.addGoodsBatch(validGoods as AbstractGoods[]);
console.log(`【单例】批量添加${count}个商品成功(过滤${goodsList.length - count}个非法商品)`);
return count;
}
/**
* 按商品名称模糊查询(不区分大小写,返回副本避免篡改)
* @param name 商品名称(支持模糊匹配)
* @returns 匹配的商品列表
*/
public getGoodsByName(name: string): T[] {
const matchedGoods = this.goodsContainer.getGoodsByName(name) ;
console.log(`【单例】根据名称"${name}"查询到${matchedGoods.length}个商品`);
return matchedGoods as T[];
}
/**
* 按商品ID精准查询(返回副本避免篡改)
* @param id 商品唯一ID
* @returns 匹配的商品(副本),未找到返回undefined
*/
public getGoodsById(id: string): T | undefined {
const matchedGoods = this.goodsContainer.getGoodsById(id) as T | undefined;
if (matchedGoods) {
console.log(`【单例】根据ID"${id}"查询到商品:${matchedGoods.name}`);
} else {
console.log(`【单例】根据ID"${id}"未查询到商品`);
}
// 返回副本,避免外部修改内部数据
return matchedGoods;
}
/**
* 按商品分类精准筛选(返回副本避免篡改)
* @param category 商品分类(自动去除首尾空格)
* @returns 匹配的商品列表
*/
public getGoodsByCategory(category: string): T[] {
const matchedGoods = this.goodsContainer.getGoodsByCategory(category) as T[];
console.log(`【单例】根据分类"${category.trim()}"查询到${matchedGoods.length}个商品`);
return matchedGoods;
}
/**
* 清空所有商品(全应用统一清空)
*/
public clearAllGoods(): void {
const beforeCount = this.getCount();
this.goodsContainer.clear();
console.log(`【单例】成功清空所有商品,共清空${beforeCount}个商品`);
}
}
4.3 单例模式关键细节
- 懒加载:首次调用
getInstance()才创建实例,减少应用启动内存占用; - 私有化构造器:杜绝外部
new实例,从语法层面保证实例唯一性; - 全局统一:任何位置调用
getInstance()都获取同一个实例,操作同一套数据; - 逻辑复用:代理上一节
GoodsList的增删改查能力,无需重复编码; - 泛型适配:
getInstance<T>()支持泛型,适配不同类型的商品子类。
五、工程演示(验证单例数据同步)
5.1 核心演示代码
// pages/Index.ets
import { AbstractGoods } from '../model/entity/AbstractGoods';
import { DigitalGoods } from '../model/entity/DigitalGoods';
import { GoodsStatus } from '../model/entity/GoodsStatus';
import { IDigitalGoods } from '../model/interface/attribute/IDigitalGoods';
import { GoodsList } from '../model/utils/GoodsList';
import { GoodsManager } from '../model/utils/GoodsManager';
@Entry
@Component
struct Index {
@State goodsList: AbstractGoods[] = [];
@State logContent: string[] = []; // 页面日志展示
private goodsManager = GoodsManager.getInstance<AbstractGoods>(); // 单例引用
aboutToAppear() {
this.initSingletonData();
this.logContent.push("=== 日志展示区 ===");
this.logContent.push("点击下方按钮查看演示日志");
}
build() {
Column() {
Text("静态成员与单例模式演示")
.fontSize(20)
.fontWeight(FontWeight.Bold)
.margin(20);
Button("演示多实例数据不一致")
.onClick(() => this.demoMultiInstance())
.margin(10);
Button("演示单例模式数据同步")
.onClick(() => this.demoSingleInstance())
.margin(10);
Button("清空日志")
.onClick(() => this.logContent = ["=== 日志展示区 ==="])
.margin(10)
.backgroundColor(Color.Grey);
// 日志展示区域
Scroll() {
Column({ space: 8 }) {
ForEach(this.logContent, (log: string) => {
Text(log)
.fontSize(14)
.fontColor('#333')
.width('100%');
})
}.padding(10)
}
.layoutWeight(1)
.width('90%')
.backgroundColor('#fff')
.borderRadius(8)
.margin(10);
}
.width('100%')
.height('100%')
.backgroundColor('#f5f5f5')
.padding(10);
}
/** 初始化单例商品数据(适配抽象类自动生成goodsId) */
private initSingletonData(): void {
// 数码商品(无需手动赋值goodsId)
const digitalProps:IDigitalGoods = {
name: "鸿蒙Mate70手机",
price: 5999,
stock: 100,
costPrice: 4000,
category: "数码产品",
status: GoodsStatus.ON_SHELF,
brand: "华为",
warranty: 2
};
const tabletProps: IDigitalGoods = {
name: "鸿蒙平板Pro",
price: 3999,
stock: 80,
costPrice: 2500,
category: "数码产品",
status: GoodsStatus.ON_SHELF,
brand: "华为",
warranty: 2
};
this.goodsManager.addGoodsBatch([
new DigitalGoods(digitalProps),
new DigitalGoods(tabletProps),
]);
this.goodsList = this.goodsManager.getGoodsList();
}
/** 自定义日志方法(控制台+页面双展示) */
private addLog(logText: string): void {
console.log(logText);
this.logContent.push(logText);
this.logContent = [...this.logContent]; // 触发UI刷新
}
/** 演示1:多实例数据不一致 */
private demoMultiInstance(): void {
this.addLog("\n--- 多实例数据不一致演示 ---");
const containerA = new GoodsList<AbstractGoods>();
const containerB = new GoodsList<AbstractGoods>();
// 创建测试商品
const testGoods = new DigitalGoods({
name: "鸿蒙平板Pro",
price: 3999,
stock: 100,
costPrice: 2500,
category: "数码产品",
status: GoodsStatus.ON_SHELF,
brand: "华为",
warranty: 2
});
containerA.addGoods(testGoods);
// 输出对比结果
this.addLog("实例A商品总数:" + containerA.getCount()); // 1
this.addLog("实例B商品总数:" + containerB.getCount()); // 0
containerA.updateGoodsStock(testGoods.goodsId, 50);
this.addLog("实例A商品库存:" + containerA.getGoodsList()[0]?.stock); // 50
this.addLog("实例B商品库存:" + containerB.getGoodsList()[0]?.stock ?? "无"); // 无
this.addLog("===============================");
}
/** 演示2:单例模式数据同步 */
private demoSingleInstance(): void {
this.addLog("\n--- 单例模式数据同步演示 ---");
const managerA = GoodsManager.getInstance<AbstractGoods>();
const managerB = GoodsManager.getInstance<AbstractGoods>();
// 验证实例唯一性
this.addLog("实例是否唯一:" + (managerA === managerB)); // true
// 新增商品
const testGoods = new DigitalGoods({
name: "鸿蒙电脑",
price: 5999,
stock: 100,
costPrice: 4000,
category: "数码产品",
status: GoodsStatus.ON_SHELF,
brand: "华为",
warranty: 2
});
managerA.addGoods(testGoods);
// 总数对比
this.addLog("管理器A商品总数:" + managerA.getCount()); // 3(初始化2+新增1)
this.addLog("管理器B商品总数:" + managerB.getCount()); // 3
// 修改库存并验证同步
managerA.updateGoodsStock(testGoods.goodsId, 50);
this.addLog("管理器A商品库存:" + managerA.getGoodsList().find(g => g.goodsId === testGoods.goodsId)?.stock); // 50
this.addLog("管理器B商品库存:" + managerB.getGoodsList().find(g => g.goodsId === testGoods.goodsId)?.stock); // 50
// 反向修改验证
managerB.updateGoodsStock(testGoods.goodsId, 30);
this.addLog("管理器A库存(B修改后):" + managerA.getGoodsList().find(g => g.goodsId === testGoods.goodsId)?.stock); // 30
this.addLog("管理器B库存(B修改后):" + managerB.getGoodsList().find(g => g.goodsId === testGoods.goodsId)?.stock); // 30
this.addLog("===============================");
}
}
5.2 演示结果
点击“演示多实例数据不一致”:
--- 多实例数据不一致演示 ---
实例A商品总数:1
实例B商品总数:0
实例A商品库存:50
实例B商品库存:无
===============================
点击“演示单例模式数据同步”:
--- 单例模式数据同步演示 ---
实例是否唯一:true
【单例】成功添加商品:鸿蒙电脑(ID: goods_1736300000_1234)
管理器A商品总数:3
管理器B商品总数:3
【单例】库存修改成功,新库存:50
管理器A商品库存:50
管理器B商品库存:50
【单例】库存修改成功,新库存:30
管理器A库存(B修改后):30
管理器B库存(B修改后):30
===============================
运行效果

六、单例模式工程化最佳实践
6.1 避坑指南
- 禁止继承:单例类通过
private constructor杜绝子类继承,避免破坏单例特性; - 线程安全:ArkTS主线程为单线程模型,
private constructor + 懒加载getInstance()天然保证实例唯一,无需加锁;若涉及TaskPool/Worker异步场景,需注意线程间内存隔离; - 轻量化:单例仅存储核心数据(如商品列表),页面临时状态不放入,避免内存泄漏;
- 懒加载优先:首次调用
getInstance()创建实例,减少应用启动耗时。
6.2 静态成员 vs 单例模式
| 特性 | 静态成员 | 单例模式 |
|---|---|---|
| 调用方式 | 类名.属性/方法 | 类名.getInstance()获取实例后调用 |
| 数据类型 | 无状态(常量/工具方法) | 有状态(全局共享数据) |
| 核心用途 | 全局工具能力复用 | 全局数据状态管理 |
| 扩展性 | 无法继承扩展 | 可通过接口扩展能力 |
七、架构整合逻辑
graph LR
A[属性契约层<br/>定义商品基础属性] --> B[抽象类层<br/>自动生成goodsId+封装通用方法]
C[行为契约层<br/>排序/促销接口规范] --> B
B --> D[泛型约束层<br/>T extends AbstractGoods]
D --> E[静态工具类:全局校验/格式化]
D --> F[单例管理类:全局商品数据管控]
E --> F[单例复用静态校验能力]
F --> G[多页面/组件<br/>共享统一商品数据]
八、内容总结
- 静态成员核心:静态属性/方法属于类本身,内存仅一份,适合封装全局无状态工具能力(如校验、格式化),无需实例化即可调用;
- 多实例问题本质:多个
GoodsList实例数据相互隔离,修改操作无法同步,导致数据不一致; - 单例模式核心价值:通过
private constructor()+getInstance()保证实例唯一,所有操作基于同一套数据,从根源解决全局数据同步问题; - 工程化整合:单例类代理上一节泛型容器的核心能力,静态类提供全局工具,结合泛型保障类型安全,形成“数据唯一+操作统一”的商品管理体系。
九、代码仓库
- 工程名称:
ClassObjectDemo_6 - 仓库地址:https://gitee.com/juhetianxia321/harmony-os-code-base.git
十、下节预告
本节课通过静态成员+单例模式解决了多实例数据不一致问题,但商品排序体系仍有优化空间:排序规则与页面强耦合、新增规则需修改页面代码。下一节将聚焦工厂模式+策略模式:
- 基于
IGoodsComparator接口落地策略模式,新增排序规则仅需实现接口,遵循开闭原则; - 实现排序工厂类,页面通过排序类型即可获取比较器,屏蔽底层实现细节;
- 整合“单例管控+策略+工厂”,让排序逻辑灵活扩展、低耦合。
浙公网安备 33010602011771号