Computer >> 컴퓨터 >  >> 프로그램 작성 >> Ruby

Ruby 객체 모델에 대한 심층적인 이해

Wikipedia에 따르면 객체 지향 프로그래밍(OOP)은 데이터와 코드를 포함할 수 있는 "객체" 개념에 기반한 프로그래밍 패러다임입니다. 절차(종종 방법이라고도 함).

Ruby는 순수한 객체 지향 언어입니다. 즉, Ruby 언어에서는 모든 것이 객체입니다. 이러한 객체는 문자열, 숫자, 클래스, 모듈 등 여부에 관계없이 객체 모델이라는 시스템에서 작동합니다. .

Ruby는 object_id라는 메소드를 제공합니다. , 모든 개체에서 사용할 수 있습니다. 이 식별자는 정수를 반환하며 두 개체에 대해 결코 동일하지 않습니다. irb로 들어가 손을 더럽히자. irb를 입력하면 됩니다. 터미널에서.

Ruby 객체 모델에 대한 심층적인 이해

위에서 보았듯이 문자열, 정수, 배열, 클래스, 심지어 메서드까지 모두 개체 ID를 가지고 있기 때문에 개체입니다.

이 문서에서는 다음 개념을 자세히 다룹니다.

  • 클래스 및 인스턴스
  • 상속
  • 공개, 비공개 및 보호 방법
  • 믹스
  • 모듈
  • 객체 계층 구조

클래스 및 인스턴스

Ruby에서 클래스는 객체의 속성과 태도(액션)를 정의하는 곳입니다. 객체가 둥글고(속성) 말할 수 있어야(액션)해야 하는 경우 이러한 속성과 액션이 메서드라고 하는 것으로 정의되기 때문에 객체가 속한 클래스에서 알 수 있습니다. 해당 클래스에서. 클래스에 속하는 객체를 인스턴스라고 합니다. .new를 사용하여 생성(인스턴스화)됩니다. . Human이라는 클래스를 만드는 것으로 시작하겠습니다. . 우리는 모두 인간이므로 재미있어야 합니다.

class Human
  def initialize(name)
    @name = name
  end
end

초기화 메서드는 생성할 클래스의 새 인스턴스에 대한 요구 사항을 식별합니다. 위의 경우 새 인간을 생성하려면 이름이 필요함을 알 수 있습니다. 따라서 Human.new(name) 명령을 사용하여 새로운 human 인스턴스를 생성할 수 있습니다. , 여기서 이름은 무엇이든 선택합니다. 우리의 경우 'Henry'를 사용합시다. irb 환경에서 이것을 테스트하려면 load './path_to_filename' 명령을 사용하여 파일을 로드하기만 하면 됩니다. 변경 사항이 있을 때마다 파일 재실행 명령을 다시 사용하십시오. 제 경우에는 load './Human.rb'입니다. irb는 해당 파일이 포함된 폴더에서 열리기 때문입니다. irb 없이 코드를 실행하려면 puts를 추가해야 합니다. 결과를 볼 수 있도록 모든 명령 앞에 문을 추가합니다.

Ruby 객체 모델에 대한 심층적인 이해

이름 없이 새로운 사람을 만들려고 하면 이름이 필요하기 때문에 인수 오류가 발생합니다. 그러나 올바르게 수행하면 Henry라는 사람이 생성되고 Human 클래스에 속하는 것을 볼 수 있습니다. . 따라서 Henry는 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 사용 메서드에서 이름 메서드를 버리고 attr_accessor :name 줄로 바꿀 수 있습니다. :

class Human
  attr_accessor :name

  def initialize(name)
    @name = name
  end
# rest of the code
end

선택한 방법에 관계없이 하루가 끝나면 이것이 얻을 수 있습니다.

Ruby 객체 모델에 대한 심층적인 이해

지금까지 생성된 모든 메소드를 instance methods라고 합니다. 클래스의 모든 인스턴스에서 호출할 수 있지만 클래스 자체에서는 호출할 수 없기 때문입니다. class method라는 것도 있습니다. , 인스턴스가 아닌 클래스 자체에서 호출할 수 있습니다. 클래스 메서드는 메서드 이름 앞에 self.를 붙여서 이름을 지정합니다. . 다음은 예입니다.

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

Ruby 객체 모델에 대한 심층적인 이해

위에서 볼 수 있듯이 클래스 메서드는 해당 인스턴스가 아닌 클래스 자체에서만 사용할 수 있습니다. 또한 인스턴스 변수가 존재하는 것처럼 두 개의 @ 접두사가 붙는 클래스 변수가 있습니다. 기호. 아래에 예가 나와 있습니다.

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 객체 모델에 대한 심층적인 이해

지금까지 정의된 모든 메소드는 공개로 알려져 있습니다. 메서드는 클래스 외부에서 액세스할 수 있기 때문입니다. 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 메서드와 동일한 클래스 내에 있으므로 오류가 없습니다. private 이후의 모든 인스턴스 메소드가 키워드가 개인 메서드가 됩니다. 따라서 private 메소드는 모든 public 메소드 다음에 클래스의 맨 아래에 정의되어야 합니다.

보호에 대해 들어본 적이 있습니까? 행동 양식? 그래 맞아! 이것들도 존재하지만 상속의 개념을 이해한 후에 이에 대해 이야기하겠습니다. .

상속

이제 클래스와 인스턴스에 대해 알았으므로 상속에 대해 이야기해 보겠습니다. . 상속의 개념을 제대로 이해하기 위해 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입니다. 슈퍼클래스이다. 지금 하고 있는 것처럼 모든 클래스를 하나의 파일에 정의하는 경우 Mammal 클래스 정의는 Human 앞에 와야 합니다. 정의하기 전에 변수를 참조하고 싶지 않기 때문에 파일에 클래스를 추가합니다. 이제 인간에게 어떤 추가 기능이 있는지 봅시다.

Ruby 객체 모델에 대한 심층적인 이해

위에 표시된 것처럼 인간은 이제 Mammal에 정의된 모든 속성에 액세스할 수 있습니다. 수업. 상속을 제거하는 경우(즉, < Mammal 해당 코드 줄에서) henry.class.superclass 명령을 실행합니다. , 우리는 응답으로 "Object"를 얻습니다. 이것은 다른 클래스에서 직접 상속하지 않을 때 모든 클래스가 "Object"라는 수퍼클래스를 갖고 있다는 것을 말해줍니다. 이는 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는 단일 클래스 상속만 지원합니다. 즉, 하나의 클래스에서 클래스 속성만 상속할 수 있습니다. 특정 클래스에 한정되지 않은 여러 속성을 클래스에 추가하려는 경우 어떤 일이 발생하는지 궁금할 것입니다. Ruby는 mixins 형태로 이를 제공합니다. 하지만 이에 대해 이야기하기 전에 보호된 방법에 대해 알아보겠습니다. .

보호된 방법

보호된 메서드는 클래스 및 해당 하위 클래스 내에서 호출할 수 있다는 점에서 개인 메서드처럼 작동합니다. 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와 같은 클래스에 있음 . 그러나 private 메소드는 명시적 수신자가 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 객체 모델에 대한 심층적인 이해

추신:Ruby 2.7 이전 , self를 사용하여 일부 정보를 얻기 위해 private 메소드를 호출할 때 불가능했습니다.

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와 같은 클래스의 객체를 가진 보호된 메소드에서 , 그러나 이것은 private 메소드에서는 불가능합니다.

믹스인

믹스인은 모듈에 정의된 코드 세트입니다. , 클래스에 포함되거나 확장될 때 해당 클래스에 추가 기능을 제공합니다. 클래스 상속에서 얻을 수 있는 것과 달리 이것에 대한 제한이 없기 때문에 여러 모듈을 클래스에 추가할 수 있습니다. 이 정보에 비추어 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에서와 같이 "포함" 용어를 "확장"으로 바꿉니다. .

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 객체 모델에 대한 심층적인 이해

위에 표시된 것처럼 "성인"이라는 이름을 가진 두 개의 클래스가 있습니다. 그것들을 자체 모듈 내에서 래핑함으로써 매번 호출되는 정확한 성인 클래스에 대해 혼동 없이 이러한 이름을 사용할 수 있습니다. 또한, 우리 코드는 더 읽기 쉽고 조직적이며, 서로 다른 코드 블록을 기능에 따라 여러 범주로 분리하기 때문에 관심사 분리 설계 원칙을 구현합니다.

객체 계층

클래스, 인스턴스, 모듈 및 상속의 개념을 이해하는 것은 놀라운 일이지만 Ruby의 객체 계층 구조를 이해하지 않고 이 영역에 대한 지식은 불완전합니다. 이것은 Ruby에서 메소드가 검색되는 순서를 나타냅니다. 이 계층 구조를 이해하는 데 도움이 되는 몇 가지 방법이 있습니다. 그 중 하나는 ancestors입니다. 방법. 이 메서드는 클래스 인스턴스에서 사용할 수 없지만 클래스 자체 및 해당 조상에서 호출할 수 있습니다. 아래에 예가 나와 있습니다.

Ruby 객체 모델에 대한 심층적인 이해

Human.ancestors의 결과에서 , 우리는 이 메서드가 문제의 클래스, 직접 포함된 모듈, 부모 클래스 및 부모 클래스의 직접 포함된 모듈을 반환한다는 것을 알 수 있습니다. 이 루프는 Basic Object에 도달할 때까지 계속됩니다. , 모든 개체의 루트입니다.

클래스에 대한 추가 정보를 얻을 수 있는 또 다른 방법은 included_modules입니다. .

Ruby 객체 모델에 대한 심층적인 이해

위에 표시된 것처럼 인간 클래스에는 두 개의 모듈이 포함되어 있습니다. 하나는 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를 호출할 때 조상이 배치되는 순서 on 클래스는 해당 클래스의 인스턴스에서 호출된 메서드를 찾을 때 따라가는 경로입니다. Human의 경우 우리가 만든 클래스 인스턴스, Ruby는 먼저 클래스 자체에서 메서드를 검색합니다. 발견되지 않으면 직접 포함된 모듈로 진행합니다. 발견되지 않으면 슈퍼클래스로 진행하고 BasicObject에 도달할 때까지 주기가 계속됩니다. . 메소드가 여전히 발견되지 않으면 "NoMethodError"가 리턴됩니다. .ancestors를 사용하여 메서드를 사용하면 개체에 대한 조회 경로와 해당 메서드가 특정 시점에서 나오는 위치를 식별할 수 있습니다.

Ruby는 methods라는 메소드도 제공합니다. , 이를 통해 모든 개체에서 사용할 수 있는 모든 메서드를 식별할 수 있습니다. 우리는 뺄셈을 수행하여 부모에게서 온 것과 고유한 것을 보여줄 수도 있습니다. 아래에 예가 나와 있습니다.

Ruby 객체 모델에 대한 심층적인 이해

위에 표시된 것처럼 변수 Henry 많은 방법을 사용할 수 있습니다. 그러나 Object에서 사용할 수 있는 항목에서 빼면 클래스에서, 우리는 우리가 우리 파일에 특별히 정의한 것들만 남게 되고 다른 모든 메소드는 상속된다는 것을 발견했습니다. 이것은 모든 객체, 해당 클래스 및 모든 조상에 대해 동일합니다. Human.methods와 관련된 여러 조합을 시도하여 자유롭게 손을 더럽힐 수 있습니다. , 포유류.방법 , 모듈.메소드 , 그리고 앞에서 정의한 다른 모든 클래스 또는 모듈; 이렇게 하면 Ruby 개체 모델을 더 잘 이해할 수 있습니다.