Menu
Coddy logo textTech

생성자 확장하기

Coddy Lua 여정의 Object Oriented Programming 섹션에 포함된 레슨 — 70개 중 33번째.

자식 클래스가 부모 클래스로부터 상속받을 때, 종종 자식 클래스의 생성자가 부모 클래스가 초기화하는 모든 항목과 자식 클래스에 특화된 추가 필드들을 함께 초기화하기를 원할 것입니다. 코드를 중복해서 작성하는 대신, 부모 클래스의 생성자를 직접 호출할 수 있습니다.

이 기법은 부모 클래스 이름을 사용하여 :new() 메서드를 호출하고, 부모 클래스를 첫 번째 인자로 전달하는 방식입니다. 점 구문(dot syntax)Vehicle.new(Vehicle, speed)를 사용하거나, 이와 동일한 콜론 구문(colon syntax)Vehicle:new(speed)를 사용할 수 있습니다. 두 방식 모두 유효하며 동일한 결과를 생성합니다. 이를 통해 모든 부모 초기화가 완료된 기본 객체가 생성되며, 이후 자식 클래스에 맞춰 이를 사용자 정의할 수 있습니다.

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

자식 생성자의 주요 단계를 살펴보세요: 먼저, speed 필드가 이미 설정된 객체를 얻기 위해 Vehicle.new(Vehicle, speed) (또는 이와 동일한 Vehicle:new(speed))를 호출합니다. 그런 다음 자식 클래스 전용인 brand 필드를 추가합니다. 마지막으로, 인스턴스가 자식 클래스에서 메서드를 먼저 찾도록 메타테이블을 Vehicle에서 Car로 변경합니다.

local myCar = Car:new(120, "Toyota")
print(myCar.speed)  -- 출력: 120
print(myCar.brand)  -- 출력: Toyota

이 패턴은 코드를 DRY하게 유지해 줍니다. 공유된 초기화 로직은 부모에 위치하며, 각 자식은 자신의 고유한 속성만 처리합니다.

challenge icon

챌린지

쉬움

자식 클래스가 부모 클래스의 생성자를 확장하는 생명체 계층 구조를 만들어 봅시다! 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 인스턴스를 생성합니다. 상속된 기능과 자식 클래스 전용 기능이 함께 작동함을 보여주기 위해 두 메서드를 모두 호출합니다.

두 개의 입력을 받게 됩니다:

  1. 새의 이름
  2. 새의 날개 길이 (숫자)

메인 파일에서 주어진 이름과 날개 길이를 가진 Bird를 생성하세요. 그런 다음 별도의 줄에 :describe():info()를 차례로 호출하세요.

:describe() 메서드(Animal에서 상속됨)는 다음을 출력해야 합니다:

Animal: {name}

:info() 메서드(Bird에서 정의됨)는 다음을 출력해야 합니다:

{name} has a wingspan of {wingspan}

예를 들어, 입력이 Eagle200이라면 출력은 다음과 같아야 합니다:

Animal: Eagle
Eagle has a wingspan of 200

여기서 핵심은 Bird 생성자가 이름 초기화를 중복해서 수행하지 않는다는 것입니다. 그 작업은 Animal의 생성자에 위임하고, 새에게 고유한 부분만 추가합니다. 이를 통해 적절한 상속 체인을 구축하면서 코드의 중복을 피할 수 있습니다(DRY 원칙)!

치트 시트

자식 클래스에서 부모 클래스 생성자를 호출하려면, 부모 클래스 자체를 첫 번째 인자로 전달하면서 점(dot) 구문을 사용하여 부모 클래스 이름을 사용하세요:

local obj = ParentClass.new(ParentClass, parentArgs)

부모 생성자를 호출한 후, 자식 전용 필드를 추가하고 메타테이블을 자식 클래스에 다시 연결합니다:

local Vehicle = {}
Vehicle.__index = Vehicle

function Vehicle:new(speed)
    local obj = {speed = speed}
    setmetatable(obj, Vehicle)
    return obj
end

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

local myCar = Car:new(120, "Toyota")
print(myCar.speed)  -- 출력: 120
print(myCar.brand)  -- 출력: Toyota

이 패턴은 초기화 로직을 DRY(Don't Repeat Yourself)하게 유지합니다. 공통 설정은 부모 클래스에 위치하고, 자식 클래스는 고유한 속성만 처리합니다.

직접 해보기

-- main.lua: 진입점

local Bird = require('Bird')

-- 입력 읽기
local name = io.read()
local wingspan = tonumber(io.read())

-- TODO: 주어진 이름과 날개 길이를 가진 Bird 인스턴스 생성

-- TODO: 새에 대해 :describe() 호출 (Animal에서 상속됨)

-- TODO: 새에 대해 :info() 호출 (Bird에 정의됨)
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

Object Oriented Programming의 모든 레슨