모듈에 중첩 된 클래스와 중첩 클래스를 사용하는시기


144

서브 클래스와 모듈을 사용하는시기에 대해 잘 알고 있지만 최근에는 다음과 같이 중첩 된 클래스를보고 있습니다.

class Foo
  class Bar
    # do some useful things
  end
end

모듈에 중첩 된 클래스뿐만 아니라 다음과 같습니다.

module Baz
  class Quux
    # more code
  end
end

문서와 기사가 희소하거나 올바른 검색어를 찾을만큼 주제에 대해 교육을받지 못했지만 주제에 대한 많은 정보를 찾을 수 없습니다.

누군가가 그 기술이 왜 언제 사용되는지에 대한 게시물에 대한 예나 링크를 제공 할 수 있습니까?

답변:


140

다른 OOP 언어에는 내부 클래스 가 있으며 상위 클래스에 바인딩되지 않으면 인스턴스화 할 수 없습니다. 예를 들어 Java에서는

class Car {
    class Wheel { }
}

Car클래스의 메소드 만을 작성할 수 있습니다 Wheel.

루비에는 그런 행동이 없습니다.

루비에서

class Car
  class Wheel
  end
end

~와 다르다

class Car
end

class Wheel
end

단지 클래스의 이름으로 WheelCar::Wheel. 이러한 이름의 차이 Car::Wheel는 일반 휠과 달리 클래스가 자동차 휠만 나타낼 수 있다는 것을 프로그래머에게 명백하게 할 수 있습니다. 루비에서 중첩 클래스 정의는 선호의 문제이지만, 두 클래스 간의 계약을보다 강력하게 시행하고 클래스와 그 사용에 대한 자세한 정보를 전달한다는 의미에서 목적을 제공합니다.

그러나 루비 인터프리터에게는 이름의 차이 일뿐입니다.

두 번째 관찰에서 모듈 내부에 중첩 된 클래스는 일반적으로 클래스의 네임 스페이스에 사용됩니다. 예를 들어 :

module ActiveRecord
  class Base
  end
end

~와 다르다

module ActionMailer
  class Base
  end
end

이것이 모듈 내부에 중첩 된 클래스의 유일한 사용은 아니지만 일반적으로 가장 일반적입니다.


5
@rubyprince, 님 Car.new과 의 관계를 설정하여 무슨 뜻인지 잘 모르겠습니다 Car::Wheel.new. Ruby에서 Car객체를 초기화하기 위해 객체를 초기화 할 필요는 Car::Wheel없지만 Car클래스를 사용하려면 클래스를로드하고 실행해야합니다 Car::Wheel.
Pan Thomakos 2016 년

30
@Pan, Java 내부 클래스 와 네임 스페이스가있는 Ruby 클래스를 혼동 하고 있습니다. 정적이 아닌 Java 중첩 클래스를 내부 클래스 라고하며 외부 클래스 의 인스턴스에만 존재합니다. 외부 참조를 허용하는 숨겨진 필드가 있습니다. Ruby 내부 클래스는 단순히 네임 스페이스이며 어떤 방식 으로든 클래스에 "바인딩"되지 않습니다. Java 정적 (중첩) 클래스 와 동일합니다 . 예, 답변에 많은 투표권이 있지만 완전히 정확하지는 않습니다.
DigitalRoss

7
나는이 답변이 어떻게 60 개의 투표를 받았는지 알지 못합니다. 말 그대로 여기에는 하나의 진정한 진술이 없습니다. 루비에는 베타 나 뉴스 피크처럼 중첩 된 클래스가 없습니다. Car와 사이에는 아무런 관계가 없습니다 Car::Wheel. 모듈 (및 클래스)은 상수의 네임 스페이스이며 루비에는 중첩 클래스 또는 중첩 모듈과 같은 것이 없습니다.
Jörg W Mittag

4
두 가지의 유일한 차이점은 일정한 해상도입니다 (어휘 적이므로 두 스 니펫이 어휘 적으로 다르기 때문에 분명히 다릅니다). 그러나 관련된 수업과 관련하여 둘 사이에는 아무런 차이가 없습니다. 완전히 관련이없는 두 개의 클래스가 있습니다. 기간. 루비에는 중첩 / 내부 클래스가 없습니다. 중첩 클래스의 정의는 정확하지만 사소하게 테스트 할 수 있기 때문에 Ruby에는 적용되지 않습니다 Car::Wheel.new. 팔. 방금 Wheel객체 안에 중첩되지 않은 객체를 만들었습니다 Car.
Jörg W Mittag

10
이 게시물은 전체 댓글 스레드를 읽지 않고 오해의 소지가 있습니다.
Nathan

50

Ruby에서 중첩 클래스를 정의하는 것은 모듈에서 클래스를 정의하는 것과 유사합니다. 실제로 클래스 간의 연관을 강요하지 않고 상수의 네임 스페이스를 만듭니다. (클래스 및 모듈 이름은 상수입니다.)

수락 된 답변이 아무것도 맞지 않았습니다. 1 아래 예에서는 기존 클래스를 포함하지 않고 어휘로 묶인 클래스의 인스턴스를 만듭니다.

class A; class B; end; end
A::B.new

캡슐화, 한 곳에서만 사용되는 코드 그룹화 및 사용되는 위치에 더 가까운 코드 배치와 같은 장점은 모듈과 동일합니다. 큰 프로젝트에는 각 소스 파일에서 반복적으로 발생하고 많은 클래스 정의가 포함 된 하나의 외부 모듈이있을 수 있습니다. 다양한 프레임 워크와 라이브러리 코드가 모두이 작업을 수행 할 때 각각 하나의 이름 만 최상위 레벨에 제공하여 충돌 가능성을 줄입니다. 확실히, 그러나 그것이 그들이 사용되는 이유입니다.

외부 네임 스페이스를 정의하기 위해 모듈 대신 클래스를 사용하는 것은 단일 파일 프로그램이나 스크립트에서 또는 이미 최상위 클래스를 사용하거나 클래스를 서로 연결하는 코드를 추가하려는 경우에 의미가 있습니다. 진정한 내부 클래스 스타일. 루비에는 내부 클래스가 없지만 코드에서 동일한 동작을 만드는 것을 막는 것은 없습니다. 내부 개체에서 외부 개체를 참조하려면 여전히 외부 개체의 인스턴스에서 도팅을 수행해야하지만 클래스를 중첩하면 이것이 현재 수행중인 작업 일 것입니다. 신중하게 모듈화 된 프로그램은 항상 둘러싸는 클래스를 먼저 만들고 중첩 또는 내부 클래스로 합리적으로 분해 될 수 있습니다. new모듈을 호출 할 수 없습니다 .

네임 스페이스가별로 필요하지 않은 스크립트에서도 재미와 연습을 위해 일반적인 패턴을 사용할 수 있습니다 ...

#!/usr/bin/env ruby

class A
  class Realwork_A
    ...
  end
  class Realwork_B
    ...
  end

  def run
    ...
  end

  self
end.new.run

15
제발, 제발, 제발이 클래스를 내부 클래스라고 부르지 마십시오. 그렇지 않습니다. 클래스 B가 클래스 안에 없습니다A . 상수 B 내부 클래스 네임 스페이스되어 A있지만 의해 참조되는 오브젝트 간의 관계가 전혀 없다 B(이 경우 단지 클래스 될 일이 있음)에 의해 참조되는 클래스 A.
Jörg W Mittag

2
"내부"용어가 제거되었습니다. 좋은 지적. 위의 주장을 따르지 않는 사람들에게 논쟁의 이유는 Java와 같이 이와 같은 일을 할 때 내부 클래스의 객체 (그리고 여기서는 정식 용어를 사용하고 있음)는 외부 클래스 및 외부 인스턴스 변수는 내부 클래스 메소드로 참조 할 수 있습니다. 코드와 연결하지 않는 한 Ruby에서는 이런 일이 발생하지 않습니다. 그리고 그 코드가 ahem, 둘러싼 클래스에 존재한다면 Bar를 내부 클래스로
DigitalRoss

1
나에게 모듈과 클래스 사이를 결정할 때 가장 도움이되는 것은 다음과 같습니다 You can't call new on a module..-- 기본적으로 일부 클래스의 네임 스페이스를 원하고 실제로 외부 "클래스" 의 인스턴스 를 만들 필요가 없다면 기본적으로 외부 모듈을 사용하겠습니다. 그러나 포장 / 외부 "클래스"의 인스턴스를 인스턴스화하려면 모듈 대신 클래스로 만들 것입니다. 적어도 이것은 나에게 의미가 있습니다.
FireDragon

@FireDragon 또는 다른 유스 케이스는 서브 클래스가 상속하는 팩토리 인 클래스를 원하고 팩토리가 서브 클래스의 인스턴스를 작성해야하는 경우가 있습니다. 이 경우 팩토리는 모듈에서 상속 할 수 없기 때문에 모듈이 될 수 없으므로 인스턴스화하지 않는 상위 클래스입니다 (원하는 경우 모듈과 같은 종류의 하위 클래스 인 '네임 스페이스')
rmcsharry

15

이것을 사용 하여 클래스를 모듈로 그룹화 할 수 있습니다. 네임 스페이스의 종류.

예를 들어 Twitter gem 은 네임 스페이스를 사용하여이를 달성합니다.

Twitter::Client.new

Twitter::Search.new

따라서 클래스 ClientSearch클래스는 모두 Twitter모듈 아래에 있습니다.

소스를 확인하려면 두 클래스의 코드를 여기여기 에서 찾을 수 있습니다 .

도움이 되었기를 바랍니다!


3
트위터 보석 업데이트 링크 : github.com/sferik/twitter/tree/master/lib/twitter
KODE

6

2.5 이전의 Ruby에서 중첩 클래스와 중첩 모듈 사이에는 또 다른 차이점이 있는데 여기에서 언급해야 할 다른 답변을 다루지 못했습니다. 조회 프로세스입니다.

간단히 말해서 : Ruby 2.5 이전의 최상위 상수 조회로 인해, 중첩 클래스를 사용하는 경우 Ruby는 중첩 된 클래스 ( Object특히)를 잘못 찾을 수 있습니다.

루비에서 전 2.5 :
중첩 클래스 구조 : 당신은 클래스가 있다고 가정 X중첩 클래스를, Y또는 X::Y. 그런 다음 이름이 top 인 최상위 클래스도 있습니다 Y. 경우 X::Y로드되어 있지 않은 경우, 다음은 호출 할 때 발생 X::Y:

찾을 수없는 데 Y에서 X, 루비의 조상에서 그것을 찾기 위해 노력할 것입니다 X. 이후X클래스가 아니라 모듈이며 조상이 있으며 그중에는 [Object, Kernel, BasicObject]. 그래서, 그것은을 찾기 위해 시도 Y에서 Object성공적를 발견하는 경우,.

그러나 그것은 최상위 수준 Y이며 그렇지 않습니다 X::Y. 이 경고가 나타납니다.

warning: toplevel constant Y referenced by X::Y


중첩 모듈 구조 : 이전 예제 X에서 클래스가 아니라 모듈 이라고 가정합니다 .

모듈 자체는 조상으로 만 X.ancestors사용 [X]됩니다.

이 경우 루비는 Y의 조상 중 하나 를 찾을 수 없으며를 X던질 것 NameError입니다. 레일 (또는 자동 로딩 기능이있는 다른 프레임 워크)은 X::Y그 후에 로드를 시도 합니다.

자세한 내용은이 기사를 참조하십시오 : https://blog.jetbrains.com/ruby/2017/03/why-you-should-not-use-a-class-as-a-namespace-in-rails-applications/

In Ruby 2.5 :
최상위 상수 조회가 제거되었습니다.
이 버그가 발생할 염려없이 중첩 클래스를 사용할 수 있습니다.


3

이전 답변 외에도 Ruby의 모듈은 클래스입니다.

$ irb
> module Some end
=> nil
> Some.class
=> Module
> Module.superclass
=> Object

11
'Ruby의 클래스는 모듈'이라고 말하는 것이 더 정확합니다!
Tim Diggins

2
루비의 모든 것이 객체 일 수도 있지만, 클래스를 호출하는 클래스가 올바르지 않은 것 같습니다 : irb (main) : 005 : 0> Class.ancestors.reverse => [BasicObject, Kernel, Object, Module, Class]
Chad M

여기에 우리의 정의에 와서는 ""
ahnbizcad

모듈을 인스턴스화 할 수 없습니다. 즉, 모듈에서 객체를 생성 할 수 없습니다. 따라서 클래스와 달리 모듈에는 메소드가 없습니다 new. 따라서 모듈은 클래스 (소문자)라고 말할 수 있지만 클래스 (대문자)와 동일하지는 않습니다.
rmcsharry
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.