생성자 확장하기
Coddy Lua 여정의 객체 지향 프로그래밍 (Object Oriented Programming) 섹션에 포함된 레슨. 70개 중 33번째.
child class가 parent로부터 inherits할 때, parent가 초기화할 모든 것에 더해 child에 특정한 추가 필드까지 child의 constructor가 초기화하도록 하려는 경우가 많습니다. 코드를 중복하는 대신, parent의 constructor를 직접 호출할 수 있습니다.
이 기법은 parent 클래스 이름을 사용하여 해당 클래스의 :new() 메서드를 호출하고, 첫 번째 인수로 self를 전달하는 방식입니다. 이렇게 하면 모든 parent 초기화가 적용된 기본 object가 생성되며, 이후 이를 child에 맞게 사용자 지정합니다.
local Vehicle = {}
Vehicle.__index = Vehicle
function Vehicle:new(speed)
local obj = {speed = speed}
setmetatable(obj, Vehicle)
return obj
end
-- Car는 Vehicle을 상속합니다
local Car = {}
Car.__index = Car
setmetatable(Car, {__index = Vehicle})
function Car:new(speed, brand)
local obj = Vehicle.new(Vehicle, speed) -- 부모 생성자 호출
obj.brand = brand -- 자식 전용 필드 추가
setmetatable(obj, Car) -- Car에 다시 연결
return obj
end자식 생성자의 주요 단계를 살펴보세요. 먼저 Vehicle.new(Vehicle, speed)를 호출하여 speed 필드가 이미 설정된 객체를 가져옵니다. 그런 다음 자식에 특화된 brand 필드를 추가합니다. 마지막으로 메타테이블을 Vehicle에서 Car로 변경하여 인스턴스가 먼저 자식 클래스에서 메서드를 찾도록 합니다.
부모 생성자를 호출할 때 콜론 구문을 사용할 수도 있습니다. Vehicle:new(speed)는 Vehicle을 첫 번째 인수로 전달하므로 Vehicle.new(Vehicle, speed)와 동일합니다. 두 형식 모두 여기서 올바르게 작동하므로, 더 읽기 쉽다고 생각되는 형식을 자유롭게 사용하세요.
local myCar = Car:new(120, "Toyota")
print(myCar.speed) -- 출력: 120
print(myCar.brand) -- 출력: Toyota이 패턴은 코드를 DRY하게 유지합니다. 공유 초기화 로직은 parent에 있고, 각 child는 고유한 속성만 처리합니다.
챌린지
쉬움자식 클래스가 부모 클래스의 생성자를 확장하는 생물 계층 구조를 만들어 봅시다! Animal 부모 클래스와 Bird 자식 클래스를 만들고, Bird의 생성자가 Animal의 생성자를 호출하여 공통 초기화를 처리한 다음 고유한 속성을 추가하도록 합니다.
코드를 세 개의 파일로 구성합니다.
Animal.lua: 동물의 이름을 저장하는:new(name)생성자를 포함하여 부모 클래스를 정의합니다. 동물의 이름을 출력하는:describe()메서드도 포함합니다.__index를 사용하는 표준 클래스 패턴을 설정하고 클래스를 반환합니다.Bird.lua: Animal에서 상속하는 자식 클래스를 정의합니다. Bird의:new(name, wingspan)생성자는Animal.new(Animal, name)을 사용하여 부모 생성자를 호출해 이름을 초기화한 다음, 객체에wingspan필드를 추가하고 마지막으로 메타테이블을 Bird에 다시 연결해야 합니다. 이름과 날개 길이를 특정 형식으로 출력하는:info()메서드도 포함합니다.main.lua: Bird 모듈을 require하고, 두 개의 입력값(이름과 날개 길이)을 읽은 다음, Bird 인스턴스를 생성하고 두 메서드를 모두 호출하여 상속된 기능과 자식 클래스 고유의 기능이 함께 작동하는 것을 보여 줍니다.
두 개의 입력값을 받습니다.
- 새의 이름
- 새의 날개 길이(숫자)
main 파일에서 주어진 이름과 날개 길이로 Bird를 생성합니다. 그런 다음 별도의 줄에서 :describe()를 호출하고 이어서 :info()를 호출합니다.
:describe() 메서드(Animal에서 상속됨)는 다음을 출력해야 합니다.
Animal: {name}:info() 메서드(Bird에 정의됨)는 다음을 출력해야 합니다.
{name} has a wingspan of {wingspan}예를 들어 입력값이 Eagle과 200이면 출력은 다음과 같아야 합니다.
Animal: Eagle
Eagle has a wingspan of 200여기서 중요한 점은 Bird 생성자가 이름 초기화 코드를 중복하지 않는다는 것입니다. 해당 작업을 Animal의 생성자에 위임한 다음, 새에게 고유한 부분만 추가합니다. 이렇게 하면 적절한 상속 체계를 구축하면서 코드를 DRY하게 유지할 수 있습니다!
직접 해보기
-- main.lua: 진입점
local Bird = require('Bird')
-- 입력 읽기
local name = io.read()
local wingspan = tonumber(io.read())
-- TODO: 주어진 name과 wingspan으로 Bird 인스턴스 생성
-- TODO: bird에서 :describe() 호출 (Animal에서 상속됨)
-- TODO: bird에서 :info() 호출 (Bird에 정의됨)
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.
객체 지향 프로그래밍 (Object Oriented Programming)의 모든 레슨
직접 연습해 보세요: 온라인 Lua 컴파일러