目前我的代码如下(有些简化)。最后,我添加了越来越多的新类,如D1 / D2,我认为是时候进行一些重构以使其更优雅。当然,目标是将新的Dx类添加为尽可能少的重复代码。至少,在单例方法FileImporter.import
中调用Dx.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 方式是什么?
更新 :(上面的代码中添加了一些评论,希望让我的意图更加清晰......)
最初,我遗漏了我认为不重要的代码以避免混淆。我应该提到上面的代码也是重构类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
答案 0 :(得分:1)
也许它不是最好的解决方案,但最初似乎Dx类共享相同的行为,因此使用具有self.import方法的C类对它们进行子类化,该方法使用块来接受其他一些代码。或者也可以通过包含一个模块来完成。
无论如何,这样的事情应该有用(对于较短的名字而言,这对于原型设计很有用)。 另请注意,我将FileImporter.import方法名称更改为另一个,以避免误解 并注意我没有测试代码:)
module F
def self.fimport(something)
yield "file"
end
end
class C
include F
def initialize(f)
end
def self.import(something, &block)
F.fimport(something) { |f|
d = self.new(f)
block.call
d
}
end
end
class D1 < C
def self.import(something)
super(something){
puts something
}
end
end
class D2 < C
def self.import(something)
super(something){
puts something
}
end
end
p D1.import("a")
p D2.import("b")
#=> a
#=> #<D1:0x100163068>
#=> b
#=> #<D2:0x100162e88>
答案 1 :(得分:1)
似乎制作一个模块将是一个优雅的解决方案。但是,很难用模糊的概念来说明代码的用途。例如:
module Importer
def import
self.whatever # self should be D1 or D2 as the case may be
# ...
end
end
class D1
include Importer
end
class D2
include Importer
end
答案 2 :(得分:0)
鉴于代码和上下文有限,我怀疑以下内容对您有用。如果不出意外,您可以了解如何使用模块来突破常见功能。
module FileImporter
def self.internal_import(main_window, viewers)
...
importer = yield file
...
end
private :self.internal_import
end
class D1
include FileImporter
def self.import(main_window)
self.internal_import(main_window, [:V1, ])
end
end
class D2
include FileImporter
def self.import(main_window)
self.internal_import(main_window, [:V2, ])
end
end