Ruby:改进复杂初始化方法
我有一段Ruby代码,可以归结为:Ruby:改进复杂初始化方法,ruby,refactoring,Ruby,Refactoring,我有一段Ruby代码,可以归结为: class Foo attr_reader :a, :b, :c def initialize build_a build_b build_c end private def build_a # something complex that eventually results in @a = something end def build_b # something complex th
class Foo
attr_reader :a, :b, :c
def initialize
build_a
build_b
build_c
end
private
def build_a
# something complex that eventually results in @a = something
end
def build_b
# something complex that eventually results in @b = something
end
def build_c
# something complex that eventually results in @c = something
end
end
在initialize
方法中调用build.*
似乎有点多余。有没有更好的方法写这个?当然,我知道延迟加载模式:
class A
def a
@a ||= something_complex
end
end
但是,我需要这个代码是线程安全的,所以我不能在这里使用这个模式
编辑:我对这段代码的主要关注点是,我希望看到这样一个事实,即build\u a
应该在初始化后调用,在build\u a
的定义中编写,而不是在initialize
方法中编写
require 'active_support/callbacks'
class Foo
include ActiveSupport::Callbacks
define_callbacks :initialize
attr_reader :a, :b, :c
def initialize
run_callbacks :initialize do
# do the rest of initialize
end
end
protected
def build_a
# something complex that eventually results in @a = something
end
set_callback :initialize, :after, :build_a
def build_b
# something complex that eventually results in @b = something
end
set_callback :initialize, :after, :build_b
def build_c
# something complex that eventually results in @c = something
end
set_callback :initialize, :after, :build_c
end
我不是100%确定我喜欢这个解决方案,但它是有效的
编辑:在思考和使用Piotr Kruczek的解决方案后,我采用了以下方法:
class Foo
def initialize
protected_methods.grep(/^initialize_/).each do |method|
send(method)
end
end
protected
def initialize_a
# something complex that eventually results in @a = something
end
def initialize_b
# something complex that eventually results in @b = something
end
def initialize_c
# something complex that eventually results in @c = something
end
end
这些回调将是一个真正的痛苦的驴测试和维护。这个解决方案不是更好吗
class Foo
attr_reader :a, :b, :c
def initialize
# things that belong in initialize
end
def self.call # or any other name
new.build_things
end
def build_things
build_a
build_b
build_c
end
end
唯一的缺点是使用
Foo.call
而不是Foo.new
。如果你不想让这个类“感觉”像一个服务对象,我会把它包装成一个,比如FooBuilder
。通过这种方式,您可以避免回调,测试很容易,代码干净易读。如果你想在初始化后构建东西
,我认为这是最好的方法。Hmm,我喜欢将这个构建器列表放在另一个方法中的想法,但我不认为它解决了主要问题:调用的方法列表。也许我会尝试实现一个调用所有build.*
方法或类似方法的循环。