【发布时间】:2010-01-26 15:09:57
【问题描述】:
目前我有如下代码(有些简化)。最终,我添加了越来越多的新类,例如 D1/D2,我认为是时候进行一些重构以使其更优雅了。目标当然是使添加新类 Dx 使用尽可能少的重复代码。至少,应该排除在单例方法Dx.import 中调用FileImporter.import 的重复部分。
module FileImporter
def self.import(main_window, viewers)
...
importer = yield file # delegate to D1/D2 for preparing the importer object
...
end
end
class D1
def self.import(main_window)
viewers = [:V11, ] # D1 specific viewers
FileImporter.import(main_window, viewers) do |file|
importer = self.new(file)
... # D1 specific handling of importer
return importer
end
end
end
class D2
def self.import(main_window)
viewers = [:V21,:v22, ] # D2 specific viewers
FileImporter.import(main_window, viewers) do |file|
importer = self.new(file)
... # D2 specific handling of importer
return importer
end
end
end
# client code calls D1.import(...) or D2.import(...)
基本上FileImporter.import 是通用部分,Dx.import 是变体。我不确定如何重构这些单例方法。这样做的常见 Ruby 方法是什么?
更新:(上面的代码中添加了一些 cmets,希望能让我的意图更清晰......)
最初,为了避免混淆,我省略了我认为不重要的代码。我应该提到上面的代码也是重构类 D1 和 D2 的结果(通过将公共部分移出并移入模块 FileImporter)。 D1.import 和 D2.import 的目的主要是创建适当类的对象(并且可能在从块返回之前进行一些特定于类的处理)。 FileImporter.import 主要是通用逻辑,在其中某些时候会屈服于特定的类来生成导入器对象。
我觉得 D1 和 D2 类看起来非常相似,应该可以进一步重构它们。例如,它们都调用FileImporter.import 来提供一个块,在其中都创建一个自己的对象。
解决方案:最初我没有意识到您可以通过在派生类的相应单例方法中调用super 来调用基类的单例方法。这确实是我遇到的主要问题,并且无法采用那条路线。所以我接受了@makevoid 答案,因为它确实使创建新的派生类更容易。
使用公共基类是一种优雅的重构解决方案,但其中一个问题是所有新的派生类都已经用完一个基类配额。我来到了这个类宏方法,它从派生类的角度提供了更简洁的结果。
module FileImporter
def self.included(mod)
mod.extend ClassMethods
end
module ClassMethods
def importer_viewer(*viewers, &blk)
@viewers = viewers
@blk = blk
class << self
def import(main_window)
if @blk.nil?
FileImporter.import(main_window, @viewers) do |file|
self.new(file)
end
else
FileImporter.import(main_window, @viewers, &@blk)
end
end
end
end
end
def self.import(main_window, viewers, multi=true)
...
importer = yield file # delegate to D1/D2 for preparing the importer object
...
end
end
class D1
include FileImporter
importer_viewer [:V11, ] do
... # D1 specific handling of importer
end
end
class D2
include FileImporter
importer_viewer [:V21,:v22, ] do
... # D2 specific handling of importer
end
end
【问题讨论】:
-
如果实际目的仍然是个谜并且没有给出所有代码,那么重构代码就很棘手。你能分享更多细节吗?另外,我真的希望您的课程实际上不称为 D1 和 D2。 :-)
-
刚刚添加了更多细节。是的,D1 和 D2 是为此目的而编造的名称。 :-)
标签: ruby refactoring