Rubyオブジェクトモデルを詳細に理解する
ウィキペディアによると、オブジェクト指向プログラミング(OOP)は、「オブジェクト」の概念に基づくプログラミングパラダイムであり、データとコードを含めることができます。フィールド形式のデータ(多くの場合、属性またはプロパティと呼ばれます)と形式のコードです。手順の(多くの場合、メソッドとして知られています)。
Rubyは純粋なオブジェクト指向言語です。つまり、Ruby言語では、すべてがオブジェクトです。これらのオブジェクトは、文字列、数値、クラス、モジュールなどであるかどうかに関係なく、オブジェクトモデルと呼ばれるシステムで動作します。 。
Rubyはobject_id
と呼ばれるメソッドを提供しています 、すべてのオブジェクトで使用できます。この識別子は整数を返し、2つのオブジェクトで同じになることはありません。手を汚すためにirbに入りましょう。これを行うには、irb
と入力します ターミナルで。
上記のように、文字列、整数、配列、クラス、さらにはメソッドでさえ、オブジェクトIDを持っているため、すべてオブジェクトです。
この記事では、次の概念について詳しく説明します。
- クラスとインスタンス
- 継承
- パブリック、プライベート、および保護されたメソッド
- ミックスイン
- モジュール
- オブジェクト階層
Rubyでは、クラスはオブジェクトの属性と態度(アクション)が定義される場所です。オブジェクトが丸く(属性)、話すことができる(アクション)必要がある場合、これらの属性とアクションはメソッドと呼ばれるものとして定義されているため、オブジェクトが属するクラスからわかります。 そのクラス内。クラスに属するオブジェクトは、インスタンスと呼ばれます。 そのクラスの、.new
を使用して作成(インスタンス化)されます 。 Human
というクラスを作成することから始めましょう 。私たちは皆人間だと思うので、これは楽しいはずです。
class Human
def initialize(name)
@name = name
end
end
初期化 メソッドは、作成されるクラスの新しいインスタンスの要件を識別します。上記の場合、新しい人間を作成するには名前が必要であることがわかります。したがって、コマンドHuman.new(name)
を使用して、humanの新しいインスタンスを作成できます。 、名前はあなたが選んだものです。この場合、「ヘンリー」を使用しましょう。 irb環境でこれをテストするには、コマンドload './path_to_filename'
を使用してファイルをロードするだけです。 変更が加えられるたびに、ファイルの再実行のためにコマンドを再利用します。私の場合は、load './Human.rb'
です。 irbは上記のファイルを含むフォルダで開かれているためです。 irbなしでコードを実行するには、puts
を追加する必要があります 結果が表示されるように、すべてのコマンドの前にステートメントを記述します。
名前のない新しい人間を作成しようとすると、名前が必要なため、引数エラーが発生します。ただし、正しく実行すると、Henryという名前の人間が作成され、クラスHuman
に属していることがわかります。 。したがって、ヘンリーはクラスHuman
のインスタンスです。 。
@name
@
のため、変数はインスタンス変数と呼ばれます。 記号で始まります。これは、問題のクラスインスタンスが存在する限り、この変数をクラス内の他のメソッドから参照できることを意味します。ここでは、それを作成し、オブジェクトが初期化された名前と同じに設定しました。
このクラスに属するオブジェクトの特性とアクションの定義に進みましょう。作成されたオブジェクトはクラスのインスタンスと呼ばれるため、それらの動作を定義するメソッドはインスタンスメソッドと呼ばれます。 。人間には名前と特定の体の部分があり、特定のアクションを実行できるので、それらを定義しましょう。
def name
@name
end
def no_of_legs
2
end
def no_of_hands
2
end
def speak
'blablabla'
end
作成された人間の名前を取得し、人間が持つ脚と手の数を定義し、人間に話す能力を与えるメソッドを追加しました。 instance.method_name
を使用して、クラスインスタンスでこれらのメソッドを呼び出すことができます。 、以下に示すように。
気が変わって、クラスインスタンスHenryの名前を変更したい場合はどうでしょうか。 Rubyにはこれを実行できる組み込みのメソッドがありますが、手動で実行することもできます。手動で、nameメソッドを、名前を取得する単なるgetterメソッドから、提供されている場合は値にも設定するsetterメソッドに変更できます。
def name=(new_name)
@name = new_name
end
Rubyの組み込みのattr_accessor
を使用する メソッドの場合、nameメソッドを破棄して、attr_accessor :name
という行に置き換えることができます。 :
class Human
attr_accessor :name
def initialize(name)
@name = name
end
# rest of the code
end
選択した方法に関係なく、1日の終わりに、これが取得可能なものです。
これまでに作成されたすべてのメソッドは、instance methods
と呼ばれます。 クラス自体ではなく、クラスの任意のインスタンスで呼び出すことができるためです。 class method
と呼ばれるものもあります 、インスタンスではなく、クラス自体で呼び出すことができます。クラスメソッドは、メソッド名の前にself.
を付けることで名前が付けられます。 。次に例を示します:
# within the Human class
def self.introduction
"I am a human, not an alien!"
end
上記のように、クラスメソッドはクラス自体でのみ使用でき、そのインスタンスでは使用できません。さらに、インスタンス変数が存在するのと同じように、2つの@
がプレフィックスとして付けられたクラス変数があります。 シンボル。例を以下に示します。
class Human
attr_accessor :name
@@no_cars_bought = 0 #class variable
def initialize(name)
@name = name
end
def self.no_cars_bought
@@no_cars_bought
end
def buy_car
@@no_of_cars_bought += 1
"#{@name} just purchased a car"
end
end
この例では、buy_car
を追加しました すべての人間が車を購入できるようにする方法。 @@no_of_cars_bought
というクラス変数も作成しました 車を購入するたびに1ずつ増加します。最後に、no_cars_bought
というクラスメソッドを作成しました 購入した車の数を取得します。これがどのように機能するか見てみましょう:
これまでに定義されたすべてのメソッドは、 publicとして知られています。 クラスの外部からアクセスできるため、メソッド。 privateと呼ばれるメソッドを定義することもできます クラス内でのみアクセスできるメソッド。例を試してみましょう。
# at the bottom of the Human class
def say_account_number
"My account number is #{account_number}"
end
private
def account_number
"1234567890"
end
これにより、次のようになります。
henry.account_number
を呼び出すとき 、account_number
のため、「NoMethodError」が発生します クラス内からのみアクセスできるプライベートメソッドです。 say_account_number
を介してアクセスした場合 このメソッドはプライベートメソッドと同じクラス内に存在するため、私たちが行ったように、エラーはありません。 private
の後のすべてのインスタンスメソッドに注意することが重要です。 キーワードはプライベートメソッドになります。したがって、プライベートメソッドは、すべてのパブリックメソッドの後にクラスの下部で定義する必要があります。
保護されたについて聞いたことがありますか メソッド?そうだね!これらも存在しますが、継承の概念を理解した後で、それらについて説明します。 。
クラスとインスタンスについて理解したところで、継承について話しましょう。 。継承の概念を正しく理解するために、Mammal
という新しいクラスを作成しましょう。 人間は哺乳類だからです。理科の授業で、「すべての人間は哺乳類ですが、すべての哺乳類が人間であるとは限りません」というフレーズを思い出します。また、哺乳類の特徴のいくつかには、髪の毛や毛皮の存在、複雑な脳が含まれていることを思い出します。この情報をMammal
に入れましょう クラス。
class Mammal
def has_hair?
"Most certainly, Yes"
end
def has_complex_brain?
"Well, as a mammal, what do you expect?"
end
end
私の理科の授業のフレーズを覚えていますか?その場合、このステートメントを検証する関係を作成することが重要です。どうしようか?人間のクラスがMammal
のプロパティを継承することを許可します < Mammal
を追加してクラスを作成します そのクラス定義に。
class Human < Mammal
# all other code
end
別のクラスのプロパティを継承しているクラスは、サブクラスと呼ばれます。 、および継承元のクラスはスーパークラスと呼ばれます 。私たちの場合、Human
サブクラスであり、Mammal
スーパークラスです。この時点で、現在行っているように1つのファイルですべてのクラスを定義している場合は、Mammal
に注意することが重要です。 クラス定義は、Human
の前に配置する必要があります 定義される前に変数を参照したくないので、ファイル内のクラス。人間が今持っている追加機能を見てみましょう。
上に示したように、人間はMammal
で定義されたすべての属性にアクセスできるようになりました。 クラス。継承を削除すると(つまり、< Mammal
を削除します) そのコード行から)、コマンドhenry.class.superclass
を実行します。 、応答として「オブジェクト」を取得します。これは、別のクラスから直接継承していない場合、すべてのクラスが「オブジェクト」としてスーパークラスを持っていることを示しています。これにより、Rubyではクラスでさえオブジェクトであるという事実がさらに強化されます。
スーパークラスとは何かがわかったので、今がキーワード superについて話す絶好の機会です。 。 Rubyはこのキーワードを提供して、スーパークラスにすでに存在するメソッドの再利用と変更を可能にします。私たちのMammal
スーパークラス、has_hair?
というメソッドがあることを思い出してください;そのメソッドが呼び出されるたびに、人間に固有の情報を追加したい場合はどうなりますか?ここでsuperキーワードを使用します。Human
クラスでは、同じ名前has_hair?
でメソッドを定義します。 。
def has_hair?
super + ", but humans can be bald at times"
end
superキーワードが呼び出されると、Rubyはスーパークラスでそのメソッド名を探し、その結果を返します。上記のメソッドでは、スーパークラスのhas_hair?
によって生成された結果にいくつかの追加情報を追加しました。 メソッド。
Rubyは単一クラスの継承のみをサポートします。つまり、1つのクラスからのみクラスプロパティを継承できます。特定のクラスに固有ではない複数のプロパティをクラスに追加したい場合はどうなるのか疑問に思っていると思います。 Rubyは、ミックスインの形式でこれを提供します。 、しかし、それらについて話す前に、保護されたメソッドについて話しましょう。 。
保護されたメソッドは、クラスとそのサブクラス内で呼び出すことができるという点で、プライベートメソッドのように機能します。 Mammal
内に保護されたメソッドを作成しましょう クラス。
#at the bottom of the Mammal class
def body_status
body
end
protected
def body
"This body is protected"
end
上記のように、body
にアクセスできません 保護されているためメソッド。ただし、body_status
からアクセスできます。 メソッド。これは、Mammal
内の別のメソッドです。 クラス。定義されているクラスのサブクラスから保護されたメソッドにアクセスすることもできます。 Human
内でこれを試してみましょう クラス。
# within the Human class
def check_body_status
"Just a human checking that " + body.downcase
end
上記のように、bodyメソッドがHuman
内で定義されているかどうかに関係なく クラスと保護された、Human
Mammal
のサブクラスであるため、クラスはそれにアクセスできます クラス。このサブクラスアクセスは、プライベートメソッドでも可能です。ただし、これにより、これらの方法の間にどのような違いがあるのかという疑問が生じます。
保護されたメソッドは、receiver.protected_method
のように、明示的なレシーバーを使用して呼び出すことができます。 、問題の受信者がキーワードself
である限り またはself
と同じクラス 。ただし、プライベートメソッドは、明示的なレシーバーがself
の場合にのみ、明示的なレシーバーを使用して呼び出すことができます。 。body.downcase
を置き換えてコードを編集しましょう self.body.downcase
を使用 check_body_status
で メソッドとプライベートメソッド呼び出しを変更してself
も含める 。
def check_body_status
"Just a human checking that " + self.body.downcase
# body method is protected
end
def say_account_number
"My account number is #{self.account_number}"
# account_number method is private
end
追伸:ルビー2.7より前 、self
を使用して、プライベートメソッドを呼び出して情報を取得する場合 不可能でした。
先に進んで、self
という単語を置き換えましょう self
と同じクラスのオブジェクトを使用する 。この場合、self
Henry
です 、メソッドを呼び出している人、およびHenry
Human
のインスタンスです Mammal
から継承するクラス クラス。
def check_body_status
non_self = Human.new('NotSelf') #same class as self
puts "Just #{non_self.name} checking that " + non_self.body.downcase
# Mammal.new is also in the same class as self
"Just a human checking that " + Mammal.new.body.downcase
end
def say_account_number
non_self = Human.new('NotSelf')
"My account number is #{non_self.account_number}"
end
上記のように、self
を置き換えることができます self
と同じクラスのオブジェクトを持つ保護されたメソッド内 、しかしこれはプライベートメソッドでは不可能です。
ミックスインは、モジュールで定義されたコードのセットです。 、任意のクラスに含まれるか拡張されると、そのクラスに追加機能を提供します。クラス継承で取得できるものとは異なり、これに制限がないため、いくつかのモジュールをクラスに追加できます。この情報に照らして、Movement
というモジュールを作成しましょう。 人間のクラスに移動能力を追加します。
module Movement
def hop
"I can hop"
end
def swim
"Ermmmm, I most likely can if I choose"
end
end
次のステップは、このモジュールをHuman
に含めることです。 クラス。これは、フレーズinclude <module_name>
を追加することによって行われます。 問題のクラスに。私たちの場合、これにはinclude Movement
を追加する必要があります 以下に示すように、人間のクラスに。
class Human < Mammal
include Movement
# rest of the code
end
このコードをテストしてみましょう:
上に示したように、モジュールを介してクラスに混合されたメソッドは、クラスのインスタンスでのみ使用可能であり、クラス自体では使用できません。モジュール内のメソッドをインスタンスではなくクラスで使用できるようにする場合はどうなりますか?これを行うには、extend Movement
のように、「include」という用語を「extend」に置き換えます。 。
.extend
用語はクラスインスタンスでも使用できますが、方法は大きく異なります。 Parent
という別のモジュールを作成しましょう 。
module Parent
def has_kids?
"most definitely"
end
end
上記のように、.extend(Parent)
を使用します お父さんの変数でhas_kids?
を作成します それに利用可能な方法。これは、モジュールのメソッドをすべてのクラスインスタンスに混在させたくない場合に特に便利です。したがって、extend
関心のある特定のインスタンスでのみ使用できます。
モジュールは、クラス、メソッド、定数、さらには他のモジュールを収容します。前に見たように、ミックスインとして使用される以外に、モジュールには他の用途があります。 名前の間隔と呼ばれる利点があるため、名前が衝突しないようにコードを整理するのに最適な方法です。 .Rubyでは、すべてのクラスはモジュールですが、モジュールはインスタンス化も継承もできないため、モジュールはクラスではありません。名前の間隔を理解するために、定数、クラス、メソッド、および別のモジュールを含むモジュールを作成しましょう。
module Male
AGE = "Above 0"
class Adult
def initialize
puts "I am an adult male"
end
end
def self.hungry
puts "There's no such thing as male food, I just eat."
end
module Grown
def self.age
puts "18 and above"
end
end
end
上記のモジュール内で定義および呼び出されたメソッドの前にself.
が付いていることに気付いたかもしれません。 。これは、モジュールをインスタンス化できず、モジュール内でself.
なしでメソッドを定義できるためです。 mixins
について説明したときに見たように、プレフィックスはミックスインとしてのみ使用できます。 。モジュール自体でメソッドを呼び出すには、self.method_name
を使用してメソッド名でこれを識別する必要があります。 。 self
も使用したことを思い出してください クラスメソッドのキーワード。ここでも同じ原則が当てはまります。
上に示したように、モジュール内で定義されたクラス、モジュール、または定数に到達するには、module_name::target_name
を使用します。 。名前空間の概念を正しく理解するために、Adult
という名前のクラスを含む別のモジュールを作成します。 、次に、両方のクラスがどのように区別されるかを確認します。
module Female
class Adult
def initialize
puts "I am an adult female"
end
end
def self.hungry
puts "Maybe there's such a thing as female food, I'm not sure. I just need to eat"
end
end
上に示したように、「Adult」という名前の2つのクラスがあります。それらを独自のモジュールでラップすることにより、毎回呼び出される正確な成人クラスについて混乱することなく、これらの名前を使用できます。さらに、コードはより読みやすく整理されており、さまざまなコードブロックを機能に基づいていくつかのカテゴリに分類するため、関心の分離の設計原則を実装しています。
クラス、インスタンス、モジュール、および継承の概念を理解することは驚くべきことですが、この分野の知識は、Rubyのオブジェクト階層を理解しないと不完全です。これは、Rubyでメソッドが検索される順序を指します。この階層を理解するのに役立ついくつかの方法があります。そのうちの1つは、ancestors
です。 方法。このメソッドはクラスインスタンスでは使用できませんが、クラス自体とその祖先で呼び出すことができます。例を以下に示します。
Human.ancestors
の結果から 、このメソッドは、問題のクラス、その直接含まれるモジュール、その親クラス、および親のクラスの直接含まれるモジュールを返すことがわかります。このループは、Basic Object
に到達するまで続きます 、これはすべてのオブジェクトのルートです。
クラスに関する詳細情報を取得するために使用できるもう1つのメソッドは、メソッドincluded_modules
です。 。
上に示したように、人間のクラスには2つのモジュールが含まれています。1つはinclude Movement
を使用して直接含めたものです。 そして、Objectクラスに含まれているもの。これは、各クラスがその祖先クラスからクラスプロパティを継承し、それらに含まれる各モジュールがそれに含まれることを意味します。この情報に基づいて、Rubyのメソッドルックアップパスとどのクラスに優先順位があるかを確認する簡単な演習を実行します。この道で他の人よりも。同じ名前で出力文字列が異なるメソッドを定義し、それらをHuman
に配置します。 クラス、Movement
モジュール、およびMammal
クラス。
# in the mammal class
def find_path
"Found me in the Mammal class path"
end
# in the Movement module
def find_path
"Found me in the Movement module path"
end
# in the Human class
def find_path
"Found me in the Human class path"
end
それでは、この演習を実行しましょう。
上記のように、.ancestors
を呼び出すときに祖先が配置される順序 クラス上とは、そのクラスのインスタンスで呼び出されるメソッドを探すときにたどるパスです。 Human
の場合 作成したクラスインスタンスでは、Rubyは最初にクラス自体のメソッドを検索します。見つからない場合は、直接含まれているモジュールに進みます。見つからない場合は、スーパークラスに進み、BasicObject
に到達するまでサイクルが続きます。 。それでもメソッドが見つからない場合は、「NoMethodError」が返されます。 .ancestors
を使用する メソッドを使用すると、オブジェクトのルックアップパスと、そのメソッドがいつでもどこから発生するかを特定できます。
Rubyには、methods
というメソッドも用意されています。 、これにより、任意のオブジェクトで使用可能なすべてのメソッドを識別できます。親からのものとそれに固有のものを示すために減算を実行することもできます。例を以下に示します。
上に示したように、変数 Henry 利用できるメソッドがたくさんあります。ただし、オブジェクトで使用可能なものからそれらを差し引くと クラスでは、ファイルで具体的に定義したものだけが残っており、他のすべてのメソッドが継承されていることがわかります。これは、すべてのオブジェクト、そのクラス、およびその祖先のいずれについても同じです。 Human.methods を含むいくつかの組み合わせを試して、手を汚してください。 、 Mammal.methods 、 Module.methods 、および以前に定義された他のすべてのクラスまたはモジュール。これにより、Rubyオブジェクトモデルをより深く理解できるようになります。
-
Rubyのデコレータデザインパターン
デコレータのデザインパターンは何ですか? そして、Rubyプロジェクトでこのパターンをどのように使用できますか? デコレータデザインパターンは、新機能を追加することでオブジェクトを強化するのに役立ちます クラスを変更せずにそれに。 例を見てみましょう! ロギングとパフォーマンス この例では、rest-clientのようなgemを使用してHTTPリクエストを作成しています。 次のようになります: require restclient data = RestClient.get(www.rubyguides.com) 今 : 一部のリクエストにログを追加したいが、RestCli
-
Ruby Freezeメソッド–オブジェクトの可変性を理解する
オブジェクトが可変であるとはどういう意味ですか? 派手な言葉で混乱させないでください。「可変性 」は、オブジェクトの内部状態を変更できることを意味します。これは、凍結されたオブジェクトを除く、すべてのオブジェクトのデフォルトです。 、または特別なオブジェクトのリストの一部であるもの。 つまり、Rubyのすべてのオブジェクトが変更可能というわけではありません! 例 : 数字や記号、さらにはtrueには意味がありません またはfalse (オブジェクトでもあります)変更します。 数字の1は常に1になります。 ただし、他のオブジェクト、特に配列オブジェクトやハッシュオブジェクトなどのデー