在 HarmonyOS Next 开发中,类的访问修饰符是实现数据封装与模块隔离的核心机制。通过 privateinternalprotectedpublic 修饰符,开发者能够精确控制类成员的可见范围,平衡代码的封装性与可扩展性。本文基于《仓颉编程语言开发指南》,解析访问修饰符的规则、应用场景及最佳实践。

一、访问修饰符的作用域规则

HarmonyOS Next 提供四种访问修饰符,控制成员在不同作用域的可见性:

| 修饰符 | 作用域描述 |
|--------------|--------------------------------------------------------------------------|
| private | 仅在定义该成员的类内部可见。 |
| internal | 在当前包及子包内可见(默认修饰符)。 |
| protected | 在当前类及其子类可见,且仅限同一模块内。 |
| public | 在所有模块中可见,但需遵循包的导出规则。 |

示例:包结构与访问权限

package device.core  
public open class Hardware {  
    private var serialNumber: String // 仅Hardware类内部可见  
    internal func reset() { ... }     // device.core包及子包可见  
    protected var firmwareVersion: String // 子类可见(需在同一模块)  
    public var model: String        // 全局可见  
}  

二、类成员的访问控制实践

1. private:严格封装内部实现

  • 适用场景:禁止外部访问的私有逻辑,如敏感数据、中间计算变量。
  • 示例

    class SecureSensor {  
        private var rawData: [UInt8] = [] // 私有成员:原始传感器数据  
        public func processData() {  
            rawData.decode() // 内部处理逻辑  
        }  
        // 外部无法访问rawData,只能通过公开方法操作  
    }

2. internal:包内可见的模块私有

  • 适用场景:模块内部协作的工具类或辅助函数,避免污染全局命名空间。
  • 示例

    package network.internal  
    internal class SocketManager {  
        func connect() { ... } // 仅在network.internal包及子包可见  
    }  

3. protected:子类可见的继承扩展

  • 适用场景:允许子类重写或访问的基类成员,但限制跨模块访问。
  • 示例

    open class BaseDevice {  
        protected var status: DeviceStatus // 子类可访问,但仅限同一模块  
        public func getStatus() -> DeviceStatus { status }  
    }  
    class WirelessDevice <: BaseDevice {  
        public func updateStatus() {  
            status = .Online // 子类可访问protected成员  
        }  
    }  

4. public:全局可见的公共接口

  • 适用场景:需要暴露给其他模块的核心接口或数据。
  • 注意事项public 类的成员默认 internal,需显式声明 public 以全局可见:

    public class PublicAPI {  
        public var publicField: Int // 必须显式声明public  
        var internalField: String // 默认为internal,跨模块不可见  
    }  

三、构造函数与访问控制

1. 构造函数的访问修饰

构造函数可通过修饰符控制实例化权限,实现设计模式:

  • 单例模式:私有构造函数禁止外部实例化

    class Singleton {  
        public static let instance = Singleton()  
        private init() { /* 初始化逻辑 */ } // 私有构造函数  
    }  
  • 工厂模式:包内可见的构造函数配合静态工厂方法

    package database  
    internal class DatabaseConnection {  
        internal init(url: String) { ... }  
        public static func create(url: String) -> DatabaseConnection {  
            DatabaseConnection(url: url) // 包内创建实例  
        }  
    }  

2. 继承中的构造函数可见性

子类构造函数的访问修饰符需与父类匹配或更宽松:

open class Parent {  
    protected init() { /* 受保护构造函数 */ }  
}  
class Child <: Parent {  
    // 子类构造函数默认internal,可访问父类protected构造函数  
    public init() { super.init() }  
}  

四、跨模块访问与包管理

1. 包导出规则

  • 模块中的 public 类需在包声明中导出,否则跨模块不可见:

    // package.json  
    {  
      "name": "my.module",  
      "exports": {  
        ".": ["public-classes"]  
      }  
    }  

2. 访问修饰符与接口实现

接口成员默认 public,实现类需使用相同或更宽松的修饰符:

interface PublicInterface {  
    func publicMethod()  
}  
class Implementation <: PublicInterface {  
    // 必须声明public,否则默认internal不满足接口要求  
    public func publicMethod() { ... }  
}  

五、常见陷阱与最佳实践

1. 避免过度使用public

  • 反例:暴露过多实现细节,破坏封装性

    public class BadDesign {  
        public var internalLogic: Int // 外部可直接修改,违反封装原则  
    }  
  • 正例:通过公共方法间接访问私有成员

    public class GoodDesign {  
        private var _value: Int = 0  
        public func getValue() -> Int { _value }  
        public func setValue(newValue: Int) { _value = newValue }  
    }  

2. protected的模块限制

  • protected 成员在跨模块子类中不可见,需使用 public 或通过接口暴露:

    // 模块A  
    open class Base {  
        protected func protectedFunc() { ... } // 模块A内子类可见  
    }  
    // 模块B  
    class Child <: Base {  
        func useProtected() {  
            protectedFunc() // 编译错误:跨模块不可访问protected成员  
        }  
    }  

3. 接口与访问修饰符的协同

接口成员的访问级别决定实现类的最低要求:

interface RestrictedInterface {  
    internal func restrictedMethod() // 接口成员为internal  
}  
class Implementation <: RestrictedInterface {  
    // 实现方法至少为internal(默认),不可声明为private  
    func restrictedMethod() { ... }  
}  

六、总结:访问修饰符的设计原则

HarmonyOS Next 的访问修饰符体系遵循以下核心原则:

  1. 最小特权原则:默认 internal,仅暴露必要的公共接口;
  2. 封装优先:使用 private 隐藏实现细节,通过公共方法提供访问入口;
  3. 模块隔离:利用 internalprotected 控制包内与继承层次的可见性。

SameX
6 声望2 粉丝