在HarmonyOs应用开发中数据存储管理是一个核心模块。日记项目的DiaryStore.ets采用单例模式Singleton Pattern来管理数据存储实例确保全局只有一个数据存储管理器。本篇将深入解析单例模式的实现和应用。单例模式基础为什么需要单例如果没有单例模式 页面 A → new DiaryStore() → 实例1Preferences 已初始化 页面 B → new DiaryStore() → 实例2Preferences 未初始化 页面 C → new DiaryStore() → 实例3Preferences 未初始化 问题实例2和3无法使用因为 init() 只在 EntryAbility 中调用了实例1使用单例模式 EntryAbility → DiaryStore.getInstance() → 实例1唯一实例 ↓ init(context) 页面 A → DiaryStore.getInstance() → 实例1已初始化 ✅ 页面 B → DiaryStore.getInstance() → 实例1已初始化 ✅ 页面 C → DiaryStore.getInstance() → 实例1已初始化 ✅ 所有页面共享同一个已初始化的实例DiaryStore 单例实现完整代码exportclassDiaryStore{privatestore:preferences.Preferences|nullnull;privatestaticinstance:DiaryStore;// 1. 私有构造函数privateconstructor(){}// 2. 静态获取实例方法staticgetInstance():DiaryStore{if(!DiaryStore.instance){DiaryStore.instancenewDiaryStore();}returnDiaryStore.instance;}// 3. 初始化方法asyncinit(context:Context):Promisevoid{try{this.storeawaitpreferences.getPreferences(context,{name:diary_store});}catch(e){console.error(DiaryStore init error: JSON.stringify(e));}}// ... 其他方法}// 导出单例实例exportconstdiaryStoreDiaryStore.getInstance();单例三要素1. 私有构造函数privateconstructor(){}作用阻止外部通过new DiaryStore()创建实例。// ❌ 编译错误构造函数是私有的conststorenewDiaryStore();// ✅ 只能通过 getInstance 获取conststoreDiaryStore.getInstance();2. 静态实例变量privatestaticinstance:DiaryStore;作用在类级别保存唯一实例。private外部不能直接访问static属于类而非实例所有调用共享同一个变量3. 静态获取方法staticgetInstance():DiaryStore{if(!DiaryStore.instance){DiaryStore.instancenewDiaryStore();}returnDiaryStore.instance;}作用提供全局访问点惰性创建实例。惰性初始化实例在第一次调用getInstance()时才创建而非类加载时创建。导出单例exportconstdiaryStoreDiaryStore.getInstance();在模块加载时就创建单例实例并导出其他文件直接导入使用// EntryAbility.etsimport{diaryStore}from../utils/DiaryStore;awaitdiaryStore.init(this.context);// Index.etsimport{diaryStore}from../utils/DiaryStore;this.diariesawaitdiaryStore.getAllDiaries();// DiaryEdit.etsimport{diaryStore}from../utils/DiaryStore;constsuccessawaitdiaryStore.saveDiary(diary);// DiaryDetail.etsimport{diaryStore}from../utils/DiaryStore;constsuccessawaitdiaryStore.deleteDiary(this.diaryId);初始化流程应用启动 │ ▼ EntryAbility.onCreate() │ │ import { diaryStore } from ../utils/DiaryStore │ → 模块加载 → DiaryStore.getInstance() → 创建实例 │ │ await diaryStore.init(this.context) │ → preferences.getPreferences(context, { name: diary_store }) │ → this.store 赋值 │ ▼ EntryAbility.onWindowStageCreate() │ │ windowStage.loadContent(pages/Index) │ ▼ Index.aboutToAppear() │ │ diaryStore.getAllDiaries() │ → this.store 已就绪 ✅ │ → 返回日记列表 │ ▼ 页面渲染完成CRUD 方法实现Create / Update — saveDiaryasyncsaveDiary(diary:DiaryItem):Promiseboolean{if(!this.store){returnfalse;}try{constlistawaitthis.getAllDiaries();constnowDate.now();letfoundfalse;// 查找是否已存在更新for(leti0;ilist.length;i){if(list[i].iddiary.id){diary.updateTimenow;list[i]diary;foundtrue;break;}}// 不存在则新增if(!found){diary.createTimenow;diary.updateTimenow;if(!diary.id){diary.iddiary_now_Math.floor(Math.random()*1000);}list.push(diary);}awaitthis.store.put(STORE_KEY,JSON.stringify(list));awaitthis.store.flush();returntrue;}catch(e){console.error(saveDiary error: JSON.stringify(e));returnfalse;}}ID 生成策略diary.iddiary_now_Math.floor(Math.random()*1000);// 示例: diary_1721856000000_42diary_前缀可读性标识now时间戳保证时序唯一性Math.floor(Math.random() * 1000)随机数避免同一毫秒的冲突Read — getAllDiariesasyncgetAllDiaries():PromiseDiaryItem[]{if(!this.store){return[];}try{constrawawaitthis.store.get(STORE_KEY,[])asstring;constlist:ArrayDiaryItemJSON.parse(raw);list.sort((a:DiaryItem,b:DiaryItem)b.updateTime-a.updateTime);returnlist;}catch(e){console.error(getAllDiaries error: JSON.stringify(e));return[];}}排序策略按updateTime降序排列最近更新的日记排在最前面。Read — getDiaryByIdasyncgetDiaryById(id:string):PromiseDiaryItem|null{constlistawaitthis.getAllDiaries();for(letitemoflist){if(item.idid){returnitem;}}returnnull;}Delete — deleteDiaryasyncdeleteDiary(id:string):Promiseboolean{if(!this.store){returnfalse;}try{constlistawaitthis.getAllDiaries();constnewListlist.filter((item:DiaryItem)item.id!id);awaitthis.store.put(STORE_KEY,JSON.stringify(newList));awaitthis.store.flush();returntrue;}catch(e){console.error(deleteDiary error: JSON.stringify(e));returnfalse;}}附加方法 — getTodayDiaryasyncgetTodayDiary():PromiseDiaryItem|null{consttodaythis.formatDate(newDate());constlistawaitthis.getAllDiaries();for(letitemoflist){if(item.datetoday){returnitem;}}returnnull;}防御性编程空值检查asyncgetAllDiaries():PromiseDiaryItem[]{if(!this.store){// ← 检查 store 是否已初始化return[];}// ...}asyncsaveDiary(diary:DiaryItem):Promiseboolean{if(!this.store){// ← 检查 store 是否已初始化returnfalse;}// ...}try-catch 错误处理asyncgetAllDiaries():PromiseDiaryItem[]{if(!this.store){return[];}try{// 正常逻辑constrawawaitthis.store.get(STORE_KEY,[])asstring;constlist:ArrayDiaryItemJSON.parse(raw);list.sort((a,b)b.updateTime-a.updateTime);returnlist;}catch(e){console.error(getAllDiaries error: JSON.stringify(e));return[];// 安全降级}}每个方法都遵循相同的模式检查 store 是否就绪try-catch 包裹业务逻辑错误时安全降级并记录日志单例的生命周期应用启动 │ │ 模块加载 │ → export const diaryStore DiaryStore.getInstance() │ → instance new DiaryStore() │ → instance.store null ← 未初始化 │ │ EntryAbility.onCreate │ → diaryStore.init(context) │ → instance.store Preferences ← 已初始化 │ ├── 页面 A: diaryStore.getAllDiaries() → store 已就绪 ✅ ├── 页面 B: diaryStore.saveDiary() → store 已就绪 ✅ ├── 页面 C: diaryStore.deleteDiary() → store 已就绪 ✅ │ 应用退出 │ │ instance 随进程销毁日期格式化工具DiaryStore 还提供了日期格式化工具方法formatDate(date:Date):string{constyeardate.getFullYear();constmonth(date.getMonth()1).toString().padStart(2,0);constdaydate.getDate().toString().padStart(2,0);returnyear-month-day;}formatDateTime(timestamp:number):string{constdatenewDate(timestamp);constyeardate.getFullYear();constmonth(date.getMonth()1).toString().padStart(2,0);constdaydate.getDate().toString().padStart(2,0);consthourdate.getHours().toString().padStart(2,0);constminutedate.getMinutes().toString().padStart(2,0);returnyear-month-day hour:minute;}使用场景// 在 Index 页面显示更新时间Text(diaryStore.formatDateTime(item.updateTime)).fontSize(11).fontColor(#CCC)// 在 DiaryEdit 页面设置日期this.datediaryStore.formatDate(newDate());// 在 DiaryDetail 页面显示创建/更新时间Text(创建时间: diaryStore.formatDateTime(this.diary!.createTime))Text(更新时间: diaryStore.formatDateTime(this.diary!.updateTime))小结DiaryStore通过单例模式实现了全局唯一实例私有构造函数 静态 getInstance 方法一次初始化在 EntryAbility 中初始化所有页面共享完整 CRUDsave、getAll、getById、delete、getToday防御性编程空值检查 try-catch 安全降级工具方法日期格式化供 UI 直接调用单例模式是数据存储管理的经典选择确保了数据一致性和资源高效利用。