Ruby

メソッドの override を強制する

More than 3 years have passed since last update.


概要

継承や 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を利用する」の節