Verse에서는 다른 클래스의 필드 및 메서드를 추가 또는 수정하여 다른 클래스의 정의를 확장하는 클래스를 생성할 수 있습니다. 한 클래스가 다른 클래스에서 정의를 상속하기 때문에 이것을 보통 서브클래스 또는 상속이라고 부릅니다.
서브클래스의 예시로 클래스 설계 장치를 살펴보겠습니다. 클래스 설계 장치를 사용하면 플레이어 캐릭터용 캐릭터 클래스를 생성하여 탱커 또는 대미지 딜러 캐릭터 같은 특정 캐릭터 클래스의 어트리뷰트와 인벤토리를 정의할 수 있습니다.
![]() |
![]() |
| 클래스 설계 장치로 생성한 대미지 딜러 캐릭터 클래스 | 클래스 설계 장치로 생성한 탱커 캐릭터 클래스 |
Verse에서는 tank 클래스와 dps 클래스를 다음과 같이 생성합니다.
tank := class():
StartingShields : int
MaxShields : int
AllowOvershield : logic
DamageReduction : int
dps := class():
StartingShields : int
MaxShields : int
AllowOvershield : logic
MovementMultiplier : float
두 클래스의 일부 필드가 같으므로 클래스의 공유 프로퍼티 및 동작을 포함하는 수퍼클래스로 중복을 줄일 수 있습니다. player_character 라는 수퍼클래스를 만들고 tank 와 dps 를 player_character 의 서브클래스로 만들겠습니다.
player_character := class():
StartingShields : int
MaxShields : int
AllowOvershield : logic
dps := class(player_character):
MovementMultiplier : float
tank := class(player_character):
DamageReduction : int
tank 및 dps 클래스는 player_character 의 서브클래스이므로 player_character 클래스의 필드와 메서드를 자동으로 상속합니다. 따라서 이 클래스에서 수퍼클래스와 다른 부분만 지정하면 됩니다.
예를 들어 dps 클래스에는 MovementMultiplier 필드만 추가하고, tank 클래스에는 DamageReduction 필드만 추가하면 됩니다. 이 구성은 나중에 두 클래스의 공유 행동을 변경할 때 수퍼클래스에서만 변경하면 되기 때문에 유용합니다.
Verse에서는 서브클래스에 메서드를 추가함으로써 변경사항을 더 많이 추가하여 탱커 클래스와 대미지 딜러 클래스를 차별화할 수 있습니다.
수퍼클래스와 서브클래스 사이의 관계를 활용할 수 있다는 점에서 서브클래스는 유용합니다. 상속 덕분에 tank 의 인스턴스는 특수한 player_character 이며, dps 의 인스턴스도 특수한 player_character 이고, 이를 is-a 관계라 합니다. tank 와 dps 둘 다 같은 수퍼클래스에서 갈라져 나온 서브클래스이므로, tank 와 dps 사이에는 관계가 없습니다.
오버라이드(Override) 지정자
초기 값을 갖는 클래스의 인스턴스를 생성하려면 일반적으로 인스턴스를 생성하는 함수를 만듭니다. 예를 들면 다음과 같습니다.
CreateDPSPlayerCharacter() : dps =
return dps{StartingShields := 0, MaxShields := 0, AllowOvershield := false, MovementMultiplier := 1.9}
CreateTankPlayerCharacter() : tank =
return tank{StartingShields := 100, MaxShields := 200, AllowOvershield := true, DamageReduction := 50}
CreateTankPlayerCharacter() 및 CreateDPSPlayerCharacter() 함수는 적절한 초기 값을 갖는 인스턴스를 생성합니다. 또는 인스턴스 생성 시 많은 초기 값을 입력할 필요가 없도록 수퍼클래스의 필드를 오버라이드 하고 초기 값을 할당할 수 있습니다.
예를 들어 이전 섹션의 tank 클래스에 오버라이드 필드를 추가하면 다음과 같습니다.
tank := class(player_character):
StartingShields<override> : int = 100
MaxShields<override> : int = 200
AllowOvershield<override> : logic = true
DamageReduction : int = 50
CreateTankPlayerCharacter() : tank =
return tank{}
서브클래스에 있는 메서드 또한 오버라이드할 수 있습니다. 즉 오버라이드된 메서드를 사용할 수 있는 모든 위치에서 오버라이딩 메서드를 사용할 수 있습니다. 이는 다음과 같은 의미입니다.
- 메서드는 최소한 오버라이드된 메서드에 의해 허용되는 실행인자를 허용해야 합니다. 따라서 파라미터 타입은 오버라이드된 함수의 파라미터 타입의 수퍼타입이 되어야 합니다.
- 메서드는 오버라이드된 메서드가 가질 수 없는 값을 반환해서는 안 되므로, 반환 타입은 오버라이드된 메서드의 반환 타입의 하위 타입이어야 합니다.
- 메서드가 오버라이드된 메서드보다 더 많은 영향을 미쳐서는 안 됩니다. 따라서 이펙트 지정자는 오버라이드된 메서드의 이펙트 지정자의 서브타입이 되어야 합니다.
Super
Self와 유사하게 (super:) 를 사용하여 수퍼클래스에 구현된 필드와 메서드에 액세스할 수 있습니다. (super:) 를 사용할 수 있으려면 필드 또는 메서드가 수퍼클래스 정의에 구현되어 있어야 합니다.
pet := class():
Sound : string
Speak() : void =
Log(Sound)
cat := class(pet):
Sound<override> : string = "Meow"
Speak<override>() : void =
(super:)Speak() # 출력 로그에 "Meow"가 표시됩니다
Log("Purr") # 출력 로그에 "Purr"가 표시됩니다
서브클래스 바디 내 블록 표현식
서브클래스 바디 내 모든 block 표현식은 수퍼클래스 바디 내 명시된 block 표현식 이후에 실행됩니다. 예를 들어 다음 코드에서 cat 클래스의 인스턴스 MrSnuffles가 생성되면 Speak() 이 실행된 후에 Purr() 가 실행됩니다.
pet := class():
Speak() : void =
...
block:
Speak()
cat := class(pet):
Purr() : void =
...
block:
Purr()
MrSnuffles := cat{}
추상 지정자
클래스 또는 클래스 메서드에 abstract 지정자가 있는 경우 클래스의 인스턴스를 생성할 수 없습니다. 추상 클래스는 일부 구현을 갖춘 수퍼클래스 또는 일반적인 인터페이스로 사용됩니다. 수퍼클래스의 인스턴스를 갖추는 것이 적절하지 않지만 유사한 클래스 간에 프로퍼티 및 행동을 복제하고 싶지 않은 경우에 유용합니다.
다음 예시에서 pet은 추상적인 개념이므로 구체적인 인스턴스를 생성할 수 없지만, pet cat 또는 pet dog은 구체적이므로 서브클래스들은 추상 클래스로 선언되어 있지 않습니다.
pet := class<abstract>():
Speak() : void
cat := class(pet):
Speak() : void =
...
dog := class(pet):
Speak() : void =
...

