루비 언어의 기본 디자인은 최초의 객체지향 언어인 스몰토크에서 많은 영향을 받았습니다. 루비는 순수 객체지향 언어이며, 가비지 콜렉션을 지원합니다.
좀더 쉽게 풀어 써 보죠. 순수 객체지향 언어라는 말은, 루비의 모든 것은 오브젝트라는 뜻입니다. C++나 Java와 달리 정수, 실수, 문자열 등의 primitive type 또한 오브젝트이며, 모든 클래스는 Object라는 최상위 클래스를 상속합니다. 가비지 콜렉션을 지원한다는 말은, Java와 같이 새로운 클래스를 만들 때 메모리를 할당하고 해제하는 작업(new/delete)을 직접 해줄 필요가 없다는 뜻입니다.
그럼 간단한 클래스를 하나 만들어볼까요.
# 클래스 이름은 항상 대문자로 시작해야 합니다.
# CamelCase 스타일(각 단어의 첫글자를 대문자로 하여 모두 붙여쓰는 스타일)로
# 명명하는 것이 보통입니다.
class Cat
# 클래스 생성자 메소드의 이름은 항상 initialize입니다.
# 메모리 해제는 가비지 컬렉터가 알아서 해주기 때문에 소멸자는 만들지 않아도 됩니다.
def initialize(name)
# 인자를 하나 받아 그 인자를 @name 이라는 인스턴스 변수에 저장합니다.
@name = name
end
# 클래스 내부에서 메소드를 정의하는 방법은 일반 함수를 정의하는 것과 동일합니다.
def say
puts @name + " says meow!"
end
end
# Cat 클래스의 인스턴스를 만들기 위해서는 Cat.new 메소드를 호출해야 합니다.
# new 메소드는 내부적으로 initialize를 호출합니다.
a_cat = Cat.new("Nyang")
# 인스턴스의 메소드를 호출해봅시다.
a_cat.say
#=> Nyang says meow!
생성자 메소드 initialize와 멤버 메소드 say를 가진 클래스 Cat이 만들어졌습니다.
여기서 주목할 부분은 인스턴스 변수 @name입니다. 루비의 인스턴스 변수(instance variable)는 C++/Java의 멤버 변수(member variable), 혹은 필드(field)에 해당하는 개념이며, 하나의 인스턴스 내부에서만 공유되는 변수입니다. 루비의 인스턴스 변수는 항상 변수 이름 앞에 '@'를 붙여 줘야 합니다.
이번에는 만들어진 클래스를 상속해봅시다
# 클래스를 상속하기 위해서는 < 연산자를 사용합니다.
class Lion < Cat
def say
# 키워드 super의 사용법은 Java(혹은 MS VC++)의 super와는 다르므로 혼동하지 마세요.
# 루비의 super는 자신의 상위 클래스에 존재하는 같은 메소드를 찾아 호출합니다.
# 이 경우 Tiger#say 내에서 호출되었으므로 상위클래스의 say인 Cat#say를 호출할 것입니다.
super
puts "...oops, roars!"
end
end
a_lion = Lion.new("Simba")
a_lion.say
#=> Simba says meow!
#=> ...oops, roars!이제 위에서 만든 Cat 클래스에서, 고양이의 이름을 바꾸고 싶습니다. 어떻게 하면 좋을까요?
결론부터 말하자면, 루비의 모든 인스턴스 변수는 private, 즉 클래스 내부에서만 접근 가능합니다. (접근 제어 키워드인 private, protected, public에 대해서는 다음 챕터에서 좀더 자세히 다루겠습니다.) 인스턴스 변수의 접근 권한을 public으로 바꿀 방법도 없습니다. 그래서 항상 getter와 setter 메소드(private 변수에 접근하기 위해 사용하는 public 메소드)를 사용해야 합니다.
그런데 루비의 getter와 setter 메소드는 Java의 getName과 setName과 같은 지루한 이름 대신, 좀더 특수한 문법을 허용합니다.
# 기존의 Cat 클래스에서는 @name에 직접 접근하는 것이 불가능합니다.
a_cat.name = "kitty" #=> NoMethodError
# Cat 클래스를 재정의합시다.
class Cat
def initialize(name)
@name = name
end
def say
puts @name + " says meow!"
end
# 지난 챕터에서 언급했듯이, 메소드에 return 문이 존재하지 않으면
# 자동으로 마지막 줄의 리턴값을 돌려줍니다.
# 따라서 이 메소드는 @name의 값을 리턴하는 함수입니다.
def name
@name
end
# 루비 메소드는 마지막 글자에 한해 '=', '?', '!'와 같은 특수문자를 허용합니다.
def name=(new_name)
@name = new_name
end
end
a_cat = Cat.new("nyang")
a_cat.name = "kitty"
puts a_cat.name
#=> "kitty"
위에서 눈치챌 수 있는 것처럼, 인터프리터는 a_cat.name문을 a_cat의 메소드 name()을 호출하는 것으로 해석하고, a_cat.name = "kitty" 문은 a_cat의 메소드 name=()을 인자 "kitty"로 주어 호출하는 것으로 해석합니다. (루비 메소드를 호출할 때는 괄호를 붙이지 않아도 된다는 것, 기억하시죠?) 결과적으로, 인스턴스 변수를 외부에서 투명하게 볼 수 있는 public 변수처럼 다루는 getter와 setter 메소드가 만들어졌습니다.
그러나 여기서 끝이라면 재미가 없죠. 루비는 인스턴스 변수인 @name을 외부에서 접근하기 위해 6줄이나 사용하는 불필요한 낭비를 허용하지 않습니다. 다음과 같은 일종의 매크로를 사용하면 같은 일을 더 짧게 할 수 있습니다.
class Cat
# 이 매크로는 인스턴스 변수 @name에 getter인 name 메소드와
# setter인 name= 메소드를 자동으로 만들어 줍니다.
# getter만 만들고 싶다면 attr_accessor 대신 attr_reader를,
# setter만 만들고 싶다면 attr_writer 매크로를 사용할 수 있습니다.
# 여러 개의 인스턴스 변수에 getter와 setter를 만들고 싶다면,
# attr_accessor :name1, :name2 와 같이 쉼표로 연결해주면 됩니다.
attr_accessor :name
def initialize(name)
@name = name
end
def say
puts @name + " says meow!"
end
end
이와 같이 쓰면 위에서 사용한 2개의 메소드와 의미상 동일한 메소드가 자동으로 만들어집니다.
'루비 튜토리얼' 카테고리의 다른 글
| 게으른 프로그래머를 위한 루비 튜토리얼 #5 (3) | 2011/09/15 |
|---|---|
| 게으른 프로그래머를 위한 루비 튜토리얼 #4 (1) | 2008/10/22 |
| 게으른 프로그래머를 위한 루비 튜토리얼 #3 (0) | 2008/01/12 |
| 게으른 프로그래머를 위한 루비 튜토리얼 #2 (0) | 2007/09/04 |
| 게으른 프로그래머를 위한 루비 튜토리얼 #1 (18) | 2007/03/31 |
