Tbpgr Blog

Employee Experience Engineer tbpgr(てぃーびー) のブログ

Ruby | Delivering Output | Represent failure with a special case object

概要

Represent failure with a special case object

前提

Confident Rubyではメソッド内の処理を次のように分類しています。
・Collecting Inputs(引数チェック、変換など)
・Performing Work(主処理)
・Delivering Output(戻り値に関わる処理)
・Handling Failure(例外処理)

当記事は上記のうち、Delivering Outputに関する話です。

詳細

状況

クエリーメソッドがいつも値を見つけることができるとは限らない

概要

nilの代わりに特別なオブジェクトを返却する。
例えばウェブサイトの匿名ユーザーの訪問へのGuestUserの返却が代表的です。

理由

特別なオブジェクトはnilチェックを避け、通常のオブジェクトと同様に安全に代役として動作します。

サンプルコード仕様

Programmersクラス、Programmerクラスを作成します。
ProgrammersはProgrammerクラスの配列をメンバに持ちます。
Programmerクラスはnameメンバを持ち、programmingを振る舞いに持ちます。

Programmersが保持していないProgrammerはエセプログラマ(PseudoProgrammer)とします。
彼らはプログラマの不利をしていますがプログラミングができません。

サンプル1ではProgrammersに対象が含まれていない場合はnilを返却しています。
この場合、以降の処理で都度nilチェックによる分岐が必要です。

サンプル2ではProgrammersに対象が含まれていない場合はPseudoProgrammerクラスを返却しています。
この場合、以降の処理では通常のProgrammerクラスと同様の処理が可能となり分岐が不要になります。

サンプルコードその1

class Programmers
  attr_reader :programmers

  def initialize(programmers)
    @programmers = programmers
  end
end

class Programmer
  attr_reader :name

  def initialize(name)
    @name = name
  end

  def programming
    "#{@name} is programming.."
  end
end

programmers = Programmers.new([Programmer.new('tanaka'), Programmer.new('sato')])

['tanaka', 'sato', 'suzuki'].each do |person|
  programmer = 
    if programmers.programmers.any? { |v|v.name == person }
      programmers.programmers.find { |v|v.name == person }
    end

  if programmer
    puts programmer.programming
  else
    puts "#{person} can not programming(he/she is pseudo programming)"
  end
end

出力

tanaka is programming..
sato is programming..
suzuki can not programming(he/she is pseudo programming)

サンプルコードその2

class Programmers
  attr_reader :programmers

  def initialize(programmers)
    @programmers = programmers
  end
end

class Programmer
  attr_reader :name

  def initialize(name)
    @name = name
  end

  def programming
    "#{@name} is programming.."
  end
end

class PseudoProgrammer
  attr_reader :name

  def initialize(name)
    @name = name
  end

  def programming
    "#{@name} can not programming(he/she is pseudo programming)"
  end
end


programmers = Programmers.new([Programmer.new('tanaka'), Programmer.new('sato')])

['tanaka', 'sato', 'suzuki'].each do |person|
  programmer = 
    if programmers.programmers.any? { |v|v.name == person }
      programmers.programmers.find { |v|v.name == person }
    else
      PseudoProgrammer.new(person)
    end

  puts programmer.programming
end

出力

tanaka is programming..
sato is programming..
suzuki can not programming(he/she is pseudo programming)