引数が増えることによる弊害
はじめに
本記事では「良いコード 悪いコードで学ぶ設計入門」で学んだ内容と別途気になって調べた内容や知識も含めアウトプットしています。
書籍に記載されている内容を順序立ててまとめるというよりは、整理しておきたい周辺の知識と深ぼった内容を雑多に書いています。
書籍ではJavaで説明されていたのですが本記事ではRubyに置き換えながら解説しています。
多すぎる引数
低凝集に陥る良くない構造の例として引数が多すぎるメソッドなどがあります。
例 - 仕様
本著ではゲームにおける魔法力(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_point
とcurrent_max_magic_point
のうち、小さい方を選び、最終的な魔法力残量final_magic_point
として設定します。final_magic_point
を返します。
このメソッドによって、魔法力の回復処理が行われます。装備品の魔法力最大値増加効果が適用され、最大値を超えない範囲で魔法力が回復されます。
機能しますが、このメソッド構造は良くありません。
問題点
魔法力残量や魔法力最大値、魔法力最大値の増分、回復量がバラバラに渡されています。
バラバラに渡す方法は、不注意で正しくない値を代入してしまう可能性が高まります。
このメソッドでは、魔法力の回復処理を行っていますが、引数が多すぎるため、次のような問題が生じます。
- 引数の数が多いため、メソッドの使用方法を覚えるのが難しくなります。
- 引数の順序を間違える可能性があり、バグを引き起こす恐れがあります。
- メソッドの変更や追加があった場合、多くの箇所で引数を修正する必要があります。
- 引数の一部が関連性のない値である場合、メソッドの意図がわかりにくくなります。
これらの問題に対処するためには、メソッドの低凝集度を改善する必要があります。凝集度の高いメソッドは、単一の責務を持ち、少ない引数で動作するように設計されています。
また、回復以外の処理を行っています。
魔法力最大値の増加計算は、回復以外のさまざまなケースでの利用が容易に考えられるのに対し、このようなベタ書きロジックでは重複コードがさまざまな箇所に書かれる事態を招きます。
改善例
本著には具体的な例はなかったため要点を抑えてコードを設計しています。
合っているかはわかりませんが以下のコードになります。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です♪
こちらのリンクを経由すると1万円引きになります。
RUNTEQを通じて開発学習の末、受託開発企業をご紹介いただき、現在も双方とご縁があります。
もし、興味がありましたらお気軽にコメントか、TwitterのDMでお声掛けください。