Yanonoblog!

こつこつと

引数が増えることによる弊害

はじめに

本記事では「良いコード 悪いコードで学ぶ設計入門」で学んだ内容と別途気になって調べた内容や知識も含めアウトプットしています。

書籍に記載されている内容を順序立ててまとめるというよりは、整理しておきたい周辺の知識と深ぼった内容を雑多に書いています。

書籍ではJavaで説明されていたのですが本記事ではRubyに置き換えながら解説しています。

多すぎる引数

低凝集に陥る良くない構造の例として引数が多すぎるメソッドなどがあります。

例 - 仕様

本著ではゲームにおける魔法力(MP)を例に説明されていました。

こういうやつですね。

image

MPに関して、以下の仕様であるとします。

  • 魔法を使うと、MPは一定量減少する。
  • 回復アイテムなどにより、MPは一定量回復する。
  • MPには最大値がある。
  • MPは最大値まで回復可能。
  • 一部の装備品は、MPの最大値を増加させる効果を持つ。
例 - コード
# 魔法力を回復するメソッド
#
# @param current_magic_point [Integer] 現在のMP残量
# @param original_max_magic_point [Integer] オリジナルのMP最大値
# @param max_magic_point_increments [Array<Integer>] MP最大値の増分の配列
# @param recovery_amount [Integer] 回復量
# @return [Integer] 回復後のMP残量
def recover_magic_point(current_magic_point, original_max_magic_point, max_magic_point_increments, recovery_amount)
  current_max_magic_point = original_max_magic_point

  # 装備品のMP最大値増加効果を適用
  max_magic_point_increments.each do |increment|
    current_max_magic_point += increment
  end

  # 回復後のMP残量を計算し、最大値を超えないように制限する
  recovered_magic_point = current_magic_point + recovery_amount
  final_magic_point = [recovered_magic_point, current_max_magic_point].min

  return final_magic_point
end

以下は解説です。

  • 各装備品のMPのプラス値の配列であるmax_magic_point_increments の要素を順に加算し、current_max_magic_point に魔法力最大値の増分を反映させます。
  • 引数に取っているMP残量であるcurrent_magic_point とデフォルトの回復量recovery_amount を加算し、recovered_magic_point に回復後の魔法力残量を計算します。
  • recovered_magic_pointcurrent_max_magic_point のうち、小さい方を選び、最終的な魔法力残量 final_magic_point として設定します。
  • final_magic_point を返します。

このメソッドによって、魔法力の回復処理が行われます。装備品の魔法力最大値増加効果が適用され、最大値を超えない範囲で魔法力が回復されます。

機能しますが、このメソッド構造は良くありません。

問題点

魔法力残量や魔法力最大値、魔法力最大値の増分、回復量がバラバラに渡されています。

バラバラに渡す方法は、不注意で正しくない値を代入してしまう可能性が高まります。

このメソッドでは、魔法力の回復処理を行っていますが、引数が多すぎるため、次のような問題が生じます。

  1. 引数の数が多いため、メソッドの使用方法を覚えるのが難しくなります。
  2. 引数の順序を間違える可能性があり、バグを引き起こす恐れがあります。
  3. メソッドの変更や追加があった場合、多くの箇所で引数を修正する必要があります。
  4. 引数の一部が関連性のない値である場合、メソッドの意図がわかりにくくなります。

これらの問題に対処するためには、メソッドの低凝集度を改善する必要があります。凝集度の高いメソッドは、単一の責務を持ち、少ない引数で動作するように設計されています。

また、回復以外の処理を行っています。

魔法力最大値の増加計算は、回復以外のさまざまなケースでの利用が容易に考えられるのに対し、このようなベタ書きロジックでは重複コードがさまざまな箇所に書かれる事態を招きます。

改善例

本著には具体的な例はなかったため要点を抑えてコードを設計しています。

合っているかはわかりませんが以下のコードになります。MagicPower クラスが魔法力に関連する機能を担当し、それぞれのメソッドが単一の責務する設計です。

class MagicPower
  attr_accessor :current, :max

  def initialize(max)
    @max = max
    @current = max
  end

  def use_magic(amount)
    @current -= amount
  end

  def recover_magic(amount)
    @current = [@current + amount, @max].min
  end

  def increase_max_magic(increment)
    @max += increment
  end
end

メソッドに引数を渡すのは、その引数を使って何か処理をさせたいからです。

引数の量が多いということは、すなわちそれだけ処理させたい内容が膨らむことになります。

処理内容が増えると、ロジックが複雑化したり重複コードが増えるため設計には特に注意が必要です。

参考

続く…

コメント

本記事の内容は以上になります!

書籍の続きのアウトプットも随時更新したいと思います。


プログラミングスクールのご紹介 (卒業生より)

お世話になったプログラミングスクールであるRUNTEQです♪

https://runteq.jp/r/ohtFwbjW

こちらのリンクを経由すると1万円引きになります。

RUNTEQを通じて開発学習の末、受託開発企業をご紹介いただき、現在も双方とご縁があります。

もし、興味がありましたらお気軽にコメントか、TwitterのDMでお声掛けください。

https://twitter.com/outputky