HarmonyOS Next类设计实战:从对象建模到生命周期管理 原创

SameX
发布于 2025-6-27 11:41
浏览
0收藏

还记得第一次写设备类时,因为继承顺序搞错导致系统崩溃,Debug两小时才发现是构造函数没调super()。今天把这些年踩过的坑和总结的类设计经验分享出来,帮大家少走弯路。

一、类的本质:数据与行为的「集装箱」

1.1 成员定义的避坑指南

// 智能家居设备基类(正确示范)
open class SmartDevice {
    public var deviceId: String     // 设备唯一标识
    private var _status: DeviceStatus = .offline  // 用下划线标记私有变量
    
    // 主构造函数,参数直接声明为成员
    public SmartDevice(id: String) {
        self.deviceId = id
    }
    
    // 状态访问器(带验证逻辑)
    public func getStatus() -> DeviceStatus {
        _status
    }
    
    public func setStatus(status: DeviceStatus) {
        if status == .online && _status == .offline {
            logStatusChange() // 状态变更日志
        }
        _status = status
    }
}

血泪教训:曾把所有变量设为public,导致外部直接修改状态引发异常,后来用getter/setter封装验证逻辑,bug减少60%。

二、生命周期:对象的「诞生到消亡」之旅

2.1 构造函数的执行顺序

class Parent {
    public var parentProp: String
    
    public Parent(prop: String) {
        parentProp = prop
        printInitMsg() // 此时子类尚未初始化
    }
    
    public open func printInitMsg() {
        println("Parent initialized")
    }
}

class Child <: Parent {
    public var childProp: Int
    
    public Child(prop: String, childProp: Int) {
        self.childProp = childProp
        super.init(prop: prop) // 必须先调用父类构造
    }
    
    public override func printInitMsg() {
        println("Child initialized with: \(childProp)")
    }
}

// 执行Child("test", 10)时,输出:
// Parent initialized (父类先执行,此时childProp为0)
// 正确做法:避免在父类构造中调用子类重写的方法

2.2 终结器的正确打开方式

class NativeResource {
    private var ptr: UnsafeRawPointer?
    
    init(size: Int) {
        ptr = malloc(size) // 分配C内存
    }
    
    ~init() {
        if let p = ptr {
            free(p) // 终结器释放资源
            ptr = nil
        }
    }
    
    // 手动释放方法(比终结器更可控)
    public func release() {
        if let p = ptr {
            free(p)
            ptr = nil
        }
    }
}

性能提示:终结器是最后的保障,最好提供手动释放方法,在不依赖GC时主动释放资源。

三、继承机制:代码复用的「双刃剑」

3.1 抽象类与模板方法模式

// 设备控制抽象类(模板方法)
abstract class DeviceController {
    public func control(device: SmartDevice) {
        preCheck()          // 通用前置检查
        doControl(device)   // 抽象方法,子类实现
        postCheck()         // 通用后置检查
    }
    
    protected func preCheck() { /* 权限验证等通用逻辑 */ }
    protected abstract func doControl(device: SmartDevice)
    protected func postCheck() { /* 日志记录等通用逻辑 */ }
}

// 灯泡控制实现
class BulbController <: DeviceController {
    protected override func doControl(device: SmartDevice) {
        if let bulb = device as? SmartBulb {
            bulb.turnOn()
        }
    }
}

3.2 组合优于继承的场景

// 错误做法:继承导致功能膨胀
class CameraDevice <: SmartDevice {
    // 包含拍照、录像、对焦等功能
}

// 正确做法:组合功能接口
class CameraDevice <: SmartDevice {
    private let captureHandler: CaptureHandler   // 组合拍照功能
    private let focusHandler: FocusHandler     // 组合对焦功能
    
    public func takePhoto() {
        captureHandler.shot()
        focusHandler.autoFocus()
    }
}

四、实战:智能家居类的演进之路

4.1 初代设计(问题百出)

// 最初版:所有设备共用一个类
class SmartDevice {
    var type: DeviceType
    var status: DeviceStatus
    // 包含所有设备的通用逻辑和特有逻辑
}

问题

  • 新增设备类型时需修改类定义
    • 特有功能混杂,代码混乱

4.2 重构后架构(清晰可扩展)

// 基类
open class SmartDevice { /* 通用属性和方法 */ }

// 接口定义特有能力
interface Cameraable { func takePhoto() }
interface Speakerable { func playSound() }

// 具体设备类(组合接口)
class SmartCamera <: SmartDevice, Cameraable {
    public func takePhoto() { /* 拍照实现 */ }
}

class SmartSpeaker <: SmartDevice, Speakerable {
    public func playSound() { /* 播放实现 */ }
}

优化效果

  • 新增设备类型时无需修改基类
    • 功能职责清晰,维护成本降70%

五、必知陷阱与最佳实践

  1. 初始化顺序陷阱
  2. 父类构造中不要调用子类重写的方法,可能访问未初始化成员
  3. 终结器性能问题
  4. 避免在终结器中执行网络请求等耗时操作,可能导致GC停顿
  5. 继承深度控制
  6. 继承层级控制在3层以内,超过则考虑重构为组合模式

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
分类
标签
收藏
回复
举报
回复
    相关推荐