FC2ブログ

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

[Ruby]抽象メソッドを定義できるようにするGem、abstractableを作りました

Rubyは抽象メソッドをサポートしていませんが、こいつを使えば抽象メソッドを定義できるようになります。

使い方はこんな感じ。
require "abstractable"
class AbstractDriver
extend Abstractable
abstract :open, :close
end

class AbstractIODriver < AbstractDriver
abstract :read, :write
end

class NotImplIODriver < AbstractIODriver; end

NotImplIODriver.new
# => following abstract methods are not implemented. (NotImplementedError)
# [:open, :close] defined in AbstractDriver
# [:read, :write] defined in AbstractIODriver

概要


抽象メソッドを定義可能にします。

特徴は fail fast であること。

未実装の抽象メソッドがあることを、早いタイミングで知ることができます。
冒頭のサンプルを見てください。newの時点で例外が発生しています。
更に実装しなければならないメソッドがすべて例外メッセージに表示されています。
(クラスに対し未実装のメソッドを探す仕組みがありますので、Guardを使えば、ファイル保存のタイミングで
 未実装のメソッドがあることを伝えられそうです。)

ちなみにRubyGemsで「abstract」と検索すると見つかる同類のGemは、
newでは例外が発生せず、未実装の抽象メソッドを呼び出すと例外が投げられるような仕組みです。
(abstractableもクラスメソッドを抽象メソッドにした場合は、呼ぶまで例外は投げられません。)

インストール方法


gem install abstractableGithubのリポジトリはこちら

使い方


次のようにまず require "abstractable" して、
抽象メソッドを定義したいクラスにAbstractableモジュールをextendしてください。
これで抽象メソッドが定義できるようになります。
require "abstractable"
class AbstractClass
extend Abstractable
end
: このドキュメントではこれ以降、require "abstractable"の記述を省略します。

抽象メソッドを定義する方法

  1. abstractメソッドにシンボルを指定する(シンボルは複数指定可能)。class AbstractList
    extend Abstractable

    abstract :add, :insert, :empty?
    end
    メソッドの定義とabstractの指定を別々にすることも可。
    (シグニチャを明示したい場合には、こちらの方がよいでしょう。)
    class AbstractList
    extend Abstractable

    def add(value); end
    def insert(index, value); end
    def empty?; end

    abstract :add, :insert, :empty?
    end
  2. abstractメソッドのブロック内でメソッドを定義する
    こうするとブロック内で定義されたメソッドはすべて抽象メソッドとして扱われるようになります。class AbstractList
    extend Abstractable

    abstract do
    def add(value); end
    def insert(index, value); end
    def empty?; end
    end
    end

    次のように書くと、抽象メソッドであることを示すのとシグニチャの明示が同時にできます。class AbstractList
    extend Abstractable

    abstract { def add(value); end }
    abstract { def insert(index, value); end }
    abstract { def empty?; end }
    end
    (開発当初はあるトリッキーな方法を使って、 abstract def add(value); end のように
    書けるようにしようとしていたのですが、バグらせる方法が見つかったので取り止めにしました。)

抽象メソッドの取得

abstract_methodsメソッドは、レシーバに定義された抽象メソッドの名前を配列で返します。
この配列の中には親クラスの抽象メソッドも含まれます。
class AbstractDriver
extend Abstractable
abstract :open, :close
end

class AbstractIODriver < AbstractDriver
abstract :read, :write
end

AbstractIODriver.abstract_methods # => [:read, :write, :open, :close]
引数にfalseを指定した場合、レシーバに定義された抽象メソッドのみが返されます。AbstractIODriver.abstract_methods(false) # => [:read, :write]

抽象メソッドの定義解除

Moduleのundef_methodメソッドを呼び出すと抽象メソッドの定義を解除できます。
ただしレシーバは抽象メソッドを定義したクラスでなければなりません。
(サブクラスから親クラスの抽象メソッドの定義は解除できない)class Parent
extend Abstractable
abstract :greet
end

class Child < Parent
undef_method :greet
end

begin
Child.new # => NotImplementedError
rescue NotImplementedError
end

class Parent
undef_method :greet
end

Child.new # => OK

newする度に未実装のメソッドがあるかどうかのチェックが行われるのか?

いいえ。未実装のメソッドが存在しないことが確認できたら、
それ以降は新たな抽象メソッドが定義されない限りチェックは行われません。
現在チェックが必要な状態かどうかは、required_validate?メソッドを呼び出すことで知ることが出来ます。class AbstractParent
extend Abstractable
abstract :greet
end

class Child < AbstractParent
def greet; end
end

Child.required_validate? # => true
Child.new
Child.required_validate? # => false

# 新たな抽象メソッドを定義
AbstractParent.abstract :execute

Child.required_validate? # => true
環境変数 ABSTRACTABLE_IGNORE_VALIDATEが定義されている場合
この場合、required_validate?メソッドは常にtrueを返すようになります。
もうチェックは不要だ、という状況で使用する想定です。
ABSTRACTABLE_IGNORE_VALIDATEの値は何でもOKです(定義されているかどうかだけ見ているので)。

抽象クラスメソッド

クラスメソッドを抽象メソッドにする場合、newではチェックできないため、
実際呼び出すまで例外は投げられません。
ただし後述しますが、未実装のメソッドを探すことはできます。
class AbstractParent
extend Abstractable

class << self
extend Abstractable
abstract :greet
end
end

class AbstractChild < AbstractParent
class << self
abstract :say
end
end

class Child < AbstractChild; end

Child.greet # => greet is abstract method defined in
# #, and must implement.
# (NotImplementedError)
抽象クラスメソッドの取得
abstract_singleton_methodsで抽象クラスメソッドを取得することができます。Child.abstract_singleton_methods # => [:say, :greet]
Child.abstract_singleton_methods(false) # => []
AbstractChild.abstract_singleton_methods # => [:say, :greet]
AbstractChild.abstract_singleton_methods(false) # => [:say]

明示的にチェックを行う

validate_not_implemented_abstract_methodsメソッドを呼ぶことで、明示的にチェックを行うことができます。class AbstractParent
extend Abstractable
abstract :greet
end

class Child < AbstractParent; end

# 明示的にチェックを行う
Child.validate_not_implemented_abstract_methods
# => following abstract methods are not implemented. (NotImplementedError)
# [:greet] defined in AbstractParent

未実装のメソッドを探す

abstractableモジュールのfind_not_implemented_infoメソッドは、次の形式のHashを返します。
{未実装のメソッドを持っているクラス => 未実装メソッドの配列, ...}
class AbstractParent
extend Abstractable
abstract :greet
end

class Child < AbstractParent; end

Abstractable.find_not_implemented_info(Child) # => {AbstractParent=>[:greet]}
このメソッドの特異クラス版である find_not_implemented_info_from_singleton を使えば、
未実装のクラスメソッドを探すことが可能です。class AbstractParent
class << self
extend Abstractable
abstract :greet
end
end

class AbstractChild < AbstractParent
class << self
abstract :say
end
end

class Child < AbstractChild; end

Abstractable.find_not_implemented_info_from_singleton(Child)
# => {#<Class:AbstractParent>=>[:greet], #<Class:AbstractChild>=>[:say]}
関連記事
スポンサーサイト

コメントの投稿

非公開コメント

プロフィール

Kenji Suzuki

Author:Kenji Suzuki
IT技術に関するあれこれを書いているブログです。
Pujohead Softの方では開発したソフトを公開しています。

最新記事
最新コメント
最新トラックバック
月別アーカイブ
カテゴリ
タグ

プログラミング デザインパターン コードリーディング bat 

検索フォーム
RSSリンクの表示
リンク
ブロとも申請フォーム

この人とブロともになる

QRコード
QR
メールフォーム

名前:
メール:
件名:
本文:

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。