概要
Yield Status Object
前提
Confident Rubyではメソッド内の処理を次のように分類しています。
・Collecting Inputs(引数チェック、変換など)
・Performing Work(主処理)
・Delivering Outpu(戻り値に関わる処理)
・Handling Failure(例外処理)
当記事は上記のうち、Delivering Outputに関する話です。
詳細
状況
コマンドメソッドが成功・失敗以上の成果物を欲しがるかもしれない。
また、その値を戻したくない。
概要
コールバック形式のStatusオブジェクトを利用したメソッドの成果物を表す。
理由
このアプローチは、成果物を与えるために何をすべきか綺麗に分かれる。
また、Return Status Objectと異なりCommand/Query Separation principleにも反していない。
サンプルコード仕様
信号を表すクラスSignalを作成します。
Signal#signal_statusでランダムに信号の状態を返却します。
サンプル1
SignalStatusクラスを作成します。信号の状態を保持するとともに、
・信号の状態確認用メソッド(SignalStatus#red?, SignalStatus#green?, SignalStatus#vanished?)
・信号の状態別用インスタンス取得メソッド(SignalStatus.red, SignalStatus.green, SignalStatus.vanished)
を持ちます。
Signal#signal_statusの戻り値をSignalStatusクラスの各状態別インスタンスで返却します。
この場合、利用側はSignalStatusが外部公開している振る舞いを元に条件を記述することができます。
サンプル2
・SignalStatusクラスにイベント処理用のメソッドon_red,on_green,on_vanishedを追加します。
・Signal#signal_statusの戻り値の部分をyieldに変更します。
サンプルコードその1
require 'pp' module Traffic class SignalStatus RED = "赤" GREEN = "青" VANISHED = "消灯" attr_reader :color def self.green new(GREEN) end def self.red new(RED) end def self.vanished new(VANISHED) end def initialize(color) @color = color end def green? @color == GREEN end def red? @color == RED end def vanished? @color == VANISHED end end class Signal def self.signal_status begin case signal_random when 1 SignalStatus.red when 2 SignalStatus.green end rescue => e return SignalStatus.vanished end end def self.signal_random ret = rand(3).to_i + 1 raise 'invalid' if ret == 3 ret end end end 10.times do signal_status = Traffic::Signal.signal_status if signal_status.green? puts "信号が青なので渡ります" elsif signal_status.red? puts "信号が赤なので待ちます" elsif signal_status.vanished? puts "信号がついていないが渡ります" end end
出力(ランダム)
信号がついていないが渡ります 信号が青なので渡ります 信号がついていないが渡ります 信号が青なので渡ります 信号が青なので渡ります 信号が青なので渡ります 信号がついていないが渡ります 信号が青なので渡ります 信号がついていないが渡ります 信号が赤なので待ちます
サンプルコードその2
require 'pp' module Traffic class SignalStatus RED = "赤" GREEN = "青" VANISHED = "消灯" attr_reader :color def self.green new(GREEN) end def self.red new(RED) end def self.vanished new(VANISHED) end def initialize(color) @color = color end def on_green yield if @color == GREEN end def on_red yield if @color == RED end def on_vanished yield if @color == VANISHED end end class Signal def self.signal_status begin case signal_random when 1 yield SignalStatus.red when 2 yield SignalStatus.green end rescue => e yield SignalStatus.vanished end end def self.signal_random ret = rand(3).to_i + 1 raise 'invalid' if ret == 3 ret end end end 10.times do Traffic::Signal.signal_status do |result| result.on_green { puts "信号が青なので渡ります" } result.on_red { puts "信号が赤なので待ちます" } result.on_vanished { puts "信号がついていないが渡ります" } end end
出力(ランダム)
信号が青なので渡ります 信号が赤なので待ちます 信号がついていないが渡ります 信号が青なので渡ります 信号がついていないが渡ります 信号が赤なので待ちます 信号がついていないが渡ります 信号がついていないが渡ります 信号が青なので渡ります 信号がついていないが渡ります