Ruby
 Computer >> コンピューター >  >> プログラミング >> Ruby

Rubyオブジェクトモデルを詳細に理解する

ウィキペディアによると、オブジェクト指向プログラミング(OOP)は、「オブジェクト」の概念に基づくプログラミングパラダイムであり、データとコードを含めることができます。フィールド形式のデータ(多くの場合、属性またはプロパティと呼ばれます)と形式のコードです。手順の(多くの場合、メソッドとして知られています)。

Rubyは純粋なオブジェクト指向言語です。つまり、Ruby言語では、すべてがオブジェクトです。これらのオブジェクトは、文字列、数値、クラス、モジュールなどであるかどうかに関係なく、オブジェクトモデルと呼ばれるシステムで動作します。 。

Rubyはobject_idと呼ばれるメソッドを提供しています 、すべてのオブジェクトで使用できます。この識別子は整数を返し、2つのオブジェクトで同じになることはありません。手を汚すためにirbに入りましょう。これを行うには、irbと入力します ターミナルで。

Rubyオブジェクトモデルを詳細に理解する

上記のように、文字列、整数、配列、クラス、さらにはメソッドでさえ、オブジェクト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を追加する必要があります 結果が表示されるように、すべてのコマンドの前にステートメントを記述します。

Rubyオブジェクトモデルを詳細に理解する

名前のない新しい人間を作成しようとすると、名前が必要なため、引数エラーが発生します。ただし、正しく実行すると、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を使用して、クラスインスタンスでこれらのメソッドを呼び出すことができます。 、以下に示すように。

Rubyオブジェクトモデルを詳細に理解する

気が変わって、クラスインスタンス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日の終わりに、これが取得可能なものです。

Rubyオブジェクトモデルを詳細に理解する

これまでに作成されたすべてのメソッドは、instance methodsと呼ばれます。 クラス自体ではなく、クラスの任意のインスタンスで呼び出すことができるためです。 class methodと呼ばれるものもあります 、インスタンスではなく、クラス自体で呼び出すことができます。クラスメソッドは、メソッド名の前にself.を付けることで名前が付けられます。 。次に例を示します:

# within the Human class
def self.introduction
  "I am a human, not an alien!"
end

Rubyオブジェクトモデルを詳細に理解する

上記のように、クラスメソッドはクラス自体でのみ使用でき、そのインスタンスでは使用できません。さらに、インスタンス変数が存在するのと同じように、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というクラスメソッドを作成しました 購入した車の数を取得します。これがどのように機能するか見てみましょう:

Rubyオブジェクトモデルを詳細に理解する

これまでに定義されたすべてのメソッドは、 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

これにより、次のようになります。

Rubyオブジェクトモデルを詳細に理解する

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の前に配置する必要があります 定義される前に変数を参照したくないので、ファイル内のクラス。人間が今持っている追加機能を見てみましょう。

Rubyオブジェクトモデルを詳細に理解する

上に示したように、人間はMammalで定義されたすべての属性にアクセスできるようになりました。 クラス。継承を削除すると(つまり、< Mammalを削除します) そのコード行から)、コマンドhenry.class.superclassを実行します。 、応答として「オブジェクト」を取得します。これは、別のクラスから直接継承していない場合、すべてのクラスが「オブジェクト」としてスーパークラスを持っていることを示しています。これにより、Rubyではクラスでさえオブジェクトであるという事実がさらに強化されます。

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オブジェクトモデルを詳細に理解する

Rubyは単一クラスの継承のみをサポートします。つまり、1つのクラスからのみクラスプロパティを継承できます。特定のクラスに固有ではない複数のプロパティをクラスに追加したい場合はどうなるのか疑問に思っていると思います。 Rubyは、ミックスインの形式でこれを提供します。 、しかし、それらについて話す前に、保護されたメソッドについて話しましょう。 。

保護されたメソッド

保護されたメソッドは、クラスとそのサブクラス内で呼び出すことができるという点で、プライベートメソッドのように機能します。 Mammal内に保護されたメソッドを作成しましょう クラス。

  #at the bottom of the Mammal class
  def body_status
    body
  end

  protected
  def body
    "This body is protected"
  end

Rubyオブジェクトモデルを詳細に理解する

上記のように、bodyにアクセスできません 保護されているためメソッド。ただし、body_statusからアクセスできます。 メソッド。これは、Mammal内の別のメソッドです。 クラス。定義されているクラスのサブクラスから保護されたメソッドにアクセスすることもできます。 Human内でこれを試してみましょう クラス。

# within the Human class
def check_body_status
  "Just a human checking that " + body.downcase
end

Rubyオブジェクトモデルを詳細に理解する

上記のように、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

Rubyオブジェクトモデルを詳細に理解する

追伸:ルビー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

Rubyオブジェクトモデルを詳細に理解する

上記のように、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

このコードをテストしてみましょう:

Rubyオブジェクトモデルを詳細に理解する

上に示したように、モジュールを介してクラスに混合されたメソッドは、クラスのインスタンスでのみ使用可能であり、クラス自体では使用できません。モジュール内のメソッドをインスタンスではなくクラスで使用できるようにする場合はどうなりますか?これを行うには、extend Movementのように、「include」という用語を「extend」に置き換えます。 。

Rubyオブジェクトモデルを詳細に理解する

.extend 用語はクラスインスタンスでも使用できますが、方法は大きく異なります。 Parentという別のモジュールを作成しましょう 。

module Parent
  def has_kids?
    "most definitely"
  end
end

Rubyオブジェクトモデルを詳細に理解する

上記のように、.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も使用したことを思い出してください クラスメソッドのキーワード。ここでも同じ原則が当てはまります。

Rubyオブジェクトモデルを詳細に理解する

上に示したように、モジュール内で定義されたクラス、モジュール、または定数に到達するには、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

Rubyオブジェクトモデルを詳細に理解する

上に示したように、「Adult」という名前の2つのクラスがあります。それらを独自のモジュールでラップすることにより、毎回呼び出される正確な成人クラスについて混乱することなく、これらの名前を使用できます。さらに、コードはより読みやすく整理されており、さまざまなコードブロックを機能に基づいていくつかのカテゴリに分類するため、関心の分離の設計原則を実装しています。

オブジェクト階層

クラス、インスタンス、モジュール、および継承の概念を理解することは驚くべきことですが、この分野の知識は、Rubyのオブジェクト階層を理解しないと不完全です。これは、Rubyでメソッドが検索される順序を指します。この階層を理解するのに役立ついくつかの方法があります。そのうちの1つは、ancestorsです。 方法。このメソッドはクラスインスタンスでは使用できませんが、クラス自体とその祖先で呼び出すことができます。例を以下に示します。

Rubyオブジェクトモデルを詳細に理解する

Human.ancestorsの結果から 、このメソッドは、問題のクラス、その直接含まれるモジュール、その親クラス、および親のクラスの直接含まれるモジュールを返すことがわかります。このループは、Basic Objectに到達するまで続きます 、これはすべてのオブジェクトのルートです。

クラスに関する詳細情報を取得するために使用できるもう1つのメソッドは、メソッドincluded_modulesです。 。

Rubyオブジェクトモデルを詳細に理解する

上に示したように、人間のクラスには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

それでは、この演習を実行しましょう。

Rubyオブジェクトモデルを詳細に理解する

上記のように、.ancestorsを呼び出すときに祖先が配置される順序 クラス上とは、そのクラスのインスタンスで呼び出されるメソッドを探すときにたどるパスです。 Humanの場合 作成したクラスインスタンスでは、Rubyは最初にクラス自体のメソッドを検索します。見つからない場合は、直接含まれているモジュールに進みます。見つからない場合は、スーパークラスに進み、BasicObjectに到達するまでサイクルが続きます。 。それでもメソッドが見つからない場合は、「NoMethodError」が返されます。 .ancestorsを使用する メソッドを使用すると、オブジェクトのルックアップパスと、そのメソッドがいつでもどこから発生するかを特定できます。

Rubyには、methodsというメソッドも用意されています。 、これにより、任意のオブジェクトで使用可能なすべてのメソッドを識別できます。親からのものとそれに固有のものを示すために減算を実行することもできます。例を以下に示します。

Rubyオブジェクトモデルを詳細に理解する

上に示したように、変数 Henry 利用できるメソッドがたくさんあります。ただし、オブジェクトで使用可能なものからそれらを差し引くと クラスでは、ファイルで具体的に定義したものだけが残っており、他のすべてのメソッドが継承されていることがわかります。これは、すべてのオブジェクト、そのクラス、およびその祖先のいずれについても同じです。 Human.methods を含むいくつかの組み合わせを試して、手を汚してください。 、 Mammal.methods Module.methods 、および以前に定義された他のすべてのクラスまたはモジュール。これにより、Rubyオブジェクトモデルをより深く理解できるようになります。


  1. Rubyのデコレータデザインパターン

    デコレータのデザインパターンは何ですか? そして、Rubyプロジェクトでこのパターンをどのように使用できますか? デコレータデザインパターンは、新機能を追加することでオブジェクトを強化するのに役立ちます クラスを変更せずにそれに。 例を見てみましょう! ロギングとパフォーマンス この例では、rest-clientのようなgemを使用してHTTPリクエストを作成しています。 次のようになります: require restclient data = RestClient.get(www.rubyguides.com) 今 : 一部のリクエストにログを追加したいが、RestCli

  2. Ruby Freezeメソッド–オブジェクトの可変性を理解する

    オブジェクトが可変であるとはどういう意味ですか? 派手な言葉で混乱させないでください。「可変性 」は、オブジェクトの内部状態を変更できることを意味します。これは、凍結されたオブジェクトを除く、すべてのオブジェクトのデフォルトです。 、または特別なオブジェクトのリストの一部であるもの。 つまり、Rubyのすべてのオブジェクトが変更可能というわけではありません! 例 : 数字や記号、さらにはtrueには意味がありません またはfalse (オブジェクトでもあります)変更します。 数字の1は常に1になります。 ただし、他のオブジェクト、特に配列オブジェクトやハッシュオブジェクトなどのデー