实现思路
- 定义
ImmutableModule
模块。
- 在模块中使用
define_method
动态定义 initialize
方法的包装器,在原始 initialize
方法执行后调用 freeze
方法冻结实例。
- 为模块添加
included
钩子方法,在类包含该模块时,将所有实例方法(包括动态定义的)替换为包装后的方法,确保方法不会修改对象状态。
代码示例
module ImmutableModule
def self.included(base)
base.instance_methods.each do |method_name|
next if method_name == :initialize
base.define_method(method_name) do |*args, &block|
frozen_self = self.frozen? ? self : freeze
frozen_self.__send__(method_name, *args, &block)
end
end
end
def initialize(*args, &block)
super(*args, &block)
freeze
end
end
class ImmutableClass
include ImmutableModule
def initialize(value)
@value = value
end
def get_value
@value
end
end
优点
- 数据完整性:确保对象状态在初始化后不可变,有助于维护数据的一致性和完整性,特别适用于多线程环境,减少同步问题。
- 安全性:防止意外修改对象状态,降低程序出现逻辑错误的风险。
- 可预测性:由于对象状态不可变,其行为更可预测,便于调试和维护。
缺点
- 灵活性降低:对象状态无法修改,对于需要频繁更新状态的场景不适用,如计数器、缓存等。
- 性能开销:每次调用方法时都要检查对象是否冻结,以及可能的冻结操作,在高并发或性能敏感的场景下可能带来性能问题。
- 内存消耗:不可变对象每次修改都需要创建新的对象,可能导致内存占用增加,特别是对于大型对象或频繁修改的场景。