I like
Google philosophy a lot, especially Thing Number 9 :
- 9. You can be serious without a suit.
, but that's not the subject of this sermon; today I'd like to tell you about Thing Number Two (TNT) :
- 2. It's best to do one thing really, really well.
- Amen -, and its IT translation : the "
Separation Of Concerns".
At the humble coder's level, separated concerns make for a more readable code.
Counter example:
1
2
3
4
5
6
|
def fetch_stuff(*args)
benchmark = Benchmark.measure{
Stuff.fetch_all
}
puts "time spent: #{benchmark} "
end
|
It's ugly, but it works.
It works, but it's ugly: this code does TWO completely unrelated things:
- fetch some stuff, and
- benchmark an action, any action
We MUST find a way to separate those 2 concerns:
The first and main concern is obvious :
1
2
3
|
def fetch_stuff
Stuff.fetch_all # <<--- CONCERN 1
end |
The second concern is easy to code
1
2
3
4
5
6
|
def generic_benchmarking_method
benchmark = Benchmark.measure
action_to_measure # <<--- (concern 1)
}
puts "time spent : #{benchmark}}" # <<--- CONCERN 2
end
|
but it's difficult to connect to the first method, because :
- users of concern 1 should not have to know about concern 2 => they will just call the first method
- as the benchmarking action is completely independent from the action it measures, this action should be passed as a parameter in some way
The solution:
piggy-back the benchmarking method on the fetching method, transparently.
Q: how do you piggy back ?
A: with Rails'
alias_method_chain , and some "convention over configuration magic"**
(** hint: it's all in the
methods naming)
Take a deep breath and watch the mystery unveil :
Before
1
2
3
4
5
6
|
def fetch_stuff(*args)
benchmark = Benchmark.measure{
Stuff.fetch_all
}
puts "time spent: #{benchmark} "
end
|
After
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
# concern 1: just do it.
def fetch_stuff
Stuff.fetch_all
end
# concern 2: just benchmark it.
def fetch_stuff_with_benchmarking(*args)
benchmark = Benchmark.measure{
fetch_stuff_without_benchmarking(*args)
}
puts "time spent: #{benchmark} "
end
alias_method_chain :fetch_stuff, :benchmarking |
That's it :
alias_method_chain wrapped
benchmarking around
fetch_stuff. After line 14 has been parsed, calls to the original
fetch_stuff are redirected to
fetch_stuff_with_benchmarking, and
fetch_stuff_without_benchmarking points to the original
fetch_stuff. It's transparent and automatic. All you have to do is respect the naming convention :
with and
without.
That's it.
This
method was introduced in Rails a year ago to DRY up the internals. There is no reason your code should not benefit shamelessly from it too. Strive for it. You'll thank me later.

API :
Articles :
0 comments:
Post a Comment