下面这篇是面向已有一定编程功底、正在上手 仓颉(Cangjie) 的技术文章。我会先把“类与对象”的语义放到仓颉的语言设计里解读,再给出一段可运行的实战示例,并结合值/引用语义、继承与接口实现等关键点做深入分析。💪

目录

仓颉中的“类与对象”:从语义到实践

实战:订单与折扣策略(类/对象 + 单继承 + 接口 + 值/引用语义)

1)定义接口与基础类型

2)用 class 表达订单模型与可共享状态

3)策略实现:基于阈值的满减、基于百分比的折扣

4)进阶:子类化订单,叠加业务约束

5)使用示例与“值/引用”思考

小结


仓颉中的“类与对象”:从语义到实践

在仓颉中,class 是典型的面向对象载体,用于抽象有状态、有行为的“引用类型”;与之并列的 struct 则是“值类型”,两者在赋值/传参与内存语义上截然不同:class 复制引用,struct 复制数据本体。这意味着 class 的同一对象可被多个变量共享,而 struct 的拷贝彼此独立,修改一份不会影响另一份。官方文档明确指出:class 是引用类型、struct 是值类型;class 支持继承、struct 不支持。(docs.cangjie-lang.cn)

仓颉的类定义以 class 开头,类体可包含成员变量、属性、静态初始化器、构造函数、成员函数与操作符函数等;class 只能定义在源文件顶层。
继承方面:仅支持单继承;想被继承的类或成员需要 open 修饰;未显式指定父类时,默认父类为 Object。实现接口使用 <: 语法,接口成员默认具备 open 语义。

值得一提的是,仓颉在运行时层面强调高性能与并发 GC(“终端场景首款全并发 GC”),这让以 class 为核心的 OOP 既保留表达力,又兼顾端侧性能与延迟敏感场景。


实战:订单与折扣策略(类/对象 + 单继承 + 接口 + 值/引用语义)

目标:实现一个可扩展的计价引擎。我们用 class 表达可共享、可变更状态的业务对象(订单、优惠策略),用 interface 抽象折扣策略协议;再用 struct 封装只读值(如价格区间)以获得复制语义与线程安全友好性。

1)定义接口与基础类型

// 折扣策略接口:任何实现者都要给出一个计算折扣额的方法
public interface DiscountPolicy {
  // 返回应扣减的金额(正数)
  public func discount(amount: Int64): Int64
}

接口定义与实现方式符合官方文档描述:接口使用 interface,实现使用 ClassName <: InterfaceName 语法;接口成员默认是 open 语义。

// 价格区间:用 struct(值类型)表达不可变的值对象
public struct PriceRange {
  let min: Int64
  let max: Int64
  public init(min: Int64, max: Int64) {
    this.min = min
    this.max = max
  }
  public func contains(v: Int64): Bool { v >= min && v <= max }
}

此处用 struct 强化“值拷贝”直觉:作为函数参数或返回值时不意外共享内部状态。(docs.cangjie-lang.cn)

2)用 class 表达订单模型与可共享状态

// 可被继承的订单基类:open 允许扩展
public open class Order {
  // 成员变量(示例用 let 表示初始化后不再变更)
  let id: String
  var amount: Int64

  public init(id: String, amount: Int64) {
    this.id = id
    this.amount = amount
  }

  // open 方法允许子类重写
  public open func finalAmount(policy: DiscountPolicy): Int64 {
    let off = policy.discount(this.amount)
    let result = this.amount - off
    result >= 0 ? result : 0
  }
}
  • 使用 open 以允许子类继承与重写,符合“类/成员需显式 open 才可继承/重写”的规则。

  • Order 作为 class,属于引用类型:多个变量引用同一笔订单实例时,共享其状态。

3)策略实现:基于阈值的满减、基于百分比的折扣

// 满减策略:满 threshold 减 reduce
public class ThresholdDiscount <: DiscountPolicy {
  let threshold: Int64
  let reduce: Int64
  public init(threshold: Int64, reduce: Int64) {
    this.threshold = threshold
    this.reduce = reduce
  }
  public func discount(amount: Int64): Int64 {
    amount >= threshold ? reduce : 0
  }
}

// 百分比折扣策略:例如 10% off,向下取整
public class PercentageDiscount <: DiscountPolicy {
  let rate: Int64  // 例如 10 表示 10%
  public init(rate: Int64) { this.rate = rate }
  public func discount(amount: Int64): Int64 { (amount * rate) / 100 }
}

4)进阶:子类化订单,叠加业务约束

// VIP 订单子类:在父类结算基础上再叠加会员折扣上限
public class VipOrder <: Order {
  let cap: Int64
  public init(id: String, amount: Int64, cap: Int64) {
    super(id, amount)
    this.cap = cap
  }

  // 重写结算逻辑:对折扣额设置上限
  public override func finalAmount(policy: DiscountPolicy): Int64 {
    let off = policy.discount(this.amount)
    let boundedOff = off <= cap ? off : cap
    let result = this.amount - boundedOff
    result >= 0 ? result : 0
  }
}

说明:仓颉支持单继承;子类可通过 super(...) 调父类构造,并重写 open 成员。这样既保留面向对象扩展性,又避免多继承复杂度。

5)使用示例与“值/引用”思考

main () {
  let p10 = PercentageDiscount(10)          // 10% off
  let full = ThresholdDiscount(10000, 800)  // 满 100 元减 8 元(单位分)

  let o1 = Order("A001", 12800)             // 128 元
  println(o1.finalAmount(p10))              // => 11520
  println(o1.finalAmount(full))             // => 12000

  // 同一对象的“共享引用”示例
  let o2 = o1
  o2.amount = 20000
  // o1 与 o2 指向同一订单实例,因此此处 o1.amount 也变为 200 元
  println(o1.finalAmount(p10))              // => 18000

  // 子类行为:上限 1000 分(10 元)
  let v1 = VipOrder("V100", 30000, 1000)
  println(v1.finalAmount(p10))              // 原始折扣 3000 分,被 cap 限制为 1000 分 => 29000
}

专业要点复盘

  1. class/struct 取舍

    • 共享状态/需要继承:选 class(引用类型)。

    • 不可变值对象/跨线程传递/避免共享副作用:选 struct(值类型)。(docs.cangjie-lang.cn)

  2. 继承与接口的协作

    • 行为扩展优先选 接口(interface)+ 组合;只有当需要复用状态与默认实现时再用继承,且保持单继承的层次扁平。(docs.cangjie-lang.cn)

  3. open 的显式性

    • 需要被继承/重写的类与成员都应加 open;默认不开放,有助于稳定 API、减少二义性。(docs.cangjie-lang.cn)

  4. 性能与运行时

    • class 属引用类型,会引入堆分配与 GC;仓颉提供并发 GC与轻量线程以降低停顿与调度成本,但在热点路径上仍建议用 struct 承载频繁复制的小对象,减少共享。(仓颉编程语言)

  5. 静态成员与静态初始化器

    • 可用 static let/var 声明类型级成员,并通过 static init(){...} 统一初始化一次(最多一个静态初始化器)。用于注册策略表、读取配置等场景尤为便利。


小结

  • 在仓颉里,“类与对象”的基本盘与现代 OOP 一致,但通过显式 open、单继承 + 接口、多场景运行时优化,将工程可维护性与端侧性能兼顾。

  • 结合上面的实战模板,你可以快速扩展更多策略(如阶梯折扣、品类券等),并用 struct 承载不可变规则数据,以值语义把并发与正确性问题挡在门外。🚀

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐