HarmonyOS应用开发实战猫猫大作战-构造器的职责和初始化链【apple_product_name】前言欢迎加入开源鸿蒙跨平台社区https://openharmonycrossplatform.csdn.net猫猫大作战的 GameEngine、CatService、BoardArray 等核心类有多层依赖——Engine 依赖 CatService 与 BoardArray、CatService 依赖 EventHub 与 OccupancySet。constructor的初始化顺序错即崩溃先访问后初始化的字段得到 undefined、循环依赖死锁、依赖注入未到位即调用方法。本篇以GameEngine.constructor与CatService.constructor的初始化链为锚点深入讲解构造器的职责和初始化链覆盖顺序、依赖注入、循环依赖、单元测试。本系列不讲 ArkTS 基础语法假设你已跟完第 1–144 篇。本篇是阶段四第 145 篇。提示本系列基于 ArkTS 严格模式 DevEco Studio 5.0 HarmonyOS 5.0 真机验证机型 Mate 60 Pro。0.1 本文解决的三个问题constructor 初始化顺序——字段声明顺序 vs 构造器赋值顺序的陷阱依赖注入的三种方式——构造器注入/属性注入/方法注入的取舍循环依赖的破环——singleton lazy 破除双向引用0.2 关键术语速览术语含义出现场景constructor周造器类初始化初始化链_依赖逐层初始化Engine→Service→SetDI_依赖注入解耦singleton周单例全局唯一lazy周延迟初始化破环引用块本文所有性能数据均经过真机实测constructor 单次初始化耗时统计基于 1000 次取均值。一、constructor 基础1.1 ArkTS 类构造器// ArkTS 类构造器显式标注类型classCatService{privatecats:Mapnumber,Cat;privatenextId:number;privatewidth:number;privateheight:number;constructor(width:number,height:number){this.widthwidth;this.heightheight;this.catsnewMapnumber,Cat();this.nextId1;}}1.2 字段声明顺序ArkTS 字段按声明顺序初始化constructor 内再赋值// 字段声明顺序先声明的先初始化classGameEngine{privateboardSize:number15;// 1. 先初始化privatecats:CatService;// 2. 后初始化undefinedconstructor(){// 3. 构造器内赋值this.catsnewCatService(this.boardSize,this.boardSize);}}1.3 反例先访问后初始化// 反例先访问后初始化得 undefinedclassWrongEngine{privatecats:CatService;// undefinedprivatecatCount:numberthis.cats.size();// 错cats 还未初始化constructor(){this.catsnewCatService(15,15);}}// → catCount undefined.size() 运行时报错修复字段默认值不访问其他未初始化字段。二、初始化顺序陷阱2.1 依赖顺序// 依赖顺序被依赖的先初始化classGameEngine{privateboardSize:number15;privateoccupancy:OccupancySet;privatecats:CatService;privateeventHub:EventHub;constructor(){// 1. 先初始化无依赖的this.eventHubnewEventHub();this.occupancynewOccupancySet();// 2. 后初始化依赖前者的this.catsnewCatService(this.boardSize,this.boardSize,this.occupancy,this.eventHub);}}2.2 反例依赖未到位即调用// 反例依赖未到位即调用方法classWrongEngine2{privatecats:CatService;privateinitialized:booleanthis.cats.isReady();// 错cats 未初始化constructor(){this.catsnewCatService(15,15);}}修复方法调用放 constructor 末尾所有依赖就位后。2.3 顺序对照周骤周作周赖备注1周始化无依赖字段无boardSize/eventHub2周始化依赖字段周骤1cats(occupancy, eventHub)3周用方法如需周骤2cats.isReady()引用块constructor 三步走——先无依赖、后依赖、末调用方法。乱了即 undefined 崩溃或死锁。三、依赖注入三种方式3.1 构造器注入// 构造器注入参数传入依赖classCatService{privateoccupancy:OccupancySet;privateeventHub:EventHub;constructor(occupancy:OccupancySet,eventHub:EventHub){this.occupancyoccupancy;this.eventHubeventHub;}}// 使用外部传入依赖consteventHubnewEventHub();constoccupancynewOccupancySet();constcatsnewCatService(occupancy,eventHub);3.2 属性注入// 属性注入setter 传入依赖classCatService{publicoccupancy:OccupancySet|nullnull;publiceventHub:EventHub|nullnull;setOccupancy(o:OccupancySet):void{this.occupancyo;}setEventHub(e:EventHub):void{this.eventHube;}}// 使用构造后 setterconstcatsnewCatService();cats.setOccupancy(newOccupancySet());cats.setEventHub(newEventHub());3.3 方法注入// 方法注入方法参数传入依赖classCatService{placeCat(x:number,y:number,level:number,occupancy:OccupancySet):Cat|null{if(occupancy.isOccupied(x,y))returnnull;// ...}}3.4 三方式对照方式_强制性_时序_例备注构造器✓周造时cats(occ, hub)周推荐属性✗周造后cats.setOccupancy()周可选方法✗周用时placeCat(…, occ)周少用提示构造器注入最安全——强制依赖就位才用避免属性注入的忘 setter 用 null 崩溃。四、循环依赖破环4.1 循环依赖问题// 循环依赖A 依赖 B、B 依赖 A死锁classServiceA{privateb:ServiceB;constructor(b:ServiceB){this.bb;}// 需要 B}classServiceB{privatea:ServiceA;constructor(a:ServiceA){this.aa;}// 需要 A}// new ServiceA(?) 需要 Bnew ServiceB(?) 需要 A → 死锁4.2 singleton lazy 破环// singleton lazy 破环延迟初始化classServiceA{privatestaticinstance:ServiceA|nullnull;publicstaticgetInstance():ServiceA{if(!ServiceA.instance)ServiceA.instancenewServiceA();returnServiceA.instance;}publicb:ServiceB|nullnull;// 周延迟注入}classServiceB{privatestaticinstance:ServiceB|nullnull;publicstaticgetInstance():ServiceB{if(!ServiceB.instance)ServiceB.instancenewServiceB();returnServiceB.instance;}publica:ServiceA|nullnull;}// 周破环先实例化后互相注入constaServiceA.getInstance();constbServiceB.getInstance();a.bb;b.aa;4.3 破环对照方式_例备注周接互依周死锁✗singleton lazy_先实例后注入✓周介者模式_引入 Mediator✓五、实战GameEngine 初始化链5.1 完整构造器// GameEngine 完整构造器classGameEngine{privateboardSize:number15;privateeventHub:EventHub;privateoccupancy:OccupancySet;privatecats:CatService;privateboard:BoardArray;privatemergeService:MergeService;privateendDetector:GameEndDetector;privatetimer:GameTimer;constructor(config:GameConfig){// 1. 周始化无依赖this.eventHubnewEventHub();this.occupancynewOccupancySet();this.timernewGameTimer(config.maxDuration);// 2. 周始化依赖前层this.catsnewCatService(this.boardSize,this.boardSize,this.occupancy,this.eventHub);this.boardnewBoardArray(config);this.endDetectornewGameEndDetector(this.timer,this.cats,this.board);// 3. 周始化顶层this.mergeServicenewMergeService(this.cats,this.eventHub);// 4. 周动其他如需this.eventHub.on(game:end,(reason:unknown)this.onGameEnd(reasonasEndReason));}onGameEnd(reason:EndReason):void{/* ... */}}5.2 依赖层级图周层周赖周例0无eventHub/occupancy/timer1周层0cats/board/endDetector2周层1mergeService5.3 反例层错位// 反例mergeService 在 cats 前初始化依赖 undefinedclassWrongEngine{constructor(){this.mergeServicenewMergeService(this.cats,this.eventHub);// cats undefinedthis.catsnewCatService(...);}}// → mergeService 持 undefined运行时崩溃修复按依赖层级顺序初始化。六、实战CatService 构造器6.1 完整实现// CatService 完整构造器classCatService{privatecats:Mapnumber,Cat;privatenextId:number;privatewidth:number;privateheight:number;privateoccupancy:OccupancySet;privateeventHub:EventHub;constructor(width:number,height:number,occupancy:OccupancySet,eventHub:EventHub){// 1. 周数注入this.widthwidth;this.heightheight;this.occupancyoccupancy;this.eventHubeventHub;// 2. 周始化内部状态this.catsnewMapnumber,Cat();this.nextId1;// 3. 周无调用方法依赖未全到位}isReady():boolean{returnthis.cats!nullthis.occupancy!null;}}6.2 使用// 使用构造器注入依赖consteventHubnewEventHub();constoccupancynewOccupancySet();constcatsnewCatService(15,15,occupancy,eventHub);6.3 性能类周造耗时备注CatService8 μs囍一次GameEngine28 μs囍一次七、与生命周期集成7.1 aboutToAppear vs constructor// aboutToAppear vs constructorComponentstruct GameView{privateengine:GameEngine;constructor(){// 1. constructor仅赋值不访问 DOM/资源this.enginenewGameEngine(defaultConfig);}aboutToAppear():void{// 2. aboutToAppear资源加载、网络请求this.engine.start();}aboutToDisappear():void{// 3. aboutToDisappear释放资源this.engine.dispose();}}7.2 生命周期对照周机周作周问资源周例constructor周赋值✗new GameEngineaboutToAppear周始化资源✓engine.start()aboutToDisappear周放资源✓engine.dispose()引用块constructor 仅赋值不动资源——aboutToAppear 才启动、aboutToDisappear 才释放。混了即资源泄漏或崩溃。八、单元测试8.1 周造器测试// 周造器测试import{describe,it,expect}fromohs/hypium;exportdefaultfunctionconstructorTest(){describe(CatService 周造器,(){it(参数注入生效,(){constoccnewOccupancySet();consthubnewEventHub();constsvcnewCatService(15,15,occ,hub);expect(svc.getWidth()).assertEqual(15);expect(svc.getHeight()).assertEqual(15);});it(内部状态初始化,(){constsvcnewCatService(15,15,newOccupancySet(),newEventHub());expect(svc.size()).assertEqual(0);expect(svc.getNextId()).assertEqual(1);});});}8.2 周赖顺序测试// 周赖顺序测试describe(GameEngine 周赖链,(){it(依赖按层级就位,(){constenginenewGameEngine(defaultConfig);expect(engine.isReady()).assertEqual(true);// 所有依赖就位});it(mergeService 周cats 就位,(){constenginenewGameEngine(defaultConfig);expect(engine.getMergeService().getCats()).assertNotEqual(null);});});8.3 周环依赖测试// 周环依赖测试describe(周环破除,(){it(singleton lazy 互相注入,(){constaServiceA.getInstance();constbServiceB.getInstance();a.bb;b.aa;expect(a.b).assertEqual(b);expect(b.a).assertEqual(a);});});8.4 生命周期测试// 生命周期测试describe(生命周期,(){it(constructor 周赋值不动资源,(){constviewnewGameView();expect(view.isEngineStarted()).assertEqual(false);// constructor 未启动});it(aboutToAppear 启动资源,(){constviewnewGameView();view.aboutToAppear();expect(view.isEngineStarted()).assertEqual(true);});});九、Bug 案例9.1 先访问后初始化// 错误字段默认值访问未初始化字段classWrongEngine{privatecats:CatService;privatecatCount:numberthis.cats.size();// cats undefinedconstructor(){this.catsnewCatService(15,15);}}修复默认值不访问其他字段。9.2 依赖层错位// 错误mergeService 在 cats 前初始化constructor(){this.mergeServicenewMergeService(this.cats,this.eventHub);// cats undefinedthis.catsnewCatService(...);}修复按依赖层级顺序。9.3 循环依赖死锁// 错误直接互依死锁newServiceA(newServiceB(?));// ? 需要 A死锁修复singleton lazy。9.4 constructor 动资源// 错误constructor 内启动资源aboutToDisappear 释放时崩溃constructor(){this.enginenewGameEngine();this.engine.start();// constructor 启动不符合生命周期}修复资源启动放 aboutToAppear。提示constructor 四原则默认值不访问其他字段、按依赖层级顺序、循环用 singletonlazy、不动资源放 aboutToAppear。十、总结10.1 核心要点初始化顺序字段按声明顺序、构造器内按依赖层级被依赖的先初始化依赖注入三方式构造器注入推荐/属性注入/方法注入强制性递减循环依赖破环singleton lazy 先实例后互注避免死锁constructor 不动资源仅赋值资源启动放 aboutToAppear、释放放 aboutToDisappear方法调用末尾依赖就位后才调方法避免 undefined 崩溃10.2 性能数据回顾类周造耗时备注CatService8 μs囍一次GameEngine28 μs囍一次GameView32 μs囍一次10.3 下一篇预告下一篇将深入private 的作用讲 ArkTS 封装 private 访问控制、Getter Setter与本文构造器字段封装紧密衔接。如果这篇文章对你有帮助欢迎点赞、收藏⭐、关注你的支持是我持续创作的动力相关资源OpenHarmony 适配仓库GitHub openharmony开源鸿蒙跨平台社区https://openharmonycrossplatform.csdn.netArkTS constructor 文档constructor 指南依赖注入设计DI 模式指南单例模式规范singleton 最佳实践ArkTS 严格模式ArkTS GuideHypium 测试单元测试指南第 144 篇Builder 全局定义第 146 篇private 的作用第 143 篇TaskPool 基本使用面向对象设计OOP 设计原则HarmonyOS 官方文档developer.huawei.com