ある日のこと、久しぶりに過去に自分が実装した機能のコードを見ていると「何だこのコードは...」と悪い意味で驚愕しました。内容はデータベースから取ってきたデータをひたすら加工し、集計して最終的に画面に表示するというものです。
ひたすら加工と書いた通り、本当にひたすらデータを加工しまくっています。配列からハッシュ(Rubyなので)へ。ハッシュをさらにハッシュや配列に記録したりと、データ加工の処理がベルトコンベアのように淡々と実行されていきます。
class Pipeline def exec(params) fetch_records(params) .then { |records| do_something1(records) } .then { |records| do_something2(records) } .then { |records| do_something3(records) } end def fetch_records(params) ... end def do_something1(records) records.filter do |record| ... end end def do_something2(records) records.reduce({}) do |record, accum| ... end end def do_something3(recods_hash) recods_hash.reduce({}) do |record, accum| ... end end end
なぜそのような実装をしたのか
実装を担当した当時、自分は関数型言語の世界にハマっていました。特に影響を受けたのはelixir
というプログラミング言語です。複数の処理(正しくは関数の実行)をパイプライン演算子で簡単に繋げていくことが出来るため、小さな関数を作って組み合わせることで非常にパワフルな処理を生み出すことが出来ます。
Enum.to_list(1..100) |> Enum.map(fn n -> n * 2 end) # [2,4,6...] |> Enum.filter(fn n -> n < 100 end) # [2,4...98] |> Enum.reduce(0, fn n, acc -> acc + n end) |> IO.puts() # 2450
この方法に感銘を受けたので、当時はクラスメソッドに小さな関数を定義し組み合わせることで「Ruby
でも同じことをやろう」と考えていました。結果的に、実現することは出来て満足しましたが、今になって改めてコードを見てみると「何だこのコードは...」と感じてしまいました。
らしさを大切に
どのプログラミング言語にも適切な書き方やパラダイムが存在しており得意なこと、苦手なことがあります。これぐらいの規模のコードならelixir
のパイプラインを組み合わせるという方法は上手く機能していますが、大規模な場合や仕様が複雑なケースでは可読性が低下します。
# これぐらいならまだ良い class Pipeline class << self def exec # thenを使うともう少しマシになるが |> のスッキリさには劣る lst = (1..100).to_a lst = lst.map { |n| n * 2 } lst = lst.filter { |n| n < 100 } lst.reduce(0) { |n, acc| acc + n } end end end puts Pipeline.exec # 2450
それよりかはRuby
で実装するのならRuby
の強みを生かして、クラス設計などを適切に行う必要があり、いわゆる「らしさ」を大切にしたいと思いました。
「何だこのコードは...」と感じたのは自分の視野、知識が広がったという成長の証なのかもしれません。自分の中のベストプラクティスというのは流動的であり「日々、変化しているんだ」と実感しました。先月に書いたコードも、今になって見れば書き直したくなることはよくあることです。