110
84

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

メソッドの override を強制する

Last updated at Posted at 2014-09-12

概要

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

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
110
84

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?