こんにちは!じゃいごテックのあつしです。
Rubyでプログラミングの基礎を学習した後は、アプリ開発などに挑戦するかと思いますが、その際、オブジェクト指向プログラミングが必要になってきます。
最初は少し難しく感じるかもしれませんが、慣れてくると(ある程度)自然にオブジェクト指向で考えて書けるようになってきます。
Rubyもオブジェクト指向プログラミング言語ですので、私が説明できる範囲の基本的なことを紹介していきたいと思います。
目次
オブジェクト指向プログラミングとは
オブジェクト指向プログラミングとは、プログラム全体を「ある役割を持ったオブジェクト(モノ)」ごとに分割して、モノに命令を与えたり、モノとモノを相互に作用させることによって、プログラムを構築していく手法です。
モノの設計図のことをクラスと言い、クラスから生み出された実体(モノ)をインスタンスと言います。
クラス
Rubyでは全てのオブジェクトは必ず何らかのクラスに属しています。
クラスはプロパティ(属性・データ)とメソッド(命令)をまとめた設計図のようなもので、その設計図によって実体化されたオブジェクト(モノ)に命令することでプログラムを進めていきます。
# 123 はIntegerクラス 123.class # > Integer # Integerクラスに定義されているgcdメソッドを呼び出す # gcdメソッドは自身と引数の最大公約数を返す p 123.gcd(456) # > 3 # "abc" はStringクラス "abc".class # > String # Stringクラスに定義されているupcaseメソッドを呼び出す # upcaseメソッドは自身を大文字に変換した文字列を返す p "abc".upcase # > "ABC" # [1, 2, 3, "a", "b", "c"] はArrayクラス # classメソッドは自身のクラスを返す。 [1, 2, 3, "a", "b", "c"].class # > Array # Arrayクラスに定義されているlengthメソッドを呼び出す # lengthメソッドは自身の要素数を返す [1, 2, 3, "a", "b", "c"].length # > 6
クラスの定義
クラス名はアッパーキャメルケース(UpperCamelCase)キャメルケース(CamelCase)で命名し、class クラス名 ~ end
の間に処理を記述していきます。
class クラス名
・
・
end
試しにAppleクラスを作ってみます
# Appleクラスを定義(中身は空っぽ) class Apple end
インスタンス
インスタンスとは、設計図であるクラスを元にして実体化させたモノです。
インスタンスを生成することで、クラスで設計されたプロパティやメソッドが使えるようになります。
クラスのインスタンス化
クラス名.new(パラメータ)
クラスはインスタンス化することにより、定義しておいたプロパティやメソッドが利用できるようになります。
class Apple end # インスタンス化して 変数 ringo に格納 ringo = Apple.new() # ringo は Appleクラス のインスタンス p ringo # > #<Apple:0x00007fffc5c5ff68> p ringo.class # > Apple
Appleクラスの中は空っぽなので何も命令することが出来ませんが、とりあえずインスタンス化することは出来ました。
※ Appleクラスは空のはずなのにclassメソッドが使える理由は次の記事で説明します。
インスタンス変数
インスタンス変数とはオブジェクト毎に個別で持つことが出来る変数で、@name
のように、変数名の先頭に@を付けます。
インスタンス変数の保存・更新
Appleクラスにデータを持たせてみましょう。
インスタンスに値を保存しておくためのインスタンス変数を定義して、値を格納するメソッドを作ります。
例としてインスタンス変数@name
を定義して、名前を保存出来るようにします。
class Apple # セッター def name=(name) @name = name end end # Appleクラスのインスタンスを生成する ringo = Apple.new p ringo # > #<Apple:0x00007fffeb78fb48> # インスタンス変数 @name に "ohrin" を保存する ringo.name = "ohrin" p ringo # > #<Apple:0x00007fffd6097968 @name="ohrin"> # インスタンス変数 @name を "toki" に変更して保存する ringo.name = "toki" p ringo # > #<Apple:0x00007fffd6097968 @name="toki">
このようにインスタンス変数を保存または更新するメソッドをセッターと言います。
インスタンス変数の参照
現在の状態ではAppleクラス
のインスタンス変数を直接参照することは出来ません。
class Apple # セッター def name=(name) @name = name end end # Appleクラスのインスタンスを生成する ringo = Apple.new p ringo # > #<Apple:0x00007fffeb78fb48> # インスタンス変数 @name に "ohrin" を保存する ringo.name = "ohrin" # インスタンス変数 @name を参照できない p ringo.name # > undefined method `name' # > for #<Apple:0x00007fffd84ff030 @name="ohrin"> (NoMethodError)
インスタンス変数を参照するメソッドを作ってみましょう。
class Apple # セッター def name=(name) @name = name end # ゲッター def name @name end end # Appleクラスのインスタンスを生成する ringo = Apple.new p ringo # > #<Apple:0x00007fffeb78fb48> # インスタンス変数 @name に "ohrin" を保存する ringo.name = "ohrin" # インスタンス変数 @name を参照する p ringo.name # > "ohrin"
このようにインスタンス変数を参照するメソッドをゲッターと言います。
アクセスメソッド
アクセスメソッドを使うと、ゲッターメソッド・セッターメソッドを記述しなくてもインスタンス変数の保存や参照を行うことが出来るようになります。
ゲッター(外部から参照が可能) | attr_reader :変数名1, :変数名2, ... |
セッター(外部から更新が可能) | attr_writer :変数名1, :変数名2, ... |
ゲッター&セッター(外部から参照と更新が可能) | attr_accessor :変数名1, 変数名2, ... |
@name
を外部から参照と更新できるようにする場合はattr_accessorメソッド
を使います。
class Apple # @name は外部から参照と更新を許可する attr_accessor :name end # Appleクラスのインスタンスを生成する ringo = Apple.new p ringo # > #<Apple:0x00007fffeb78fb48> # インスタンス変数 @name に "ohrin" を保存する ringo.name = "ohrin" # インスタンス変数 @name を参照する p ringo.name # > "ohrin"
initializeメソッド
def initialize(引数1, 引数2, ..)
.
.
end
initializeメソッドはオブジェクトがインスタンス化されたときに自動的に呼び出されるメソッドで、引数を与えて実行することが出来ます。
オブジェクト生成時に必ず実行したい処理(インスタンス変数の初期化など)を記述します。
Appleクラス
のインスタンス変数を増やして、複数のApplesクラス
のオブジェクトをインスタンス化して配列に格納してみます。
class Apple # @name と @color は外部からは参照を許可する attr_reader :name, :color # @sweetness と @sour は外部から参照と更新を許可する attr_accessor :sweetness, :sour # インスタンス化された時に引数の値をインスタンス変数に代入する def initialize(name, color, sweetness, sour) @name = name @color = color @sweetness = sweetness @sour = sour end end apple_list = [["トキ", "yellow", 5, 2], ["フジ", "red", 3, 3], ["王林", "green", 5, 1], ["紅玉", "red", 1, 5], ["陸奥", "red", 2, 3]] # apple_list のデータを使ってAppleクラスのインスタンスを生成する apples = apple_list.map do |name, color, sweetness, sour| Apple.new(name, color, sweetness, sour) end apples.each { |apple| p apple } # > #<Apple:0x00007ffff365b378 @name="トキ", @color="yellow", @sweetness=5, @sour=2> # > #<Apple:0x00007ffff365af40 @name="フジ", @color="red", @sweetness=3, @sour=3> # > #<Apple:0x00007ffff365a900 @name="王林", @color="green", @sweetness=5, @sour=1> # > #<Apple:0x00007ffff3658ce0 @name="紅玉", @color="red", @sweetness=1, @sour=5> # > #<Apple:0x00007ffff3658bf0 @name="陸奥", @color="red", @sweetness=2, @sour=3>
インスタンスメソッド
class クラス名
def メソッド名(引数1, 引数2, ..)
.
.
end
end
クラス内に記述されたメソッドは、そのクラスのインスタンスが呼び出すことが出来ます。
そのようなメソッドをインスタンスメソッドと言います。
Appleクラスに、りんごの情報を出力するinformationメソッド
を定義して呼び出してみます。
class Apple attr_reader :name, :color attr_accessor :sweetness, :sour def initialize(name, color, sweetness, sour) @name = name @color = color @sweetness = sweetness @sour = sour end # りんごの情報を出力するインスタンスメソッド def information "品種名:#{@name.ljust(4, " ")} 色:#{jcolor} " << "甘さ:#{@sweetness} 酸っぱさ:#{@sour}" end # 英語の色を日本語の色に変換するインスタンスメソッド def jcolor case @color when "red" "赤" when "green" "緑" when "yellow" "黄" else "他" end end end apple_list = [["トキ", "yellow", 5, 2], ["フジ", "red", 3, 3], ["王林", "green", 5, 1], ["紅玉", "red", 1, 5], ["陸奥", "red", 2, 3]] # Appleクラスのインスタンスを生成する apples = apple_list.map do |name, color, sweetness, sour| Apple.new(name, color, sweetness, sour) end # 各りんごの情報を表示する informationメソッド を呼び出す apples.each { |apple| puts apple.information } # > 品種名:トキ 色:黄 甘さ:5 酸っぱさ:2 # > 品種名:フジ 色:赤 甘さ:3 酸っぱさ:3 # > 品種名:王林 色:緑 甘さ:5 酸っぱさ:1 # > 品種名:紅玉 色:赤 甘さ:1 酸っぱさ:5 # > 品種名:陸奥 色:赤 甘さ:2 酸っぱさ:3 # 配列1番目(トキ)の色を参照する p apples[0].color # > "yellow" p apples[0].jcolor # > "黄"
privateメソッド
クラス外から呼び出されたくないメソッドを定義したい場合はprivateメソッド
を使用します。
jcolorメソッド
を外部から呼び出されたくない場合、以下の様に記述します。
class Apple attr_reader :name, :color attr_accessor :sweetness, :sour def initialize(name, color, sweetness, sour) @name = name @color = color @sweetness = sweetness @sour = sour end # りんごの情報を出力するインスタンスメソッド def information "品種名:#{@name.ljust(4, " ")} 色:#{jcolor} " << "甘さ:#{@sweetness} 酸っぱさ:#{@sour}" end # private 以降のメソッドは外部から呼び出せない private # 英語の色を日本語の色に変換するインスタンスメソッド def jcolor case @color when "red" "赤" when "green" "緑" when "yellow" "黄" else "他" end end def example_method1 "example1" end def example_method2 "example2" end end apple_list = [["トキ", "yellow", 5, 2], ["フジ", "red", 3, 3], ["王林", "green", 5, 1], ["紅玉", "red", 1, 5], ["陸奥", "red", 2, 3]] # Appleクラスのインスタンスを生成する apples = apple_list.map do |name, color, sweetness, sour| Apple.new(name, color, sweetness, sour) end # private 以降の行で定義したメソッドは外部から呼び出すことが出来ない p apples[0].jcolor # > private method `jcolor' called # > for #<Apple:0x00007fffc4ccf648> (NoMethodError) p apples[0].example_method1 # > private method `example_method1' called # > for #<Apple:0x00007fffe0146ee0> (NoMethodError) p apples[0].example_method2 # > private method `example_method2' called # > for #<Apple:0x00007fffd65371d0> (NoMethodError)
クラス変数
クラスのインスタンス全てで共有する変数をクラス変数と言います。
@@変数名
で定義し、インスタンスメソッドなどで利用することが出来ます。
class Apple attr_reader :name, :color attr_accessor :sweetness, :sour # クラス変数で通し番号を管理する @@counter = 1 def initialize(name, color, sweetness, sour) # インスタンスに通し番号を振る @id = @@counter # クラス変数 @@counter をインクリメントする @@counter += 1 @name = name @color = color @sweetness = sweetness @sour = sour end def information "品種番号:#{"%2d" % @id} 品種名:#{@name.ljust(4, " ")} 色:#{jcolor} " << "甘さ:#{@sweetness} 酸っぱさ:#{@sour}" end private def jcolor case @color when "red" "赤" when "green" "緑" when "yellow" "黄" else "他" end end end apple_list = [["トキ", "yellow", 5, 2], ["フジ", "red", 3, 3], ["王林", "green", 5, 1], ["紅玉", "red", 1, 5], ["陸奥", "red", 2, 3]] # Appleクラスのインスタンスを生成する apples = apple_list.map do |name, color, sweetness, sour| Apple.new(name, color, sweetness, sour) end # 品種番号 @id も含めたりんごの情報を表示する apples.each { |apple| puts apple.information } # > 品種番号: 1 品種名:トキ 色:黄 甘さ:5 酸っぱさ:2 # > 品種番号: 2 品種名:フジ 色:赤 甘さ:3 酸っぱさ:3 # > 品種番号: 3 品種名:王林 色:緑 甘さ:5 酸っぱさ:1 # > 品種番号: 4 品種名:紅玉 色:赤 甘さ:1 酸っぱさ:5 # > 品種番号: 5 品種名:陸奥 色:赤 甘さ:2 酸っぱさ:3
定数
クラス内で定数を定義することも出来ます。
アルファベット大文字で始まる定数名を定義して、変更することが出来ない値を格納する事ができ、外部からはクラス名::定数名
で参照することができます。
今回はjcolorメソッド
を削除して、英語と日本語の色の対応JCOLOR
を定数にします。
※ 今までは色が"other"に該当する品種がありませんでしたが、陸奥は赤も黄色もあるので"other"に変更します。
class Apple attr_reader :name, :color attr_accessor :sweetness, :sour # 英語と日本語の対応を定数にする JCOLOR = { "red" => "赤", "green" => "緑", "yellow" => "黄", "other" => "他" } # クラス変数で通し番号を管理する @@counter = 1 def initialize(name, color, sweetness, sour) @id = @@counter @@counter += 1 @name = name @color = color @sweetness = sweetness @sour = sour end def information "品種番号:#{"%2d" % @id} 品種名:#{@name.ljust(4, " ")} 色:#{JCOLOR[@color]} " << "甘さ:#{@sweetness} 酸っぱさ:#{@sour}" end end apple_list = [["トキ", "yellow", 5, 2], ["フジ", "red", 3, 3], ["王林", "green", 5, 1], ["紅玉", "red", 1, 5], ["陸奥", "other", 2, 3]] # Appleクラスのインスタンスを生成する apples = apple_list.map do |name, color, sweetness, sour| Apple.new(name, color, sweetness, sour) end # りんごの情報を表示する apples.each { |apple| puts apple.information } # > 品種番号: 1 品種名:トキ 色:黄 甘さ:5 酸っぱさ:2 # > 品種番号: 2 品種名:フジ 色:赤 甘さ:3 酸っぱさ:3 # > 品種番号: 3 品種名:王林 色:緑 甘さ:5 酸っぱさ:1 # > 品種番号: 4 品種名:紅玉 色:赤 甘さ:1 酸っぱさ:5 # > 品種番号: 5 品種名:陸奥 色:他 甘さ:2 酸っぱさ:3 # Appleクラスの 定数JCOLOR を参照 p Apple::JCOLOR # > {"red"=>"赤", "green"=>"緑", "yellow"=>"黄", "other"=>"他"} # 標準モジュールMathクラスの 定数PI を参照 p Math::PI # > 3.141592653589793
特異メソッド(クラスメソッド)
class クラス名
def self.メソッド名
.
.
End
end
又は
class クラス名
def クラス名.メソッド名
.
.
end
end
クラス直下で頭にselfキーワード(またはクラス名)を付けてメソッドを定義することでクラスにメソッドを紐づけることができます。このようなメソッドを特異メソッドと言いい、インスタンスや外部から呼び出すことができます。
なお、Rubyリファレンスマニュアルによると「特異メソッドは他のオブジェクト指向システムにおけるクラスメソッドの働きをする」とのことでしたので、普段は特異メソッドのことをクラスメソッドと呼ぶことにします。
Appleクラスにクラス変数@@counter
を参照するクラスメソッドget_counter
を実装してみる。
class Apple attr_reader :name, :color attr_accessor :sweetness, :sour # 英語と日本語の対応を定数にする JCOLOR = { "red" => "赤", "green" => "緑", "yellow" => "黄", "other" => "他" } @@counter = 1 # @@counter を参照するクラスメソッド def self.get_counter @@counter end def initialize(name, color, sweetness, sour) @id = @@counter @@counter += 1 @name = name @color = color @sweetness = sweetness @sour = sour end def information "品種番号:#{"%2d" % @id} 品種名:#{@name.ljust(4, " ")} 色:#{JCOLOR[@color]} " << "甘さ:#{@sweetness} 酸っぱさ:#{@sour}" end end apple_list = [["トキ", "yellow", 5, 2], ["フジ", "red", 3, 3], ["王林", "green", 5, 1], ["紅玉", "red", 1, 5], ["陸奥", "other", 2, 3]] # Appleクラスのインスタンスを生成する apples = apple_list.map do |name, color, sweetness, sour| Apple.new(name, color, sweetness, sour) end # クラス変数 @@counter を出力する p Apple.get_counter # > 6
今回のまとめ
今回はオブジェクト指向プログラミングの概念とクラスの基本をざっくりご紹介しました。
- オブジェクト指向プログラミング(OOP)の概念
- クラスの定義
- インスタンスの生成
- インスタンス変数
- アクセスメソッド(セッター・ゲッター)
- initializeメソッド
- インスタンスメソッド
- privateメソッド
- クラス変数・定数
- 特異メソッド
Rubyの学習をしていると、いつかオブジェクト指向プログラミングにぶつかりますので、少しづつ慣れていきたいですね。
なお、マッキントッシュ (Macintosh)の由来となった、McIntoshの日本語品種名は旭(あさひ)です。(リンゴ小ネタ)