Ruby에서 include와 extend의 ​​차이점은 무엇입니까?


415

루비 메타 프로그래밍에 대해 궁금합니다. 믹스 인 / 모듈은 항상 나를 혼란스럽게합니다.

  • include : 지정된 모듈 메소드 에서 대상 클래스의 인스턴스 메소드 로 혼합
  • extend : 지정된 모듈 메소드 에서 대상 클래스의 클래스 메소드 로 혼합

가장 큰 차이점은 이것입니까 아니면 더 큰 용은 숨어 있습니까? 예 :

module ReusableModule
  def module_method
    puts "Module Method: Hi there!"
  end
end

class ClassThatIncludes
  include ReusableModule
end
class ClassThatExtends
  extend ReusableModule
end

puts "Include"
ClassThatIncludes.new.module_method       # "Module Method: Hi there!"
puts "Extend"
ClassThatExtends.module_method            # "Module Method: Hi there!"

답변:


249

당신이 말한 것이 맞습니다. 그러나 그것보다 더 많은 것이 있습니다.

클래스 Klazz와 모듈 이 있다면를 Mod포함하여 의 메소드 에 대한 액세스 인스턴스를 제공합니다 . 또는 당신은 확장 할 수 있습니다 로 바치는 클래스 에 대한 액세스 의 방법을. 또한로 임의의 객체를 확장 할 수 있습니다 . 이 경우 같은 클래스를 가진 다른 모든 객체 는 그렇지 않더라도 개별 객체는 의 메소드를 얻습니다 .ModKlazzKlazzModKlazzMod KlazzModo.extend ModModo


324

extend- 지정된 모듈의 메소드와 상수를 대상의 메타 클래스 (예 : 싱글 톤 클래스)에 추가합니다.

  • 를 호출 Klazz.extend(Mod)하면 Klazz는 Mod의 메소드를 클래스 메소드로 갖습니다.
  • 를 호출 obj.extend(Mod)하면 obj에는 Mod의 메소드가 (인스턴스 메소드로) 있지만 다른 인스턴스 obj.class에는 해당 메소드가 추가 되지 않았습니다 .
  • extend 공개 방법입니다

포함 - 기본적으로 대상 모듈 / 클래스의 인스턴스 메소드로 지정된 모듈의 방법으로 혼합합니다. 예 :

  • 를 호출 class Klazz; include Mod; end;하면 이제 모든 Klazz 인스턴스가 Mod의 메소드에 액세스 할 수 있습니다 (인스턴스 메소드).
  • include 컨테이너 클래스 / 모듈 내에서 호출되기 때문에 전용 메소드입니다.

그러나 모듈은 종종 메소드를 원숭이 패치하여 동작을 무시 include 합니다 included. 이것은 레거시 Rails 코드에서 매우 두드러집니다. Yehuda Katz에서 세부 사항 더 .

include다음 코드를 실행했다고 가정하고 기본 동작과 함께 에 대한 자세한 내용

class Klazz
  include Mod
end
  • Mod가 Klazz 또는 그 조상 중 하나에 이미 포함되어 있으면 include 문이 적용되지 않습니다
  • 충돌하지 않는 한 Klazz의 Mod 상수도 포함합니다.
  • Klazz가 Mod의 모듈 변수에 액세스 할 수 있도록합니다 (예 : @@foo또는@@bar
  • 주기적 포함이 있으면 ArgumentError 발생
  • 모듈을 호출자의 직계 조상으로 연결합니다 (예 : Klazz.ancestors에 Mod를 추가하지만 Klazz.superclass.superclass.superclass의 체인에 Mod가 추가되지 않으므로 superKlazz # foo를 호출 하면 확인하기 전에 Mod # foo를 확인합니다. Klazz의 실제 수퍼 클래스의 foo 메소드에 대한 자세한 내용은 RubySpec을 참조하십시오.)

물론 루비 코어 문서 는 항상 이런 일을하기에 가장 좋은 장소입니다. RubySpec 프로젝트 는 기능을 정확하게 문서화했기 때문에 환상적인 리소스였습니다.


22
나는 이것이 꽤 오래된 게시물이라는 것을 알고 있지만 답장의 명확성으로 인해 의견을 말하지 못했습니다. 좋은 설명 감사합니다.
MohamedSanaulla

2
@anwar 분명히,하지만 이제는 의견을 말하고 기사를 다시 찾을 수있었습니다. 그것은 여기에 있습니다 : aaronlasseigne.com/2012/01/17/explaining-include-and-extend 그리고 나는 여전히 스키마가 이해를 훨씬 더 쉽게 만든다고 생각합니다
systho

1
이 응답의 가장 큰 extend장점 은 활용도에 따라 메소드를 클래스 또는 인스턴스 메소드 로 적용하는 방법 입니다. Klass.extend= 클래스 메소드, objekt.extend= 인스턴스 메소드. 나는 항상 (잘못) 클래스 메소드가에서 왔다고 가정 extend했다 include.
Frank Koehl

16

맞습니다.

배후에 include는 실제로 append_features 의 별명입니다. (문서에서) :

Ruby의 기본 구현은이 모듈이 aModule 또는 해당 조상 중 하나에 아직 추가되지 않은 경우이 모듈의 상수, 메소드 및 모듈 변수를 aModule에 추가하는 것입니다.


4

include클래스로 모듈은 모듈의 방법으로 가져 인스턴스 메소드 .

그러나 extend모듈을 클래스에 넣으면 모듈 메서드를 클래스 methods 로 가져옵니다 .

예를 들어 Module_test다음과 같이 정의 된 모듈이있는 경우 :

module Module_test
  def func
    puts "M - in module"
  end
end

이제 include모듈입니다. 클래스 A를 다음과 같이 정의하면 :

class A
  include Module_test
end

a = A.new
a.func

출력은 다음과 같습니다 M - in module..

include Module_test을 바꾸고 extend Module_test코드를 다시 실행하면 다음과 같은 오류가 발생 undefined method 'func' for #<A:instance_num> (NoMethodError)합니다.

로 메소드 호출 a.funcA.func변경하면 출력이 다음으로 변경됩니다 M - in module.

위의 코드 실행에서 우리 include는 모듈 일 때 메소드가 인스턴스 메소드가 되고 extend, 모듈 일 때 그 메소드가 클래스 메소드 가 된다는 것이 분명합니다 .


3

RubySpecs를 파헤치는 팁을 포함하여 다른 모든 대답은 좋습니다.

https://github.com/rubyspec/rubyspec/blob/master/core/module/include_spec.rb

https://github.com/rubyspec/rubyspec/blob/master/core/module/extend_object_spec.rb

사용 사례는 다음과 같습니다.

ClassThatIncludes 클래스에 ReusableModule 모듈 을 포함 하면 메소드, 상수, 클래스, 서브 모듈 및 기타 선언이 참조됩니다.

ReusableModule 모듈을 사용하여 ClassThatExtends 클래스 를 확장 하면 메소드와 상수가 복사 됩니다. 분명히주의하지 않으면 동적으로 정의를 복제하여 많은 메모리를 낭비 할 수 있습니다.

ActiveSupport :: Concern을 사용하는 경우 .included () 기능을 사용하면 포함 클래스를 직접 다시 작성할 수 있습니다. 우려 내의 모듈 ClassMethods 는 포함 클래스로 확장 (복사)됩니다.


1

또한 작동하는 메커니즘을 설명하고 싶습니다. 내가 옳지 않으면 정정하십시오.

사용할 때 include클래스에서 일부 메소드를 포함하는 모듈에 링크를 추가합니다.

class A
include MyMOd
end

a = A.new
a.some_method

객체에는 메소드가 없으며 클래스와 모듈 만 있습니다. 따라서 메시지를 a받으면 고유 클래스 에서 some_method검색 방법 some_methoda시작한 다음 A클래스에서 A클래스 모듈 에 링크하여 검색 합니다.

우리가 사용할 때 extend객체의 고유 클래스의 모듈에 연결을 추가합니다. 따라서 A.new.extend (MyMod)를 사용하면 A의 인스턴스 고유 클래스 또는 a'클래스에 모듈에 대한 연결을 추가 합니다. 그리고 A.extend (MyMod)를 사용하면 A (object 's, classes are objects) eigenclass에 연결을 추가합니다 A'.

따라서 메소드 검색 경로 a는 다음과 같습니다. a => a '=> a'class => A에 연결된 모듈.

또한 조회 경로를 변경하는 prepend 메소드가 있습니다.

a => a '=> 앞에 붙인 모듈 => A => 포함 된 모듈 A

내 하찮은 영어 실력에 죄송하다는 말씀을 드리고 싶습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.