# 零基础鸿蒙应用开发第二十八节:商品排序体系之工厂与策略模式

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

【学习目标】

  1. 掌握策略模式核心思想,基于IGoodsComparator接口封装排序规则,实现排序逻辑的灵活扩展与解耦;
  2. 理解工厂模式的应用场景,开发排序工厂类统一管理比较器实例,屏蔽底层实现细节;
  3. 整合单例管控+策略模式+工厂模式,构建“数据统一、规则可扩展、调用低耦合”的商品排序体系;
  4. 实战验证多场景排序能力,实现商品列表页“一键切换排序规则”,完成从功能实现到工程化设计的进阶。

【学习重点】

  • 策略核心:以接口为契约,将库存、价格(升/降序)等排序规则封装为独立策略类,遵循“开闭原则”;
  • 工厂实现:通过排序工厂类统一创建比较器实例,页面仅需传入排序类型即可调用,降低耦合;
  • 架构整合:将策略模式、工厂模式融入上一节的单例体系,保证全局数据一致性的同时提升扩展性;
  • 实战验证:在商品列表页新增下拉选择器,支持多规则切换,验证工程化重构的优势。

一、工程结构

复制上一节的ClassObjectDemo_6工程,重命名为ClassObjectDemo_7,新增排序策略与工厂相关文件:

ClassObjectDemo_7/
├── entry/                          # 应用主模块
│   ├── src/main/ets/
│   │   ├── pages/                  # 视图层
│   │   │   └── Index.ets           # 核心演示页(排序规则切换实战)
│   │   ├── model/                  # 核心模型层
│   │   │   ├── entity/             # 实体层(复用原有)
│   │   │   ├── interface/          # 契约层(复用IGoodsComparator接口)
│   │   │   ├── comparator/         # 排序策略实现(新增价格降序比较器)
│   │   │   └── utils/              # 工具层:新增排序工厂类
│   │   │       ├── GoodsList.ets   # 复用:泛型商品容器
│   │   │       ├── GoodsTool.ets   # 复用:泛型商品工具类
│   │   │       ├── GoodsManager.ets # 复用:单例商品管理中枢
│   │   │       └── SortFactory.ets # 新增:排序比较器工厂类
│   ├── resources/                  # 资源目录
│   └── module.json5                # 模块配置
└── hvigorfile.ts                   # 构建脚本

二、核心痛点回顾与技术选型

2.1 第十六节排序体系的核心问题

在第十六节的实现中,页面调用排序逻辑需要直接依赖具体比较器类

// 原页面调用方式:强耦合具体实现
this.goodsList = GoodsTool.sortByComparator(this.goodsList, new StockAscComparator());

存在两个核心问题:

  1. 耦合度高:页面需导入所有用到的比较器,若比较器类名/路径变更,所有调用处都需修改;
  2. 扩展繁琐:例如新增价格降序排序时,需创建PriceDescComparator,再在页面导入并实例化,规则越多越混乱。

2.2 技术选型:策略模式+工厂模式

技术特性 解决的核心问题 适用场景
策略模式 排序规则与页面解耦,新增规则无需修改页面 多规则灵活切换的场景
工厂模式 统一管理比较器实例,屏蔽创建细节 实例化逻辑复杂、规则较多
单例模式 保证商品数据全局唯一,排序后数据同步 全应用商品数据统一管控

三、策略模式:排序规则的解耦与扩展

3.1 策略模式核心逻辑

策略模式的核心是接口定义契约,类实现具体策略,核心优势:

  • 新增策略无需修改原有代码,符合开闭原则(对扩展开放、对修改关闭)
  • 策略类专注单一排序规则,职责清晰,便于维护;
  • 页面仅依赖接口,不依赖具体实现,降低耦合。

3.2 新增价格降序排序策略

基于IGoodsComparator接口,新增价格降序比较器,与原有库存、价格升序比较器共同构成策略族:

// model/comparator/PriceDescComparator.ets(价格降序)
import { AbstractGoods } from '../entity/AbstractGoods';
import { IGoodsComparator } from '../interface/behavior/IGoodsComparator';

/**
 * 价格降序比较器
 */
export class PriceDescComparator implements IGoodsComparator<AbstractGoods> {
  compare(a: AbstractGoods, b: AbstractGoods): number {
    return b.price - a.price;
  }
}

原有StockAscComparator/StockDescComparator/PriceAscComparator保持不变,共同构成排序策略库。

四、工厂模式:排序比较器的统一管理

4.1 工厂模式核心逻辑

排序工厂类的核心是封装比较器的实例化逻辑,对外提供统一的获取接口,页面只需传入排序类型,无需关注具体实现类;同时通过缓存复用无状态的比较器实例,减少内存开销。

4.2 实现SortFactory排序工厂

// model/utils/SortFactory.ets
import { IGoodsComparator } from '../interface/behavior/IGoodsComparator';
import { AbstractGoods } from '../entity/AbstractGoods';
import { StockAscComparator } from '../comparator/StockAscComparator';
import { StockDescComparator } from '../comparator/StockDescComparator';
import { PriceAscComparator } from '../comparator/PriceAscComparator';
import { PriceDescComparator } from '../comparator/PriceDescComparator'; // 新增价格降序导入

// 定义排序类型枚举
export enum SortType {
  STOCK_ASC = "stockAsc", // 库存升序
  STOCK_DESC = "stockDesc", // 库存降序
  PRICE_ASC = "priceAsc", // 价格升序
  PRICE_DESC = "priceDesc" // 新增:价格降序
}

/**
 * 排序比较器工厂类
 * 核心:根据排序类型返回对应比较器实例,屏蔽创建细节;缓存实例减少重复创建
 */
export class SortFactory {
  // 缓存比较器实例(无状态策略类复用,提升性能)
  private static comparatorCache: Map<SortType, IGoodsComparator<AbstractGoods>> = new Map();

  /**
   * 获取比较器实例
   * @param sortType 排序类型
   * @returns 对应的比较器实例
   */
  public static getComparator(sortType: SortType): IGoodsComparator<AbstractGoods> {
    // 从缓存中读取,减少每次创建新的排序比较器,如果不存在则创建新的拍下比较器并存到Map中
    if (!SortFactory.comparatorCache.has(sortType)) {
      switch (sortType) {
        case SortType.STOCK_ASC:
          SortFactory.comparatorCache.set(sortType, new StockAscComparator());
          break;
        case SortType.STOCK_DESC:
          SortFactory.comparatorCache.set(sortType, new StockDescComparator());
          break;
        case SortType.PRICE_ASC:
          SortFactory.comparatorCache.set(sortType, new PriceAscComparator());
          break;
        case SortType.PRICE_DESC:
          SortFactory.comparatorCache.set(sortType, new PriceDescComparator());
          break;
        default:
          SortFactory.comparatorCache.set(sortType, new StockAscComparator());
      }
    }
    return SortFactory.comparatorCache.get(sortType)!;
  }
}

五、工程实战:排序规则的灵活切换

5.1 页面核心逻辑

// pages/Index.ets
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 { GoodsManager } from '../model/utils/GoodsManager';
import { GoodsTool } from '../model/utils/GoodsTool';
import { SortFactory, SortType } from '../model/utils/SortFactory';
import { IBookGoods } from '../model/interface/attribute/IBookGoods';
import { IDigitalGoods } from '../model/interface/attribute/IDigitalGoods';

@Entry
@Component
struct Index {
  @State goodsList: AbstractGoods[] = [];
  @State currentSortKey: SortType = SortType.STOCK_ASC;
  @State currentSortValue: string = "库存升序";
  @State selectMenus: SelectOption[] = [{value:"库存升序"},{value:"库存降序"},{value:"价格升序"},{value:"价格降序"}];
  private goodsManager = GoodsManager.getInstance();
  
  // 排序规则配置表(Map数组方式,适配Select索引联动)
  private sortMapList: Map<string,SortType>[] = [
    new Map([["库存升序",SortType.STOCK_ASC]]),
    new Map([[ "库存降序",SortType.STOCK_DESC]]),
    new Map([["价格升序",SortType.PRICE_ASC]]),
    new Map([["价格降序",SortType.PRICE_DESC]])
  ];

  aboutToAppear() {
    this.initData();
    this.sortGoods();
  }

  // 初始化商品数据
  private initData(): void {
    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
    };
    const bookProps: IBookGoods = {
      name: "鸿蒙开发实战",
      price: 69,
      stock: 500,
      costPrice: 30,
      category: "图书",
      status: GoodsStatus.ON_SHELF,
      author: "散修",
      publishDate: "2025-01"
    };

    const goodsList = [
      new DigitalGoods(digitalProps),
      new DigitalGoods(tabletProps),
      new BookGoods(bookProps)
    ];
    this.goodsManager.addGoodsBatch(goodsList);
    this.goodsList = this.goodsManager.getGoodsList();
  }

  // 排序商品列表(基于工厂获取比较器,解耦具体实现)
  private sortGoods(): void {
    // 共排序工厂中获取对应的排序比较实例
    const comparator = SortFactory.getComparator(this.currentSortKey);
    this.goodsList = GoodsTool.sortByComparator(this.goodsList, comparator);

    // 控制台日志(输出排序规则+结果,便于调试)
    console.log(`[排序切换] 当前规则:${this.currentSortValue}`);
    console.log(`[排序结果]`, this.goodsList.map(g => `${g.name}(价格:¥${g.price} | 库存:${g.stock})`));
  }

  build() {
    Column() {
      Text("工厂+策略模式 排序演示")
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
        .margin(20);

      // 排序选择器
      Row({ space: 10 }) {
        Text("排序规则:")
          .fontSize(16);

        Select(this.selectMenus)
          .value(this.currentSortValue)
          .onSelect((index: number, key: string) => {
            this.currentSortValue = key;
            const sortType = this.sortMapList[index].get(key);
            this.currentSortKey = sortType as SortType;
            this.sortGoods();
          })
          .width(200);
      }
      .margin({ bottom: 15 });

      // 商品列表(空状态兜底,提升用户体验)
      Scroll() {
        Column({ space: 10 }) {
          if (this.goodsList.length === 0) {
            Text("暂无商品数据")
              .fontSize(16)
              .fontColor('#999')
              .margin(20);
          } else {
            ForEach(this.goodsList, (goods: AbstractGoods) => {
              Column({ space: 5 }) {
                Text(goods.name)
                  .fontSize(18)
                  .fontWeight(FontWeight.Medium);
                Text(`分类:${goods.category} | 价格:¥${goods.price.toFixed(2)} | 库存:${goods.stock}件`)
                  .fontSize(14)
                  .fontColor('#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');
                }
              }
              .width('90%')
              .padding(15)
              .backgroundColor(Color.White)
              .borderRadius(8);
            })
          }
        }
        .padding(10)
        .width('100%');
      }
      .layoutWeight(1)
      .width('100%');
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#f5f5f5')
    .padding(10);
  }
}

5.2 演示效果

排序工程策略_20260108143734_616_125

六、架构整合与工程化最佳实践

6.1 三层架构整合逻辑

本次重构后,商品管理体系形成“数据层-规则层-调用层”的三层架构,各层职责清晰、解耦:

graph LR A[单例GoodsManager:全局数据层<br/>- 统一管控商品数据<br/>- 保证数据唯一性] --> B[GoodsTool:工具层<br/>- 排序/筛选等通用能力<br/>- 依赖策略接口] C[SortFactory:工厂层<br/>- 封装比较器实例化<br/>- 屏蔽底层实现细节<br/>- 缓存策略实例] --> B D[排序策略族:规则层<br/>- IGoodsComparator定契约<br/>- 各类实现具体排序规则<br/>- 无状态可复用] --> C B --> E[页面:调用层<br/>- 仅依赖工厂/枚举<br/>- 无需关注规则实现<br/>- 一键切换排序规则]

6.2 工程化最佳实践

  1. 策略模式应用原则
    • 当同一类业务有多种实现方案时,优先用接口定义契约,避免if-else分支判断;
    • 新增策略只需实现接口,无需修改原有代码,符合开闭原则;
    • 策略类设计为无状态,便于缓存复用(如比较器类无属性,仅含compare方法)。
  2. 工厂模式应用原则
    • 当实例化逻辑复杂或需要统一管理时,使用工厂类封装;
    • 通过枚举限定入参类型,避免字符串硬编码导致的错误;
    • 无状态策略类通过缓存复用,减少内存开销和实例化耗时。
  3. 与单例模式的配合
    • 单例负责数据的全局唯一性,策略和工厂负责业务逻辑的扩展,各司其职;
    • 排序操作基于单例数据,保证多页面/多组件排序后数据同步;
    • 单例类仅存储核心数据,页面临时状态(如当前排序值)保留在页面内部,避免内存泄漏。

七、内容总结

  1. 策略模式核心:以IGoodsComparator接口为契约,将排序规则封装为独立策略类(如PriceDescComparator),实现规则与页面解耦,新增规则仅需新增策略类,无需修改原有代码;
  2. 工厂模式核心:通过SortFactory统一管理比较器实例化逻辑,页面仅需传入SortType枚举即可调用,屏蔽底层实现细节;同时缓存无状态策略实例,提升性能;
  3. 架构整合价值:单例(数据统一)+策略(规则扩展)+工厂(实例管理)的整合,既保证商品数据全局唯一,又让排序体系具备“低耦合、高扩展”的工程化特性;
  4. 实战优化:补充商品列表空状态兜底、排序日志增强、策略实例缓存,提升代码健壮性和运行性能。

八、代码仓库

下节预告

本节课我们通过工厂+策略模式完成了商品排序体系的重构,也掌握了“类实现接口”的核心用法,但当业务场景升级,需要为商品实现多种促销规则(折扣、多档位满减、赠品、满折、会员专属优惠等)时,单纯依赖“一个类实现多个接口”的基础用法,会暴露出致命问题:

  1. 商品类职责爆炸:商品类会充斥大量促销计算逻辑,违背“单一职责原则”,代码臃肿且难以维护;
  2. 规则叠加混乱:多种促销规则的叠加顺序(如先满减后折扣/先折扣后满减)、优先级(如400-40优先于200-20)无法统一管控,易出现计算错误;
  3. 扩展成本指数级增加:新增第三种促销规则时,需让商品类实现新接口,所有调用处都要同步调整,完全违背“开闭原则”;
  4. 代码复用性差:不同商品的同类促销规则(如数码/图书都支持满200减20),无法复用同一套实现逻辑,重复代码大量滋生。

下一节,我们将跳出“单纯用类实现接口”的思维局限,通过进阶版策略模式破解多促销规则的扩展难题

posted @ 2026-01-25 11:04  鸿蒙-散修  阅读(0)  评论(0)    收藏  举报