HarmonyOS 应用开发《掌上英语》第13篇:面向对象 vs 函数式:ArkTS 中的编程范式选择

面向对象 vs 函数式:ArkTS 中的编程范式选择

一、引言

ArkTS 是 HarmonyOS 原生开发语言,它在 TypeScript 基础上增强了静态类型检查和装饰器体系,同时保留了多范式的表达能力。在实际项目开发中,我们经常面临一个选择:用面向对象(OOP)还是函数式(FP)?

这个问题没有绝对答案------每种范式都有其擅长的领域。本文将以我们英语学习 App 的实际代码为例,分析 class + @ObservedV2 的面向对象模型、@Builder 参数化 UI 片段、以及函数式单例工具类(如 AudioPlayer)的设计思路,帮你理清在 ArkTS 中如何做出合理的范式选择。

二、面向对象范式:class + @ObservedV2 响应式模型

2.1 WordCard 数据模型

在 ArkTS V2 装饰器体系中,@ObservedV2 + @Trace 是实现响应式数据模型的标准方式。它的本质是面向对象的------通过 class 封装数据,用装饰器标记可观察属性:

typescript 复制代码
// commonLib/models/WordCardModel.ets
@ObservedV2
export class WordCard {
  @Trace id: number = 0;
  @Trace word: string = '';
  @Trace phonetic: string = '';
  @Trace partOfSpeech: string = '';
  @Trace translation: string = '';
  @Trace example: string = '';
  @Trace exampleTranslation: string = '';
  @Trace audioUrl: string = '';
  @Trace tags: string[] = [];
  @Trace category: string = 'basic';

  constructor(id: number, word: string, phonetic: string, partOfSpeech: string,
    translation: string, example: string, exampleTranslation: string,
    audioUrl: string = '', tags: string[] = [], category: string = 'basic') {
    this.id = id;
    this.word = word;
    // ... 其余字段赋值
  }
}

为什么选 class 而非 plain object?

在 ArkTS 中,普通的对象字面量 { word: 'apple', ... } 无法被 @Trace 追踪。只有通过 @ObservedV2 装饰的 class 实例,其 @Trace 属性变化才能触发 UI 刷新。这是面向对象范式在 ArkTS 响应式体系中的核心优势------class 不是可选的,而是响应式的基础

2.2 @ObservedV2 的工作原理

当你在 @ComponentV2 装饰的组件中读取 wordCard.word 时,ArkTS 框架会自动建立"属性→组件"的依赖关系。一旦 wordCard.word 被修改,只有读取了这个特定字段的组件会重新渲染------其他未依赖的组件不受影响。

typescript 复制代码
@ComponentV2
struct WordDisplay {
  @Param wordCard: WordCard = new WordCard();

  build() {
    Text(this.wordCard.word)  // 只依赖 word 字段
      .fontSize(34);
    Text(this.wordCard.phonetic)  // 只依赖 phonetic 字段
      .fontSize(15);
  }
}

这种字段级粒度的响应式追踪是面向对象方案独有的能力。如果改用函数式不可变数据,每次修改都需要创建新对象并整体重新赋值,性能开销大且代码更繁琐。

2.3 CourseCoreBar:封装业务逻辑的类

除了数据模型,面向对象也适合封装带有行为的状态:

typescript 复制代码
// MainPage.ets
@ObservedV2
export class CourseCoreBar {
  @Trace showIcon: ResourceStr = '';
  @Trace description: string = '';

  constructor(showIcon: ResourceStr, description: string) {
    this.showIcon = showIcon;
    this.description = description;
  }
}

这类"带状态的业务对象"天然适合 class------它同时承载数据和 UI 依赖的响应式属性,无需在组件内部维护额外的状态管理逻辑。

三、函数式范式:@Builder 与工具类

3.1 @Builder:参数化 UI 片段

ArkTS 的 @Builder 是一个纯粹的 UI 函数------它接收参数,返回 UI 描述,没有内部状态。这正是函数式编程中"纯函数"的体现:

typescript 复制代码
// CourseHomePage.ets
@Builder
tabBuilder(label: string, key: string) {
  Text(label)
    .fontSize(14)
    .fontColor(this.currentCategory === key ? '#165DFF' : $r('sys.color.font_secondary'))
    .fontWeight(this.currentCategory === key ? FontWeight.Bold : FontWeight.Normal)
    .padding({ left: 12, right: 12, top: 6, bottom: 6 });
}

// 在 build() 中调用
TabContent() { this.cardArea(); }
  .tabBar(this.tabBuilder('全部', 'all'));

@Builder 的优势在于:

  • 无副作用:输入相同参数,输出相同 UI
  • 可组合 :多个 @Builder 可以嵌套组合
  • 按需复用:同一段 UI 逻辑在不同位置调用,只需传不同参数

对于不需要内部状态的 UI 片段,用 @Builder 而非创建一个新组件,可以减少组件树的层次,提升渲染性能。

3.2 Builder 导出模式

项目采用统一的 Builder 导出模式,将每个页面的构建函数暴露给路由系统:

typescript 复制代码
// CourseHomePage.ets 末尾
@Builder
export function CourseHomePageBuilder() {
  CourseHomePage();
}

这是一种"工厂函数"风格------CourseHomePageBuilder 是一个纯函数,每次调用都创建新的组件实例。路由系统通过 RouterTable 统一管理这些 Builder:

typescript 复制代码
// RouterTable.ets
RouterTable.builderMap.set(RouterMap.WORD_CARD_PAGE, wrapBuilder(WordCardPageBuilder));

这种模式将"创建组件"的职责从组件内部剥离出来,实现了更干净的关注点分离。

3.3 AudioPlayer:单例模式中的函数式设计

AudioPlayer 虽然使用了 class 语法,但它的设计理念更接近函数式------全局唯一实例,无 UI 依赖

typescript 复制代码
// commonLib/utils/AudioPlayer.ets
export class AudioPlayer {
  private static instance?: AudioPlayer;
  private avPlayer: media.AVPlayer | null = null;
  private currentUrl: string = '';

  private constructor() { }

  public static getInstance(): AudioPlayer {
    if (!AudioPlayer.instance) {
      AudioPlayer.instance = new AudioPlayer();
      AudioPlayer.instance.initPlayer();
    }
    return AudioPlayer.instance;
  }

  public play(url: string): void {
    if (!url || url === '') return;
    if (!this.avPlayer) return;
    // ... 播放逻辑
  }

  public stop(): void { /* ... */ }
  public release(): void { /* ... */ }
}

工具类使用单例 + 方法的模式有几个优点:

  • 无状态副作用 :每次调用 play() 只依赖传入的 URL 参数
  • 全局复用 :任何一个页面都可以通过 AudioPlayer.getInstance().play(url) 播放音频
  • 无 UI 耦合:可以在非 UI 线程中使用,不受组件生命周期影响

类似的还有 NewWordManagerGlobalContextUtils 等工具类,都采用同样的单例 + 方法设计。

四、范式对比:何时用哪种?

维度 面向对象 (class) 函数式 (@Builder / 单例)
状态管理 @Trace 字段级追踪,适合 UI 数据 无状态或全局单例,适合工具能力
复用方式 继承、组合、依赖注入 参数化、高阶函数
响应式 原生支持,属性变化自动刷新 UI 不涉及响应式,适合非 UI 逻辑
测试难度 需要 mock 状态和装饰器环境 纯函数测试简单
典型场景 数据模型、ViewModel、业务实体 UI 片段复用、工具类、路由工厂

选择指南

  1. 数据模型 → 用 class + @ObservedV2。需要响应式追踪的属性加上 @Trace,确保 UI 自动刷新。

  2. UI 复用片段 → 用 @Builder。当一段 UI 只是"带参数的模板"且不需要独立生命周期时,@Builder 比抽取成独立组件更轻量。

  3. 工具类/服务类 → 用单例 + 方法。如音频播放、网络请求、数据持久化,这些不依赖 UI 状态的功能适合函数式风格。

  4. 组件创建工厂 → 用导出函数(Builder 模式)。将组件的创建与路由解耦,方便路由表统一管理。

五、混合范式的最佳实践

实际项目中,两种范式不是互斥的,而是互补的。以下是我们项目中的混合使用模式:

typescript 复制代码
// 1. 面向对象:ViewModel 管理状态
@ObservedV2
class TopicVM {
  @Trace currentIndex: number = 0;
  // ... 响应式属性
}

// 2. 函数式:Builder 创建组件
@Builder
export function TopicPageBuilder() {
  TopicPage();
}

// 3. 工具类单例:全局服务
const audioPlayer = AudioPlayer.getInstance();
audioPlayer.play('hello.mp3');

// 4. @Builder 复用 UI 片段(在组件内部)
@Builder
commonHeader(title: string) {
  Text(title).fontSize(18).fontWeight(FontWeight.Bold);
}

核心原则:有状态的用 class,无状态的用函数。ArkTS V2 的装饰器体系让面向对象在 UI 层如鱼得水,而函数式风格在工具层保持简洁和可测试性。理解两种范式的优劣,在合适的场景选择合适的方式,比固执于一种"纯"范式要实用得多。

六、总结

在 ArkTS 中,面向对象和函数式编程并非"二选一"的对立关系,而是一枚硬币的两面:

  • 面向对象 (class + @ObservedV2/@Trace)是 ArkTS 响应式 UI 的基础,适合数据模型、ViewModel 和业务实体
  • 函数式@Builder、单例工具类)在 UI 片段复用、工具服务和无状态逻辑中更简洁
  • 混合使用是最佳实践:数据层用 OOP 保证响应式,工具层用 FP 保证可测试性

理解每种范式的设计意图和适用场景,才能写出既符合 ArkTS 平台特性又易于维护的代码。

相关推荐
春卷同学37 分钟前
HarmonyOS掌上记账APP开发实践第23篇:弹性动效与手势 — EdgeEffect.Spring 与手势系统实战
spring·华为·harmonyos
l134062082351 小时前
鸿蒙应用开发实战【88】— DevEco Studio高效开发技巧
华为·harmonyos·鸿蒙系统
一缕清烟在人间2 小时前
鸿蒙应用开发实战【54】— 应用详情页AppDetailPage开发(下)
华为·harmonyos·鸿蒙系统
l134062082352 小时前
鸿蒙应用开发实战【85】— 应用签名与正式HAP打包
华为·harmonyos·鸿蒙系统
爸爸6193 小时前
鸿蒙应用开发实战【06】— 应用入口与生命周期管理(EntryAbility 深度解析)
华为·harmonyos·鸿蒙系统
不羁的木木3 小时前
《HarmonyOS技术精讲-NearLink Kit(星闪服务)》第8篇:智能家居控制——手机控制智能灯实战
智能手机·智能家居·harmonyos
用户0934077735144 小时前
HarmonyOS WPS Open SDK 实践:用能力分层重构 OpenFileRequest
typescript·harmonyos
春卷同学4 小时前
HarmonyOS掌上记账APP开发实践第21篇:Tabs 底部导航 — 鸿蒙应用的标准导航范式
华为·harmonyos
子非鱼94274 小时前
03-Flutter 鸿蒙实战 03:登录注册与本地会话持久化
网络·flutter·华为·harmonyos
子非鱼94275 小时前
01-Flutter 鸿蒙实战 01:会说话的相册项目架构拆解
flutter·华为·harmonyos