不変な値を活用してコード設計を行う
はじめに
本記事では「良いコード 悪いコードで学ぶ設計入門」で学んだ内容と別途気になって調べた内容や知識も含めアウトプットしています。
書籍に記載されている内容を順序立ててまとめるというよりは、整理しておきたい周辺の知識と深ぼった内容を雑多に書いています。
書籍ではJavaで説明されていたのですが本記事ではRubyに置き換えながら解説しています。
可変と不変
変数やオブジェクトの値が状態変更できることを可変(ミュータブル)と呼びます。
例えば、配列やオブジェクトの要素の値を変更できる場合、それは可変です。
一方で、状態変更できないことを不変(イミュータブル)と呼びます。
変数やオブジェクトの値が変更不可能であり、一度設定された値は変更できないことを意味します。
適切な設計がされていないとどうなるか
可変と不変が適切に設計されていないと、ある値が特定の状況で変更されることを想定して実装したのに、別の箇所で予期しない値に変わっているといった問題が発生します。
デバッグやメンテナンスの困難さにつながり、開発者が予測できない挙動に悩まされることになります。
上記の問題をなくすには変更を最小限にする設計が重要です。
再代入
変数に再度値を代入することを再代入、または破壊的代入と呼びます。
再代入は変数の意味が変わり、推測を困難にします。また、いつ変更されたのか追うのが難しくなります。
アンチパターン例
下記の例では同じ命名の変数に対してパラメータの加算や補正を再代入して使い回されています。
このように途中で変数の意味が変わるとコードの可読性が低下し、メンテナンスや修正をするのが困難になります。。
def damage(member, enemy) # メンバーの腕力と武器性能を基本攻撃力として定義 tmp = member.power + member.weapon_attack # メンバーのスピードで攻撃力を補正 tmp = (tmp * (1 + member.speed / 100)).to_i # 攻撃力から敵の防御力を差し引いたのがダメージ tmp = tmp - (enemy.defence / 2).to_i # ダメージ値が負数にならないよう補正 tmp = [0, tmp].max tmp end
改善例
可読性を向上させるためには、変数の意味を明確にし、適切な命名規則を使用します。
変数名をより具体的なものにすることで、コードの意図や役割が明確になります。
def damage(member, enemy) base_attack = member.power + member.weapon_attack speed_modifier = (base_attack * (1 + member.speed / 100)).to_i defense_adjustment = (enemy.defence / 2).to_i final_damage = [0, speed_modifier - defense_adjustment].max final_damage end
上記のように命名を分けて再代入を行わないのは至極一般的ですが、再代入自体を防ぐ方法もあります。
不変にして再代入を防ぐ
freezeメソッド
Rubyにおいては、変数を不変にするためには freeze
メソッドを使用することができます。
freeze
メソッドを呼び出すと、オブジェクトが変更できない不変な状態になります。
def do_something value = 100 value.freeze # valueを不変にする value = 200 # 実行時エラー(RuntimeError)が発生する end
不変にするメリット
不変にすると以下のメリットが得られます。
- 変数の意味が変化しなくなるため、混乱が抑えられる
- 挙動が安定し、結果を予測しやすくなる
- コードの影響範囲が限定的になり、保守が容易になる
定数を定義したらfreezeを使う
Railsにおいて定義した定数は基本的にfreeze
しておくことが推奨されています。これにより、定数が不用意に変更されることを防ぐことができます。
# 定数の例 POKEMON = ['zenigame', 'fushigidane', 'hitokage'].freeze
定数をfreeze
しておくと、その値を変更しようとするとFrozenError
が発生します。これにより、誤って定数の値を変更することを防止し、予期しないバグの発生を防ぐことができます。
JavaScriptではconstを使用すれば良いだけ
JavaScriptにおいては const
キーワードを使用することで変数を不変(再代入不可)にすることができます。
const
で宣言された変数は、初期化後に再代入することはできません。
function doSomething() { const value = 100; value = 200; // エラー: 再代入はできません }
参考
続く…
コメント
本記事の内容は以上になります!
書籍の続きのアウトプットも随時更新したいと思います。
プログラミングスクールのご紹介 (卒業生より)
お世話になったプログラミングスクールであるRUNTEQです♪
こちらのリンクを経由すると1万円引きになります。
RUNTEQを通じて開発学習の末、受託開発企業をご紹介いただき、現在も双方とご縁があります。
もし、興味がありましたらお気軽にコメントか、TwitterのDMでお声掛けください。