仓颉编程实战:类与对象的精髓
下面这篇是面向已有一定编程功底、正在上手 仓颉(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: Int64let max: Int64public init(min: Int64, max: Int64) {this.min = minthis.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: Stringvar amount: Int64public init(id: String, amount: Int64) {this.id = idthis.amount = amount}// open 方法允许子类重写public open func finalAmount(policy: DiscountPolicy): Int64 {let off = policy.discount(this.amount)let result = this.amount - offresult >= 0 ? result : 0}
}
-
使用
open以允许子类继承与重写,符合“类/成员需显式 open 才可继承/重写”的规则。 -
Order作为 class,属于引用类型:多个变量引用同一笔订单实例时,共享其状态。
3)策略实现:基于阈值的满减、基于百分比的折扣
// 满减策略:满 threshold 减 reduce
public class ThresholdDiscount <: DiscountPolicy {let threshold: Int64let reduce: Int64public init(threshold: Int64, reduce: Int64) {this.threshold = thresholdthis.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: Int64public 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 : caplet result = this.amount - boundedOffresult >= 0 ? result : 0}
}
说明:仓颉支持单继承;子类可通过
super(...)调父类构造,并重写open成员。这样既保留面向对象扩展性,又避免多继承复杂度。
5)使用示例与“值/引用”思考
main () {let p10 = PercentageDiscount(10) // 10% offlet full = ThresholdDiscount(10000, 800) // 满 100 元减 8 元(单位分)let o1 = Order("A001", 12800) // 128 元println(o1.finalAmount(p10)) // => 11520println(o1.finalAmount(full)) // => 12000// 同一对象的“共享引用”示例let o2 = o1o2.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
}
专业要点复盘
-
class/struct 取舍
-
共享状态/需要继承:选 class(引用类型)。
-
不可变值对象/跨线程传递/避免共享副作用:选 struct(值类型)。(docs.cangjie-lang.cn)
-
-
继承与接口的协作
-
行为扩展优先选 接口(interface)+ 组合;只有当需要复用状态与默认实现时再用继承,且保持单继承的层次扁平。(docs.cangjie-lang.cn)
-
-
open 的显式性
-
需要被继承/重写的类与成员都应加
open;默认不开放,有助于稳定 API、减少二义性。(docs.cangjie-lang.cn)
-
-
性能与运行时
-
class 属引用类型,会引入堆分配与 GC;仓颉提供并发 GC与轻量线程以降低停顿与调度成本,但在热点路径上仍建议用 struct 承载频繁复制的小对象,减少共享。(仓颉编程语言)
-
-
静态成员与静态初始化器
-
可用
static let/var声明类型级成员,并通过static init(){...}统一初始化一次(最多一个静态初始化器)。用于注册策略表、读取配置等场景尤为便利。
-
小结
-
在仓颉里,“类与对象”的基本盘与现代 OOP 一致,但通过显式 open、单继承 + 接口、多场景运行时优化,将工程可维护性与端侧性能兼顾。
-
结合上面的实战模板,你可以快速扩展更多策略(如阶梯折扣、品类券等),并用 struct 承载不可变规则数据,以值语义把并发与正确性问题挡在门外。🚀
