Currently I have code like the following (simplified somewhat). Eventually, I've added more and more new classes like D1/D2, and I think it's time to do some refactoring to make it more elegant. The goal of course is to make adding new class Dx use as little duplicate code as possible. At least, the duplicate parts of calling FileImporter.import
inside the singleton method Dx.import
should be factored out.
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(...)
Essentially FileImporter.import
is the common part, with Dx.import
being the variation. I'm not sure how to refactor these singleton methods. What is the common Ruby way of doing this?
Update: (some comments were added into code above, hopefully make my intension clearer ...)
Originally, I've left out code I thought not significant to avoid confusion. I should have mentioned that the code above was also the result of refactoring class D1 and D2 (by moving common part out and into module FileImporter
). The purpose of D1.import
and D2.import
was mainly to create objects of proper class (and possibly followed by some class-specific handling before returning from the block). FileImporter.import
is mainly the common logic, within which at some point would yield to specific class for generating the importer object.
I feel that class D1 and D2 and looks really similar and it should be possible to further refactor them. For example, they both call FileImporter.import
to supply a block, within which both create an object of itself.
Solution: Originally I didn't realize you could call base class's singleton methods simply by calling super
from within derived class's corresponding singleton methods. That was really the main problem I had and was not able to go with that route. So I've accepted @makevoid answer as it indeed makes creating new derived classes more easily.
Using a common base class is an elegant refactoring solution, but one problem with that is all new derived classes would already use up the one base class quota. I've came to this class macro method which provides even more concise results from the derived class perspective.
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