Help us understand the problem. What is going on with this article?

【Ruby】柔軟性のあるコードを書くために気をつけていること

More than 1 year has passed since last update.

普段コードを書く上で気をつけている点を多少まとめました。
みなさんの参考になれば幸いです。

全般的なコト

プログラムの単一責任化

変更に対して強いコードは、往々にして個々のプログラムの処理内容がシンプルです。
シンプルなプログラムはそれ自体が何をやっているかが分かりやすく、また他のプログラムと組み合わせて使うのが簡単です。
変更があった際も、どこを変更すれば良いのかが比較的明確かつ、影響する場所も特定が簡単です。

そのため複雑な処理を行う一つの大きなプログラムを書くのではなく、シンプルな処理を行うプログラム群を作り、それらを組み合わせるようにしています。

例えば、下記のような給与を計算する処理があったとします。
この場合、total_salaryは給与の要素の合算値を出していますが、このメソッド内部で色々な要素の計算式を扱っています。
この場合、給与を構成する計算式や要素が変更されると、total_salaryがどんどん複雑になっていきます。
また、個々の要素の合算値を求めたり、組み合わせたりする場合には、このメソッドを使い回せないので別途メソッドを作る必要があります。

def total_salary
 (working_days * daily_salary * salary_rank) + (bonus_rate * prev_contribution) - (absence_days * base_panishment_fee * panishment_rank)
end

このような場合、下記のように一つのプログラム(メソッド)が一つの役割を実施するように処理を分けてあげると可読性やメンテナンス性が向上します。

def total_salary
  base_salary + bonus - absence_amount
end

def base_salary
  working_days * daily_salary * salary_rank
end

def bonus
  bonus_rate * prev_contribution
end

def absence_amount
  absence_days * base_panishment_fee * panishment_rank
end

引数

メソッドに渡す引数はその順番や数に対する依存が発生します。
下記の場合、引数が3つでhoge, fuga, piyo の順番で引数に渡す必要があります。
引数が2つや3つ程度ならいいのですが、5つくらいになるとだいぶ辛くなってきます。

def initialize(hoge, fuga, piyo)
  @hoge = hoge
  @fuga = fuga
  @piyo = piyo
end

Hashで渡す

引数をHash(またはキーワード)で渡すことで、引数に渡す順序を気にしなくても良いようになります。

def initialize(args)
  @hoge = args[:hoge]
  @fuga = args[:fuga]
  @piyo = args[:piyo] || 'piyo' # 引数がない場合のデフォルト値
end

Hash#fetchを使うことで、より安全にハッシュで引数を渡すことができます。

def initialize(args)
  @hoge = args.fetch(:hoge)
  @fuga = args.fetch(:fuga)
  @piyo = args.fetch(:piyo)
  @bool = args.fetch(:bool, false) # 第二引数にデフォルト値を入れられます
end

配列

一度ラップする

配列を操作する場合、配列のindexを指定することが多いと思います。
特にCSVや外部サービスから取得したデータなど、どうしても配列で値を取得せざるを得ない場合、 arr[0] などと記載するケースが多いと思います。

Hashのようにキーを指定するのであれば、valueがどのようなデータであるかはキー名から想像がつきますが、indexによる指定では扱っているデータが何なのかがパッと分かりません。
また、配列を直接参照している場合、配列内の要素の順序変更や追加によって参照元の処理を全て修正する必要があります。
さらに、配列がネストしていたりと複雑な構造の場合には、それだけ複雑な内部構造の知識があらゆるところで使われてしまいます。

そのため、配列を扱う場合には配列の内部構造を知っている箇所を絞ってやるのが良いと考えています。

original_array = ["ばなな", 50]
Product = Struct.new(:name, :price)

# product_infoの配列順序に関する知識を管理する
# product()を呼び出した後はキーを指定することで値を取得でき、配列内部について知らなくても良くなる
def convert2struct(arr)
  Product.new(arr[0], arr[1])
end

product = convert2struct(original_array)
# => #<struct Product name="ばなな", price=50>
product[:name]
# => "ばなな"
product[:price]
# => 50

条件式

if文やcase文を使うと楽に条件毎の処理を記載することができます。

ただその一方で条件分岐が多くなるに伴ってコードの量と複雑さが増します。
またコードの再利用性も乏しいので、条件分岐が多くなるケースでは(特にcase文は)できる限り使いたくない処理のうちの一つです。

# if文の場合
if hoge == 'HOGE'
  # 何らかの処理
elsif hoge == 'FUGA'
  # 何らかの処理
elsif hoge == 'PIYO'
  # 何らかの処理
else
  # 何らかの処理
end

# case文の場合
case hoge
when 'HOGE'
  # 何らかの処理
when 'FUGA'
  # 何らかの処理
when 'PIYO'
  # 何らかの処理
else
  # 何らかの処理
end

ダックタイプを利用

オブジェクトは自身の情報(自身がどのクラスに所属しているか、どのメソッドに応答できるかなど)を知っているので、ifやcaseで確認するのではなく、オブジェクトにメッセージを送るだけで完結するのが理想です。
そこで用いられるのはダックタイピングです。
詳細はこちらを参照いただけると幸いです。

その他

OOPらしい柔軟性のあるコードを書いていくためには、何はともあれSOLIDの概念をきちんと理解することが重要かなと思っています。
SOLIDの概念はこちらがわかりやすかったです。

また、特にRubyを扱っていらっしゃる方には下記の本を一読されることをお勧めします。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした