前節で組み込みの例外クラスを紹介しましたが、必要ならば、自分で例外クラスを作成することもできます。exception01.rbに、例外クラスを作成する例を示します。
class UnacceptableRequidError < StandardError attr_reader :obj def initialize(obj) @obj = obj end end class Vessel def pour_out requid = @requid @requid = nil requid end def pour_in(requid) if requid.to_s == acceptance @requid = requid else raise UnacceptableRequidError.new(requid), "unacceptable" end end def acceptance raise NotImplementedError.new, "#acceptance is not implemented!" end end class Teapot < Vessel def initialize(requid) pour_in(requid) end def acceptance "tea" end end class Decanter < Vessel def initialize(requid) pour_in(requid) end def acceptance "wine" end end class Kettle < Vessel def initialize(requid) pour_in(requid) end end
このコードは、容器を表すクラス「Vessel」と、Vesselを継承した「Teapot」(お茶を入れる容器)と「Decanter」(ワインなどを入れる容器)といったクラスを定義し、それを利用する例となっています。クラスについて復習の意味も兼ねて、少し複雑な例にしてみました。
Teapotにはお茶しか入れることはできませんし、Decanterにはワインしか入れることができません(できないと思ってください!)。なので、クラスの利用者が許容できない液体を入れようとした場合は、例外を発生させて利用者に知らせるものとしましょう。
では、exception01.rbと同じディレクトリでpryを起動して、定義した各クラスを使ってみましょう。
[1] pry(main)> require_relative "exception01" => true [2] pry(main)> teapot = Teapot.new("tea") => #<Teapot:0x007faa8dacc008 @requid="tea"> [3] pry(main)> teapot.pour_out => "tea" [4] pry(main)> teapot.pour_out => nil [5] pry(main)> teapot.pour_in("coffee") UnacceptableRequidError: unacceptable from /Users/flost/work/ruby_rails_tutorial/ruby_21_guide/09/exception01.rb:20:in `pour_in' [6] pry(main)> decanter = Decanter.new("wine") => #<Decanter:0x007faa8d99fce8 @requid="wine"> [7] pry(main)> decanter.pour_in("beer") UnacceptableRequidError: unacceptable from /Users/flost/work/ruby_rails_tutorial/ruby_21_guide/09/exception01.rb:20:in `pour_in'
[1]では、require_relativeを使って、exception01.rbを読み込んでいます。この1行を実行することで、exception01.rbの中で定義されたクラスを使うことができます。
[2]では、Teapotクラスのオブジェクトを作成し、初期値として「tea」を与えています。Teapotは「tea」を許容するので、無事オブジェクトが生成され、[3]でteapotに格納されている「tea」を取り出せます。また、一度取り出されると中身がnilになるので、[4]では返り値がnilとなっています。[5]では「coffee」をteapotに格納しようとしていますが、許容できないのでUnacceptableRequidErrorが発生しています。
[6]から[7]は、Decanterの利用例です。[6]では「wine」を初期値としてDecanterオブジェクトを生成できていますが、「beer」は許容できないので、[7]でUnacceptableRequidErrorが発生しています。
exeption01.rbは1〜7行目で、UnacceptableRequidErrorという例外を定義しています。4行目から6行目のコンストラクタで、UnacceptableRequidErrorのオブジェクト変数に、クラスの利用者が入れようとしたオブジェクトを格納しておきます(格納したオブジェクトの利用例は、次節の例外の捕捉で説明します)。
9〜23行目はVesselクラスの定義です。Vesselクラスは三つのメソッドpour_outとpour_inとacceptanceを持っており、pour_outは格納された液体を外に注ぎ、pour_inは外から液体を注ぐためのメソッドです。また、acceptanceは許容する液体を示す文字列を返すためのメソッドです。
pour_outはインスタンス変数@requidに格納された液体をローカル変数requidにいったん移し、@requidにnilを代入し、一時的に移しておいたrequidの内容を返します。この一連の動作によって、「容器の中身を外に移して空(nil)になった」ということを表しています。
pour_inは引数に与えられたオブジェクトを、acceptanceメソッドの返すオブジェクトと付き合わせて、同じならインスタンス変数@requidに格納し、違えばUnacceptableRequidError例外を発生させます。TeapotやDecanterといった派生クラスでacceptanceをオーバーライドすることによって、派生クラスの実装次第で許容する液体を変えることができます。
ここで、Vesselクラスでのacceptanceの定義に注目してください。25行目でNotImplementedErrorという例外を発生させています。これは、Rubyのクラス設計でよく使われるテクニックです。
もし派生クラスでacceptanceメソッドがオーバーライドされていなければ、基底クラスVesselのacceptanceメソッドが呼ばれます。このとき、NotImplementedError、つまり「メソッドが実装されていませんよ」という例外が発生するので、クラスの利用者にacceptanceメソッドを実装するように促せます。
サンプルコードではKettleクラスがacceptanceメソッドをオーバーライドしていないので、NotImplementedErrorが発生します。
[8] pry(main)> Kettle.new("water") NotImplementedError: #acceptance is not implemented! from /Users/flost/work/ruby_rails_tutorial/ruby_21_guide/09/exception01.rb:25:in `acceptance'
Copyright © ITmedia, Inc. All Rights Reserved.