概要
継承や Mix-in を利用する際に、特定のメソッドの override を強制したいな。
そのような場合は NotImplementedError を利用すると便利です。
1. 継承の場合
class Mage
attr_reader :name
def initialize(name)
@name = name
end
def spell
"#{job} の #{name} は #{magic} を唱えた!"
end
private
# サブクラスで必ず実装しておいて欲しい。
def job
raise NotImplementedError.new("You must implement #{self.class}##{__method__}")
end
# サブクラスで必ず実装しておいて欲しい。
def magic
raise NotImplementedError.new("You must implement #{self.class}##{__method__}")
end
end
class BlackMage < Mage
private
def job
"黒魔道士"
end
end
class WhiteMage < Mage
private
def magic
"ケアル"
end
end
class BlueMage < Mage
private
def job
"青魔道士"
end
def magic
"マイティガード"
end
end
BlackMage.new("ビビ").spell
#=> NotImplementedError: You must implement BlackMage#magic
WhiteMage.new("エーコ").spell
#=> NotImplementedError: You must implement WhiteMage#job
BlueMage.new("クイナ").spell
#=> "青魔道士 の クイナ は マイティガード を唱えた!"
2. Mix-in の場合
module Mage
attr_reader :name
def initialize(name)
@name = name
end
def spell
"#{job} の #{name} は #{magic} を唱えた!"
end
private
# Mix-in する Class で必ず実装しておいて欲しい。
def job
raise NotImplementedError.new("You must implement #{self.class}##{__method__}")
end
# Mix-in する Class で必ず実装しておいて欲しい。
def magic
raise NotImplementedError.new("You must implement #{self.class}##{__method__}")
end
end
class BlackMage
include Mage
private
def job
"黒魔道士"
end
end
class WhiteMage
include Mage
private
def magic
"ケアル"
end
end
class BlueMage
include Mage
private
def job
"青魔道士"
end
def magic
"マイティガード"
end
end
BlackMage.new("ビビ").spell
#=> NotImplementedError: You must implement BlackMage#magic
WhiteMage.new("エーコ").spell
#=> NotImplementedError: You must implement WhiteMage#job
BlueMage.new("クイナ").spell
#=> "青魔道士 の クイナ は マイティガード を唱えた!"
このように実装しておけば、
未実装の場合にメソッドを呼び出した際には NotImplementedError が発生し、
例外のメッセージで実装すべきインスタンスメソッドを知らせてくれるので便利です。
3. 発展: クラスアノテーション化する
複数のメソッドに
raise NotImplementedError.new("You must implement #{self.class}##{__method__}")
と書くのは面倒です。
そこで クラスアノテーション として宣言できるようにしてみました。
module NecessaryToOverride
# 必ず Override すべきメソッドを列挙する。
def necessary_to_override(*methods)
methods.each do |method|
define_method(method) do |*_args|
raise NotImplementedError.new("You must implement #{self.class}##{__method__}")
end
end
end
end
Module.include(NecessaryToOverride)
以下のように利用します。
3-1. 継承の場合
class Mage
# サブクラスで job と magic というメソッドを必ず実装しておいて欲しい。
necessary_to_override :job, :magic
attr_reader :name
def initialize(name)
@name = name
end
def spell
"#{job} の #{name} は #{magic} を唱えた!"
end
end
class BlackMage < Mage
private
def magic
"ファイア"
end
end
BlackMage.new("ビビ").spell
#=> NotImplementedError: You must implement BlackMage#job
3-2. Mix-in の場合
module Mage
# Mix-in する Class で job と magic というメソッドを必ず実装しておいて欲しい。
necessary_to_override :job, :magic
attr_reader :name
def initialize(name)
@name = name
end
def spell
"#{job} の #{name} は #{magic} を唱えた!"
end
end
class WhiteMage
include Mage
private
def job
"白魔道士"
end
end
WhiteMage.new("エーコ").spell
#=> NotImplementedError: You must implement WhiteMage#magic
参考
-
Ruby Patterns
- 「Overrideしてほしいメソッドの実装にNotImplementedErrorを利用する」の節