Javaなどオブジェクト指向の言語にある抽象クラスですがRubyには実装されていません。他言語からRubyに移行された方には使い慣れた抽象クラスがほしいというのが実情でしょう。今回はRubyでも実現可能な疑似抽象クラスについて作成事例も含め紹介しています。
Rubyは抽象クラスがない
RubyにはJavaなど他のオブジェクト言語にはある抽象クラスがありません。以降、抽象クラスがない理由と対策について考えていきます。
そもそも抽象クラスって何?
オブジェクト指向で言う抽象を説明すると、類似する事象から重要な部分を抜き出した汎用的なモデルです。抽象クラスも同じく類似する機能から共通する部分(名称や引数、戻り値など)を抜き出して定義したクラスです。抽象クラスには定義のみ記で述します。実装は派生したサブクラスで行います。
抽象クラスの抽象メソッドをサブクラスで実装(メソッドの内容を記述)することをオーバーライドといいます。抽象クラスのようにひとつのクラスが多くの用途に柔軟に対応する仕組みをデザインパターンでは多様性(ポリモーフィズム)と言います。
「Template Method」は抽象クラスを取り入れたデザインパターンです。「Template Method」では親クラスで処理の枠組みを決めます。具体的な処理の内容は派生したサブクラスで行います。良く使うパターンなので覚えておくとよいでしょう。
Javaなどの言語にはインターフェイスという機能もある
抽象クラスと類似した機能にインターフェイスがあります。インターフェイスはオブジェクト間のやりとりを共通化するのが目的です。
インターフェイスもRubyの機能には含まれていません。抽象クラスとインターフェイスでは構成は類似しますが、使用目的が違います。抽象クラスが処理の骨組みを強制するのに対して、インターフェイスが行うのは「ふるまい」の共通化です。
インターフェイスの存在意義についてJavaではインターフェイスはアクセス修飾子が全てpublicに強制されるので外部向けの実装を強制可能な事や、単一継承が仕様のJavaにおいてインターフェイスでは多重継承が可能などのメリットがありますが、Rubyについては疑似抽象クラスとインターフェイスの違いを意識する必要はないでしょう。
抽象クラスを使うのはどんな時か
抽象クラスの役割は開発の際の共通ルールを決めることです。ある程度以上の大きなプロジェクトでは共通のルールを明確にしてルールに則った開発を進める必要があります。
抽象クラスでは実際に作り込みを開始する前に実装するメソッドの種類や名称などを定義しておくことが可能です。以降の開発では事前に定義した抽象クラスを継承し実装していきます。
抽象クラスの特徴としては継承したサブクラスの実装の強制です。抽象クラスで抽象メソッドとして定義したメソッドをサブクラスで実装をしない場合エラーを出力します。この強制が派生するサブクラスの実装の際のルール(制約)となります。また抽象メソッド以外のメソッドは共通メソッドとして派生したサブクラス間で共有します。
なぜRubyには抽象クラスがないのか
Rubyの設計思想には「自由であること」、「柔軟であること」という概念があります。Rubyでは制限を設ける役割である抽象クラスは設計思想に相容れないとしてRubyの機能には取り込みませんでした。機能による制限ではなくコーディングの時には各人が注意して開発してくださいねという考えです。
またRuby設計思想を実現するには少人数(理想は一人)で開発をするのが望ましいとしています。抽象クラスが必要とされるのはある程度の大きさのプロジェクトですから少数先鋭を想定したRubyでは必要ないとの判断です。
しかしRubyも時代の流れとともに活躍の場も拡がっています。特に「Ruby on Rails」の登場によりスマフォから大規模プロジェクトまで多岐に渡っています。大規模プロジェクトでも使われるようになったRubyには抽象クラスの必要性は増してきたといえるでしょう。
Rubyでも疑似的な抽象クラスは作れる
抽象クラスはRubyの機能に含まれませんが、例外処理(raise)を使用することにより疑似抽象クラスとして実装が可能です。
Rubyで抽象クラスを作る方法
Rubyによる疑似抽象クラスの実例を紹介します。サンプルソースはシンプルな構成ですので実際に動かして試して見てください。
例外処理(raise)を使用して疑似クラスを再現する
例外処理のみを記述した「abstract method」を定義しよう
抽象クラスには抽象メソッドが一つ以上必要です。抽象メソッドとして定義するメソッドに例外処理(raise)のみ記述しましょう。これにより派生したサブクラスにて抽象メソッドのの実装漏れがあった場合、記述した例外処理を実行します。
引用したサンプルソースでは「puts_string」を抽象メソッドとして定義しています。「SuperClassA」では「puts_string」をオーバライドしているので正常終了します。もうひとつの「SuperClassB」は オーバライドの記述がないため抽象メソッドに記述された例外処理(raise)を実行しエラーを出力しています。
# -*- coding: utf-8 -*-
class AbstractClass
def my_print
puts_string
end
# 抽象メソッド
def puts_string
raise "Called abstract method: my_print"
end
end
class SuperClassA < AbstractClass
def puts_string
puts "a"
end
end
class SuperClassB < AbstractClass
def print_string
print "a\n"
end
end
c = SuperClassA.new
c.my_print
c = SuperClassB.new
c.my_print
今回紹介した疑似抽象クラスの実例では対象メソッドに例外処理を書くことで実装可能なシンプルな構成です。実装する敷居は高くはないので実際にサンプルを動かして試してみてください。Rubyによる擬似抽象クラスの紹介はネットを検索するとたくさんでてきます。
他のページも参考にしながら疑似抽象クラスについての理解を深めていくと良いでしょう。今回紹介した抽象化について、今後のRubyの開発に積極的に取り入れより柔軟な設計を目指しましょう。