Using class methods to drive the behavior of your class.
When writing Ruby, instead of this:
I believe you should do this instead:
It reduces coupling and simplifies maintenance
Both the constructor and the “perform” method are part of the public API of
Table::Load By reducing the public API you reduce coupling and reasons why callers may have to change.
It simplifies unit-testing
When unit testing, you don’t want the code under test to execute the code inside
Table::Load. By making just one method call, just one simple stub/expectation is needed:
load = double allow(Table::Load).to receive(:new).and_return(load) expect(load).to receive(:perform)
It allows complexity to be introduced without changing every caller
This is related to the first reason. Lets say you need to create different types of classes depending on a characteristic of the file to be loaded. Instead of having to leak that problem into the caller, you can hide that change inside the static facade. The caller doesn’t care about which implementation is being used to load the file — it just want it to be loaded. This allows you to introduce complexity inside
Load::Table while keeping the usage simple. One example could be
class Table::Load def self.perform(file) if file.end_with?(".gz") new(GzFile.new(file)).perform else new(PlainFile.new(file)).perform end end # constructor and perform here... end
This can be later extracted to a builder or factory if necessary.
But class methods are a mess!
That’s why the underlying implementation should be the same:
class Table::Load def self.perform(file) new(file).perform end # constructor and perform here... end
Another reason why I prefer to encapsulate those two calls is because the class method makes the usage of this class explicit. I believe that anyone who sees that class will immediately learn how the class is intend to be used.
I also think this kind of construction is resistant to the introduction of many other public methods. If you feel you need that, you probably want to introduce/extract a new class.
The only drawback of this design is the extra class method, but I find that acceptable given the benefits. One option to mitigate this small issue is using a gem like attr_extras. With it, the final code would be:
class Table::Load static_facade :perform, :file # constructor and perform here... end
Here is the documentation for the static_facade.
The next step
You can even go one step further by naming your class as an action and rename your static facade to “call” or whatever is the convention on your language for callable objects. Those are sometimes called method objects. The attr_extras gem for Ruby also has a macro for that named method_object.
Method objects like that work better when they represent “outer layers” of your system or a complicated but well defined process.
What do you think? Do you see the benefits of this approach? Can you think of drawbacks of this kind of design?