旧gaaamiiのブログ

間違ったことを書いている時があります。コメントやTwitter、ブコメなどでご指摘ください

【Ruby】接客と委譲

(さらに追記)不十分なところを指摘していただきました!ありがとうございます!

nomnel.net

(追記)オープンクラスの理解が間違っていたためタイトルを変更しました。今回あげている例はNoMethodErrorを起こさずに他のオブジェクトに処理を委譲する流れを説明したものです。

パーフェクトRubyの7章で、存在しないメソッドを呼び出したときにNoMethodErrorを発生させる代わりに他のオブジェクトに処理を任せる、という内容の節がありました。これを接客業に当てはめて説明してみたくなったので、ブログ記事にしています。

接客でやってはいけないこと

派遣バイトとしてコンサート会場などの案内スタッフになると、業務前にほぼ必ず以下のような説明を受けます。

みなさんは本日、◯◯(イベント会社)のスタッフです。そのためお客様に対して「わからない」と言ってはいけません。必ず、「お待ちください」と伝えて、近くのチーフに尋ねてください

接客において、お客様に対してわからないと言ってはいけないのは基本です。しかしわからないものはわかりません。会場に慣れていない僕らのような日雇いアルバイトは、現場をよく知るチーフに確認をすることで解決します。

日雇いバイトは業務をチーフへ委譲することでエラーを回避することができるのです。

委譲をコードで書く

まず、チーフはお客さんの質問に答える能力があります。それはもう完璧な回答をしてくれます。

(staff.rbというファイルにコードを書いていきます)

class Chief
  def initialize
    @info = { "トイレ" => "2F東3扉前", "喫煙所" => "1F西門外"}
  end
  def answer(target)
    puts "#{target}#{@info[target]}にございます。"
  end
end

日雇いバイトは、今日来たばっかなので会場のことを何も知りません。ただ、自分が所属するチーフの情報は持っています。ああ、あの口が悪くてタバコくせえ人か、ということがわかっています。

class PartTimer
  def initialize
    @chief = Chief.new
  end
end

この状態でバイトがお客さんに質問され、答えようとしても、あたふたしてエラーとなってしまいます。

undefined method 'answer' for #<PartTimer:0x007fe22b088d00 @chief=#<Chief:0x007fe22b088cd8>> (NoMethodError)

これでは困ります。業務説明を思い出してみましょう。

そのためお客様に対して「わからない」と言ってはいけません。必ず、「お待ちください」と伝えて、近くのチーフに尋ねてください

なるほど、わからなければチーフに尋ねればいいのか。

class PartTimer
  def initialize
    @chief = Chief.new
  end
  def method_missing(name, *args)
    @chief.__send__ name, *args
  end
end

これで、バイトくんはお客さんに「わからない」などと答えずに、チーフへ業務を投げ渡すことができるようになります。追加したコードが何をしているのかを以下で詳しく見ていきます。

BasicObject#method_missingを上書き

def method_missing(name, *args)

method_missingはBasicObjectに定義されている、メソッドが無かった時の振る舞いを決めるものです。本来なら先ほどのようにNoMethodErrorが発生します。ここではエラーを発生させずにチーフに尋ねて欲しいので、メソッドがない場合の振る舞いを上書きしています。こうやって定義済みのクラスを上書きできることを、Rubyではオープンクラスと呼びます。 (追記)ここでは定義済みのクラスBasicObjectではなくそれを継承したPartTimerクラスのメソッドを上書きしているだけなので、オープンクラスの説明として不適切でした。

Object#__send__で業務を委譲

@chief.__send__ name, *args

上書きしたmethod_missingの中で、バイトくん(PartTimerオブジェクト)がインスタンス変数に持っているチーフへと、回答という業務を委譲するようにしています。

(追記)nameはメソッドの名前、*argsはメソッドに渡す引数です。PartTimer.new.answer("トイレ")でエラーが発生したとすると、nameには:answerが、*argsには"トイレ"が入ります。*がついていると可変長引数なので、メソッドの引数が2つでも3つの場合でも対応できるようになっています。

ここで実際にバイトくん(ここではtanakaとします)にanswerしてもらいましょう。ちゃんと答えが返ってくるでしょうか。

tanaka = PartTimer.new
tanaka.answer("トイレ")
tanaka.answer("喫煙所")

トイレと喫煙所の場所を尋ね(引数に渡し)ます。

$ ruby staff.rb
トイレは2F東3扉前にございます。
喫煙所は1F西門外にございます。

tanakaくんはanswerメソッドを持たないのに、きっちりと答えが返ってきました。チーフへの委譲は上手くいったようです。


何を伝えたいのかわかりづらくなってしまいましたが、ポイントはmethod_missingを上書きしているというところです。