아직까지 루비와 레일스를 취미삼아 공부하다 보니 루비를 다른 언어들과 차별되게 하는 것들이 클래스와 클래스와 객체를 동적으로 확장하는 것과 블록이 아닌가 하는 생각이 들었습니다. 거창한 제목을 붙여 보았습니다만 먼저 클래스와 객체의 동적인 확장과 관련된 것을 공부삼아 간단히 정리해 보겠습니다. 내용은 틀린것이 있으면 수정하고 필요하다면 보완하도록 하겠습니다.
모든것은 객체
루비에서 모든 것은 객체입니다. 다른 객체지향언어들과 같이 모든 객체는 클래스를 가집니다. 객체에는 각 인스턴스 변수가 저장되어 있고 클래스에는 메소드가 저장됩니다. 클래스의 인스턴스에 메소드가 호출되면 인스턴스의 클래스를 찾아서 해당 클래스와 상위 클래스 들에서 메소드를 찾아서 실행시키게됩니다. 메소드를 호출할때는 기본적으로 객체를 지정해야하며 이를 생략하면 self가 포함된 것으로 간주됩니다. self는 상황에 따라 다른 값을 가지게 되는데 보통 irb에서는 main이란 Object가 됩니다.
싱글톤 클래스
각 객체에는 그 객체만을 위한 메소드나 클래스를 지정할 수 있습니다. 이를 싱글톤 메소드 혹은 클래스라고 부르는데 이때 루비는 그 객체만의 싱글톤 클래스란 무명 클래스를 만들어 객체와 부착시킵니다. 싱글톤 클래스는 객체와 객체의 클래스 사이에 위치하여 객체만의 메소드를 추가하거나 클래스의 메소드를 오버라이드 할수 있도록 해줍니다.
클래스 역시 객체입니다. 루비에서 모든 클래스들은 기본 클래스 Class의 인스턴스입니다. 위에서 언급한 객체와 클래스의 관계를 생각할 때 각 객체 클래스의 기본 클래스 Class에 메소드를 저장하면 모든 클래스가 같은 클래스 메소드를 가지게 됩니다. 이를 방지하게 위해 클래스 메소드들은 앞서 이야기한 싱글톤 클래스를 이용하여 구현됩니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
class String self.module_eval do define_method :foo do puts "inside foo" end end (class << self; self; end).module_eval do define_method :bar do puts "inside bar" end end end "string".foo # => "inside foo" String.bar # => "inside bar" |
인스턴스 변수, 클래스 변수, 클래스 인스턴스 변수
각 객체는 내부의 인스턴스 변수를 가질 수 있으며 클래스는 클래스 변수를 가질 수 있습니다. 루비에서는 클래스 인스턴스 변수란 것이 추가됩니다. 클래스 메소드가 아닌 메소드의 정의에서 @가 붙는 변수는 보통의 인스턴스 변수가 되지만 클래스의 정의나 클래스 메소드의 정의에 @가 붙는 변수가 클래스 인스턴스 변수가 됩니다.
클래스 인스턴스 변수는 객체의 메소드에서 접근할 수 없으므로 보통의 경우 클래스 변수를 사용하면 되지만, 클래스에서 상속이 일어나는 경우 클래스 변수는 자손과 공유하게 되므로 자손과 따로 클래스 전용의 변수를 사용하고자 한다면 클래스 인스턴스 변수를 사용해야 합니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
class Base @@var1 = nil class << self attr_accessor :var2 end def self.var1 p @@var1 end end class A < Base @@var1 = 'A' self.var2 = 'B' end class B < Base @@var1 = 'B' self.var = 'B' end >> A.var1 # 'B' >> B.var1 # 'B' >> A.var2 # 'A' >> B.var2 # 'B' |
참고
- Programming Ruby: Pragmatic programmer’s guide, 2nd edition
- The Ruby Way, Second Edition
- http://www.rubycentral.com/faq/rubyfaq-8.html
- http://ola-bini.blogspot.com/2006/09/ruby-singleton-class.html
