零基础鸿蒙应用开发第二十六节:泛型与商品容器的灵活适配

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

【学习目标】

  1. 聚焦泛型在电商商品管理场景的实战落地,完成通用商品容器的设计与实现,落地商品统一增删查改、多维度排序筛选能力;
  2. 熟练运用 <T extends AbstractGoods> 泛型约束语法,在保障类型安全的前提下,实现数码/图书等多品类商品的通用化管理;
  3. 封装泛型工具类,解决不同商品类型管理逻辑冗余的问题,提升代码复用性与工程化程度;
  4. 整合“抽象类+属性契约+行为契约+泛型”四层架构,完成电商商品模块的工程化闭环;

【学习重点】

  • 实战核心:聚焦T extends AbstractGoods在商品容器中的应用,解决多品类商品管理的复用性与类型安全问题;
  • 容器设计:用泛型类实现商品统一增删查改,适配所有商品子类,避免为不同品类重复编写管理逻辑;
  • 工具封装:通过“泛型方法+比较器接口”实现商品排序/筛选,聚焦业务逻辑而非语法本身;
  • 架构整合:将泛型作为通用管理层,承接前序的抽象类与契约体系,形成完整的商品管理链路;
  • 交互验证:通过鸿蒙UI联动泛型能力,验证商品管理逻辑的正确性,而非关注UI布局规范。

一、工程结构

复制上一节的ClassObjectDemo_4工程,重命名为ClassObjectDemo_5,聚焦商品管理能力扩展,重构目录结构如下:

ClassObjectDemo_5/
├── entry/                          # 应用主模块
│   ├── src/main/ets/
│   │   ├── pages/                  # 视图层:仅存放UI交互
│   │   │   └── Index.ets           # 测试页面:泛型商品管理实战+UI交互
│   │   ├── model/                  # 核心模型层
│   │   │   ├── entity/             # 实体层:核心数据结构
│   │   │   │   ├── AbstractGoods.ets      # 抽象商品基类
│   │   │   │   ├── DigitalGoods.ets       # 数码商品子类
│   │   │   │   ├── BookGoods.ets          # 图书商品子类
│   │   │   │   └── GoodsStatus.ets        # 商品状态枚举
│   │   │   ├── interface/          # 契约层:属性/行为/比较器接口
│   │   │   │   ├── attribute/      # 属性契约
│   │   │   │   │   ├── IBaseGoods.ets
│   │   │   │   │   ├── IDigitalGoods.ets
│   │   │   │   │   └── IBookGoods.ets
│   │   │   │   ├── behavior/       # 行为契约
│   │   │   │   │   ├── IDiscountable.ets
│   │   │   │   │   ├── IFullReductionable.ets
│   │   │   │   │   ├── IReturnable.ets
│   │   │   │   │   └── IGoodsComparator.ets  # 泛型比较器接口
│   │   │   │   └── promotion/      # 促销契约
│   │   │   │       ├── IPromotionRule.ets
│   │   │   │       ├── IDiscountRule.ets
│   │   │   │       └── IFullReductionRule.ets
│   │   │   ├── comparator/         # 比较器实现(排序规则)
│   │   │   │   ├── StockAscComparator.ets     # 库存升序
│   │   │   │   ├── StockDescComparator.ets    # 库存降序
│   │   │   │   └── PriceAscComparator.ets     # 价格升序
│   │   │   └── utils/              # 泛型工具层
│   │   │       ├── GoodsList.ets   # 泛型商品容器(增删查改)
│   │   │       ├── GoodsTool.ets   # 泛型工具类(排序/筛选)
│   │   │       └── PromotionValidator.ets     # 复用:促销规则校验
│   ├── resources/                  # 资源目录(默认生成)
│   └── module.json5                # 模块配置(默认生成)
└── hvigorfile.ts                   # 构建脚本(默认生成)

二、泛型的实战价值:解决商品管理的核心痛点

在上一节的数码/图书商品体系中,若为不同商品类型单独编写管理逻辑,会出现代码冗余、类型固化、扩展困难三大问题:

  • 冗余:数码/图书商品的添加、删除、查询逻辑100%重复,仅类型不同;
  • 固化:新增食品/服饰等商品类型时,需重复开发全套管理逻辑;
  • 泛型的实战解决思路:
    通过<T extends AbstractGoods>将类型参数化,一套逻辑适配所有商品子类,既保留“数码/图书”的类型区分,又复用管理逻辑,兼顾“复用性+类型安全”——这是泛型在电商商品管理场景的核心价值。

说明:第十五节已学习泛型核心知识,本节聚焦实战落地,不再重复基础语法。

三、泛型商品容器

3.1 统一管理所有商品(增删查改)

基于泛型实现通用商品容器,核心解决“多品类商品管理逻辑复用”问题(优化:按商品唯一ID判重,符合业务实际):

// model/utils/GoodsList.ets
import { AbstractGoods } from '../entity/AbstractGoods';

/**
 * 泛型商品容器:适配所有AbstractGoods子类(数码/图书/新增品类)
 * 核心:无需为不同商品类型单独编写增删查改逻辑
 */
export class GoodsList<T extends AbstractGoods> {
  // 私有列表:对外仅返回副本,保障数据安全
  private readonly goodsList: T[] = [];

  /**
   * 添加单个商品(按goodsId判重,避免重复添加)
   */
  public addGoods(item: T): boolean {
    if (!item) {
      console.warn("【GoodsList】添加失败:商品实例为空");
      return false;
    }
    if (this.goodsList.some(g => g.goodsId === item.goodsId)) {
      console.warn(`【GoodsList】添加失败:goodsId=${item.goodsId}的商品已存在`);
      return false;
    }
    this.goodsList.push(item);
    console.log(`【GoodsList】成功添加:${item.name}(goodsId=${item.goodsId})`);
    return true;
  }

  /**
   * 批量添加商品(自动过滤无效/重复项)
   */
  public addGoodsBatch(items: T[]): number {
    if (!Array.isArray(items) || items.length === 0) {
      return 0;
    }
    const validItems = items.filter(item => item && !this.goodsList.some(g => g.goodsId === item.goodsId));
    this.goodsList.push(...validItems);
    console.log(`【GoodsList】批量添加成功:共${validItems.length}件商品`);
    return validItems.length;
  }

  /**
   * 删除商品(按唯一goodsId)
   */
  public removeGoodsById(id: string): boolean {
    const index = this.goodsList.findIndex(g => g.goodsId === id);
    if (index === -1) {
      console.warn(`【GoodsList】删除失败:未找到goodsId为${id}的商品`);
      return false;
    }
    this.goodsList.splice(index, 1);
    console.log(`【GoodsList】成功删除:goodsId=${id}的商品`);
    return true;
  }

  /**
   * 按ID精准查询商品(返回副本,不篡改原数据)
   * 核心:适配List的精准查询场景,与getGoodsByName/ByCategory形成完整查询体系
   */
  public getGoodsById(id: string): T | undefined {
    const matchedGoods = this.goodsList.find(g => g.goodsId === id);
    return matchedGoods
  }

  /**
   * 修改商品库存(核心业务能力)
   */
  public updateGoodsStock(id: string, newStock: number): boolean {
    const targetGoods = this.goodsList.find(g => g.goodsId === id);
    if (!targetGoods) {
      console.warn(`【GoodsList】修改失败:未找到goodsId为${id}的商品`);
      return false;
    }
    targetGoods.stock = newStock;
    console.log(`【GoodsList】${targetGoods.name}库存已更新为:${newStock}`);
    return true;
  }
  
  // 按名称查询商品模糊查找
  public getGoodsByName(name: string): T[] {
    // 过滤名称包含目标字符串的商品(不区分大小写)
    const matchedGoods = this.goodsList.filter(goods =>
    goods.name.toLowerCase().includes(name.toLowerCase())
    );
    // 返回副本,避免外部篡改,同时做泛型类型转换
    const result = [...matchedGoods] as T[];
    console.log(`根据名称"${name}"查询到${result.length}个商品`);
    return result;
  }

  // 按分类筛选商品
  public getGoodsByCategory(category: string): T[] {
    const trimCategory = category?.trim();
    if (!trimCategory) {
      return [];
    }
    return this.goodsList.filter(g => g.category === trimCategory);
  }

  // 获取商品列表(返回副本,避免外部修改原数据)
  public getGoodsList(): T[] {
    return [...this.goodsList];
  }

  //  获取商品总数
  public getCount(): number {
    return this.goodsList.length;
  }

  // 清空列表
  public clear(): void {
    if (this.goodsList.length > 0) {
      this.goodsList.length = 0;
      console.log("【GoodsList】商品列表已清空");
    }
  }
}

3.2 泛型比较器接口:定义排序规范

复用泛型接口语法,为商品排序提供统一规范:

// model/interface/behavior/IGoodsComparator.ets
import { AbstractGoods } from '../../entity/AbstractGoods';

/**
 * 泛型比较器接口:统一商品排序规则的定义规范
 */
export interface IGoodsComparator<T extends AbstractGoods> {
  /**
   * 比较两个商品
   * @returns a<b返回负数,a>b返回正数,相等返回0
   */
  compare(a: T, b: T): number;
}

3.3 比较器实现:聚焦排序业务规则

基于泛型比较器接口,实现库存/价格排序规则:当然你也可以自己拓展补充。

库存升序

// model/comparator/StockAscComparator.ets
import { AbstractGoods } from '../entity/AbstractGoods';
import { IGoodsComparator } from '../interface/behavior/IGoodsComparator';

/**
 * 库存升序比较器:业务规则→库存小的商品排在前面
 */
export class StockAscComparator implements IGoodsComparator<AbstractGoods> {
  compare(a: AbstractGoods, b: AbstractGoods): number {
    return a.stock - b.stock;
  }
}

库存降序

// model/comparator/StockDescComparator.ets
import { AbstractGoods } from '../entity/AbstractGoods';
import { IGoodsComparator } from '../interface/behavior/IGoodsComparator';

/**
 * 库存降序比较器:业务规则→库存大的商品排在前面
 */
export class StockDescComparator implements IGoodsComparator<AbstractGoods> {
  compare(a: AbstractGoods, b: AbstractGoods): number {
    return b.stock - a.stock;
  }
}

价格升序


// model/comparator/PriceAscComparator.ets
import { AbstractGoods } from '../entity/AbstractGoods';
import { IGoodsComparator } from '../interface/behavior/IGoodsComparator';

/**
 * 价格升序比较器:业务规则→价格低的商品排在前面
 */
export class PriceAscComparator implements IGoodsComparator<AbstractGoods> {
  compare(a: AbstractGoods, b: AbstractGoods): number {
    return a.price - b.price;
  }
}

3.4 泛型工具类:封装排序/筛选能力

用静态泛型方法封装商品筛选/排序,核心解决“多维度数据操作复用”问题:

知识补充:static修饰方法时表示静态方法,需通过类名.方法名调用,无需实例化类;静态方法天然适配工具类的“无状态、通用能力”特性。(下一节重点讲解)

// model/utils/GoodsTool.ets
import { AbstractGoods } from '../entity/AbstractGoods';
import { IGoodsComparator } from '../interface/behavior/IGoodsComparator';

/**
 * 泛型商品工具类:封装通用的排序/筛选能力
 */
export class GoodsTool {
  /**
   * 筛选有库存商品
   */
  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[] {
    if (!Array.isArray(goodsList) || !comparator) return [];
    const validGoods = goodsList.filter(g => !!g);
    return [...validGoods].sort((a, b) => comparator.compare(a, b));
  }
}

四、通过UI交互验证

4.1 联动泛型能力与UI交互

(UI仅为验证逻辑,无需关注布局规范;整合上一节促销规则,体现架构闭环)

import { AbstractGoods } from '../model/entity/AbstractGoods';
import { DigitalGoods } from '../model/entity/DigitalGoods';
import { BookGoods } from '../model/entity/BookGoods';
import { GoodsStatus } from '../model/entity/GoodsStatus';
import { GoodsList } from '../model/utils/GoodsList';
import { GoodsTool } from '../model/utils/GoodsTool';
import { StockAscComparator } from '../model/comparator/StockAscComparator';
import { StockDescComparator } from '../model/comparator/StockDescComparator';
import { PriceAscComparator } from '../model/comparator/PriceAscComparator';
import { prompt } from '@kit.ArkUI';
import { LengthMetrics } from '@kit.ArkUI';
import { IDigitalGoods } from '../model/interface/attribute/IDigitalGoods';
import { IBookGoods } from '../model/interface/attribute/IBookGoods';
import { IDiscountRule } from '../model/interface/promotion/IDiscountRule';

@Entry
@Component
struct Index {
  @State goodsList: AbstractGoods[] = [];
  @State inputStock: number = 0;
  @State inputName: string = '';
  private goodsContainer = new GoodsList<AbstractGoods>();

  // 页面初始化:加载商品(含促销规则,整合前序内容)
  aboutToAppear(): void {
    const initGoodsList = this.initGoods();
    this.goodsContainer.addGoodsBatch(initGoodsList);
    this.goodsList = this.goodsContainer.getGoodsList();
  }

  // 初始化商品(整合促销规则,体现四层架构)
  private initGoods(): AbstractGoods[] {
    // 数码商品(带8折促销)
    const digitalProps: IDigitalGoods = {
      name: "鸿蒙Mate70手机",
      price: 5999,
      stock: 100,
      costPrice: 4000,
      category: "数码产品",
      status: GoodsStatus.ON_SHELF,
      brand: "华为",
      warranty: 2
    };
    const digitalGoods = new DigitalGoods(digitalProps);
    const discountRule: IDiscountRule = {
      promotionId: "discount_digital_001",
      promotionName: "双十二数码会员8折",
      startTime: new Date(),
      endTime: new Date(2025, 11, 31),
      isEnabled: true,
      discountRate: 0.8,
      isMemberExclusive: true
    };
    digitalGoods.setDiscountRule(discountRule);

    // 鸿蒙平板(无促销)
    const tabletProps: IDigitalGoods = {
      name: "鸿蒙平板Pro",
      price: 3999,
      stock: 80,
      costPrice: 2500,
      category: "数码产品",
      status: GoodsStatus.ON_SHELF,
      brand: "华为",
      warranty: 2
    };
    const tabletGoods = new DigitalGoods(tabletProps);

    // 图书商品(带7折促销)
    const bookProps: IBookGoods = {
      name: "鸿蒙开发实战",
      price: 69,
      stock: 500,
      costPrice: 30,
      category: "图书",
      status: GoodsStatus.ON_SHELF,
      author: "散修",
      publishDate: "2025-01"
    };
    const bookGoods = new BookGoods(bookProps);
    const bookDiscountRule: IDiscountRule = {
      promotionId: "discount_book_001",
      promotionName: "图书全场7折",
      startTime: new Date(),
      endTime: new Date(2025, 11, 31),
      isEnabled: true,
      discountRate: 0.7,
      isMemberExclusive: false
    };
    bookGoods.setDiscountRule(bookDiscountRule);

    return [digitalGoods, tabletGoods, bookGoods];
  }

  // 库存升序排序
  private sortStockAsc(): void {
    this.goodsList = GoodsTool.sortByComparator(this.goodsList, new StockAscComparator());
  }

  // 库存降序排序
  private sortStockDesc(): void {
    this.goodsList = GoodsTool.sortByComparator(this.goodsList, new StockDescComparator());
  }

  // 价格升序排序
  private sortPriceAsc(): void {
    this.goodsList = GoodsTool.sortByComparator(this.goodsList, new PriceAscComparator());
  }

  // 筛选有库存商品
  private filterInStock(): void {
    this.goodsList = GoodsTool.filterInStock(this.goodsList);
  }

  // 筛选数码类商品
  private filterDigital(): void {
    this.goodsList = this.goodsContainer.getGoodsByCategory("数码产品");
  }

  // 重置列表
  private resetList(): void {
    this.goodsContainer.clear();
    this.goodsContainer.addGoodsBatch(this.initGoods());
    this.goodsList = this.goodsContainer.getGoodsList();
    this.inputName = '';
    this.inputStock = 0;
    prompt.showToast({ message: "列表已重置为初始状态" });
  }

  // 修改库存(增加合法性校验)
  private updateStock(id: string, stock: number): void {
    if (stock < 0) {
      prompt.showToast({ message: "库存不能为负数" });
      return;
    }
    const success = this.goodsContainer.updateGoodsStock(id, stock);
    success ? prompt.showToast({ message: "库存修改成功" }) : prompt.showToast({ message: "库存修改失败" });
    this.goodsList = this.goodsContainer.getGoodsList();
  }

  // 删除商品
  private deleteGoods(id: string): void {
    const success = this.goodsContainer.removeGoodsById(id);
    success ? prompt.showToast({ message: "商品删除成功" }) : prompt.showToast({ message: "商品删除失败" });
    this.goodsList = this.goodsContainer.getGoodsList();
  }

  // 按名称查找商品
  private findGoodsByName(): void {
    if (!this.inputName.trim()) {
      this.goodsList = this.goodsContainer.getGoodsList();
      prompt.showToast({ message: "请输入商品名称" });
      return;
    }
    const goods = this.goodsContainer.getGoodsByName(this.inputName);
    this.goodsList = goods;
    if (!goods) prompt.showToast({ message: `未找到【${this.inputName}】` });
  }

  build() {
    Column() {
      // 页面标题
      Text("泛型商品管理实战")
        .fontSize(22)
        .fontWeight(FontWeight.Bold)
        .margin(20);

      // 商品名称查询区
      Row({ space: 10 }) {
        TextInput({ placeholder: "输入商品名", text: this.inputName })
          .width(150)
          .onChange(val => this.inputName = val);
        Button("查找商品")
          .onClick(() => this.findGoodsByName());
      }.margin({ bottom: 15 })

      // 排序/筛选按钮组
      Flex({
        space: { main: LengthMetrics.vp(10), cross: LengthMetrics.vp(10) },
        wrap: FlexWrap.Wrap,
        justifyContent: FlexAlign.Center
      }) {
        Button("库存升序").onClick(() => this.sortStockAsc());
        Button("库存降序").onClick(() => this.sortStockDesc());
        Button("价格升序").onClick(() => this.sortPriceAsc());
        Button("筛选有库存").onClick(() => this.filterInStock());
        Button("筛选数码商品").onClick(() => this.filterDigital());
        Button("重置列表").onClick(() => this.resetList());
      }.margin({ bottom: 15 });

      // 商品列表展示
      Scroll() {
        Column({ space: 10 }) {
          ForEach(this.goodsList, (goods: AbstractGoods) => {
            Column({ space: 8 }) {
              Row(){
                Text(goods.name).fontSize(18).fontWeight(FontWeight.Medium);
                Text(`${goods.status === GoodsStatus.ON_SHELF ? '在售':'已下架'}`).fontSize(14).fontColor('#666');
              }
              Text(`分类:${goods.category} | 价格:¥${goods.price.toFixed(2)} | 库存:${goods.stock}件`)
                .fontSize(14)
                .fontColor(goods.stock === 0 ? Color.Red : '#666');

              // 数码商品专属信息
              if (goods instanceof DigitalGoods) {
                Text(`品牌:${goods.brand} | 质保:${goods.warranty}年`).fontSize(12).fontColor('#999');
              }

              // 图书商品专属信息
              if (goods instanceof BookGoods) {
                Text(`作者:${goods.author} | 出版日期:${goods.publishDate}`).fontSize(12).fontColor('#999');
              }

              // 操作按钮
              Row({ space: 8 }) {
                Button("删除商品")
                  .backgroundColor("#F87272")
                  .fontColor(Color.White)
                  .borderRadius(6)
                  .onClick(() => this.deleteGoods(goods.goodsId));

                TextInput({ placeholder: "库存", text: `${goods.stock}` })
                  .type(InputType.Number)
                  .width(70)
                  .onChange(val => this.inputStock = isNaN(Number(val)) ? 0 : Number(val));

                Button("修改库存")
                  .onClick(() => this.updateStock(goods.goodsId, this.inputStock));
              }.width('100%')
            }
            .width('90%')
            .padding(15)
            .backgroundColor(Color.White)
            .borderRadius(10)
            .shadow({ radius: 5, color: '#ccc', offsetX: 2, offsetY: 2 }) // 样式仅为美观
          })
        }.width('100%').padding(10)
      }
      .layoutWeight(1)
      .width('100%')
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#f5f5f5')
    .padding(10);
  }
}

4.2 交互亮点

  1. 全链路数据管控:泛型容器统一管理商品增删改查,对外仅返回数据副本,避免外部篡改;
  2. 多维度操作:支持库存/价格排序、分类/库存筛选,一套泛型逻辑适配所有商品类型;
  3. 数据安全:库存修改校验负数、添加商品按ID判重,避免非法数据录入;
  4. 架构闭环:整合前序的促销规则,体现“属性契约→抽象类→泛型管理→UI交互”的完整链路。

运行效果

泛型商品管理_20260108115144_614_125

五、架构整合逻辑

graph LR %% 基础契约层(复用二十三节) A[属性契约层<br/>IBaseGoods/IDigitalGoods等<br/>定义:商品固有属性规范] --> B[抽象类层<br/>AbstractGoods<br/>封装:通用属性复用/基础逻辑校验/抽象方法定义] C[促销规则契约层<br/>IPromotionRule/IDiscountRule等<br/>定义:促销规则数据规范] --> D[规则校验工具层<br/>PromotionValidator<br/>统一:规则合法性校验] %% 行为契约层(拆分促销/排序接口,分工更清晰) E1[行为契约-促销接口<br/>IDiscountable/IFullReductionable<br/>定义:商品促销行为规范] --> F[商品子类层<br/>DigitalGoods/BookGoods<br/>实现:多行为契约+动态规则注入+差异化业务逻辑] E2[行为契约-排序接口<br/>IGoodsComparator<br/>定义:商品排序规则规范] --> K[比较器实现层<br/>StockAsc/PriceAsc等<br/>实现:排序规则落地] %% 新增:泛型通用管理层(二十四节核心) B --> H[泛型约束层<br/>T extends AbstractGoods<br/>保障:类型安全+多品类适配] H --> I[泛型容器层<br/>GoodsList<br/>封装:商品统一增删查改] H --> J[泛型工具层<br/>GoodsTool<br/>封装:商品排序/筛选通用能力] %% 核心依赖链路(复用+新增) B --> F D --> F F --> I I --> J J --> K E2 --> J I & J & F --> G[UI交互层<br/>Index.ets<br/>可视化:泛型能力验证+促销/排序操作]

六、内容总结

  1. 泛型核心价值:通过T extends AbstractGoods的泛型约束,实现“一套管理逻辑适配所有商品子类”,既保证类型安全,又解决多品类商品管理的代码冗余问题;
  2. 核心组件分工:泛型容器(GoodsList)负责商品增删查改、泛型工具类(GoodsTool)负责排序/筛选入口、比较器负责具体排序规则,三层组件覆盖商品管理全场景;
  3. 工程化价值:新增商品品类(如食品、服饰)时,仅需实现AbstractGoods子类,无需修改泛型相关代码,大幅降低维护成本。

七、代码仓库

八、下节预告

本节课基于泛型实现了单页面商品管理,但创建多个GoodsList实例时,数据无法同步。
下一节将聚焦静态成员+单例模式,基于本节的泛型容器升级为全应用唯一的商品管理中枢:

  1. 静态成员:为泛型容器添加静态属性/方法,实现全局复用的商品校验、规则配置能力;
  2. 单例模式:改造GoodsListGoodsManager,保证全应用仅一个实例,解决多页面数据不一致问题;
  3. 深度整合:泛型(类型安全)+ 静态(全局复用)+ 单例(数据一致),完成工程化商品管理体系。
posted @ 2026-01-24 14:25  鸿蒙-散修  阅读(1)  评论(0)    收藏  举报