概要
Reject unworkable values with preconditions
前提
Confident Rubyではメソッド内の処理を次のように分類しています。
・Collecting Inputs(引数チェック、変換など)
・Performing Work(主処理)
・Delivering Output(戻り値に関わる処理)
・Handling Failure(例外処理)
当記事は上記のうち、Collecting Inputsに関する話です。
詳細
状況
いくつかの入力値は利用可能な形式に変換することができない。
それらを受け入れることは、潜在的に有害でデバッグを困難にする側面がある。
概要
前提条件説(precondition clauses)を利用して早い段階で受入不可能な値を拒否する。
理由
すぐに失敗する方が、部分的に成功して混乱した例外を引き起こすよりも良い
前提条件説(precondition clauses)について
・不正な入力を事前に防ぐ
・各メソッドの処理前に前提条件説(precondition clauses)を確認することで、期待される入力値のドキュメントの役割も果たす
サンプルコード仕様(修正前)
Personクラスに名前と年齢を保持。
振る舞いは
大人かどうか返却する:adult?
年齢を説明する:explain_age
さばを読む:fudge_the_count
の3種類です。
各メソッドはおそらく別々の開発者が作成した関係で、ageに対する入力値の扱いがことなります。
このような不統一はバグの元になる可能性が大きいです。
サンプルコード
class Person attr_reader :name, :age def initialize(name, age) @name, @age = name, age end def adult? fail "invalid" unless @age @age >= 20 end def explain_age @age ? "I'm #{@age} years old" : "" end # さばを読む def fudge_the_count (@age / 10).ceil * 10 end end tanaka = Person.new("tanaka", 15) puts tanaka.adult? puts tanaka.explain_age puts tanaka.fudge_the_count puts "-------------------------" suzuki = Person.new("suzuki", 25) puts suzuki.adult? puts suzuki.explain_age puts suzuki.fudge_the_count
出力
false I'm 15 years old 10 ------------------------- true I'm 25 years old 20
サンプルコード仕様(修正後)
Personクラスに名前と年齢を保持。
振る舞いは
大人かどうか返却する:adult?
年齢を説明する:explain_age
さばを読む:fudge_the_count
の3種類です。
ここまでは修正前と変わりませんが、前提条件説(precondition clauses)を利用して
コンストラクタの処理で不正な入力を除外します。
そのため、各メソッドでは不正な値について気にする必要がありません。
また、入力値に対する対応も一環しています。
きいです。
サンプルコード
class Person attr_reader :name, :age def initialize(name, age) @name= name self.age = age end def age=(age) fail 'invalid age nil' if age.nil? fail 'invalid age not have to_i' unless age.respond_to?(:to_i) @age = age.to_i end def adult? @age >= 20 end def explain_age "I'm #{@age} years old" end # さばを読む def fudge_the_count (@age / 10).ceil * 10 end end tanaka = Person.new("tanaka", 15) puts tanaka.adult? puts tanaka.explain_age puts tanaka.fudge_the_count puts "-------------------------" suzuki = Person.new("suzuki", 25) puts suzuki.adult? puts suzuki.explain_age puts suzuki.fudge_the_count
出力
false I'm 15 years old 10 ------------------------- true I'm 25 years old 20