コード日進月歩

しんくうの技術的な小話、メモ、つれづれ、など

変数には1度だけ書き込み、オブジェクトはできるだけ生成後に触れる部分を最小限にする。

スコープを狭くしよう、というコラムです。

変数には1度だけ書き込むほうがいい

かのリーダブルコードに以下のような記述がある

本章では「生きている」変数が多いとコードが理解しにくくなることを説明した。でももっと理解し難いのは、変数が絶えず変更され続けることだ。値を追跡する難易度が格段に上がってしまう。 この問題と戦うために、ちょっと変わったものを提案したい。それは、変数は一度だけ書き込むというものだ。
- リーダブルコード 9.3 変数は一度だけ書き込む より

変数というのは可変の数値ではあるが、なるべく変更されるのは少なくあってほしい。理由をあげるとすれば…

  • 変数が書き換わることがあればその書き換わる前後で差分を比較する必要がある
  • 値が変わらなければ、意図違いで使うことは減る。

という部分である。ここらへんは詳しくはリーダブルコードを読んでください。。

この「変数は1度だけ書き込むことが望ましい」ということを踏まえて「オブジェクトはできるだけ不変とする」という話をしたい。

オブジェクトはできるだけ生成後に変わる部分を少なく

たとえば下記のようなクラスがあったとする。

class Human
  attr_accessor :first_name, :last_name, :age

  def output_profile_text
    "Hello! My name is #{first_name} #{last_name}, #{age} old."
  end

end

これをオブジェクト化して使おうとすると以下のようになると思います。

taro = Human.new
taro.first_name = "Taro"
taro.last_name = "Suzuki"
taro.age = "20"
p taro.output_profile_text

このようになった場合に、 taro オブジェクトは生成後に必要な値をセットしないと output_profile_text が正しく書き出せない。

taro = Human.new
taro.first_name = "Taro"
taro.last_name = "Suzuki"
p taro.output_profile_text
#=>"Hello! My name is Taro Suzuki,  old."

また名前のようなものは、一度設定したら変わることはないので、外から変更可能というのも別に必要性がない。

taro = Human.new
taro.first_name = "Taro"
taro.last_name = "Suzuki"

# おそらくこのような書き換えは正常ケースでは不要
taro.last_name = "Sato"

このように

  • 最初から使うことが約束されている
  • 途中から書き換えることが無い

というような情報はインスタンスが作られる時点でセットしてあげるようにしたほうが、変な書き換えを心配しなくていいし、見るべきスコープを狭くすることができる。ここの考え方は最初の変数の話と似た部分となる。

class Human
  # 初期化で絶対必要になる情報をセット,利便性を考えて名前付き引数
  def initialize(first_name:, last_name:, age:)
    @first_name = first_name
    @last_name = last_name
    @age = age
  end

  def output_profile_text
    "Hello! My name is #{@first_name} #{@last_name}, #{@age} old."
  end

end
taro = Human.new(first_name: "Taro", last_name: "Suzuki", age: 20)
p taro.output_profile_text

このようにして書き換えてあげれば途中で書き換える使われ方も減るはずだし、スコープも狭くなるので見通しは遠くなるはずです。

参考リンク

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)