믹스인 vs 상속
Coddy Lua 여정의 Object Oriented Programming 섹션에 포함된 레슨 — 70개 중 53번째.
동작을 공유하는 두 가지 방법을 배웠습니다: 상속(inheritance)은 프로토타입 체인을 통해 "is-a" 관계를 생성하는 반면, 믹스인(mixins)은 함수를 클래스에 직접 복사합니다. 하지만 각 접근 방식은 언제 사용해야 할까요?
객체가 근본적인 정체성을 공유할 때 상속(inheritance)을 사용하세요. Bird는 Animal의 *일종(is an)*입니다. Bird가 :eat()이나 :sleep()과 같은 핵심적인 동물의 행동을 상속받는 것은 타당합니다. 이 관계는 영구적이며 객체가 근본적으로 무엇인지를 정의합니다.
객체의 핵심 정체성을 정의하지 않는 기능을 추가하고 싶을 때 믹스인(mixins)을 사용하세요.
모든 새가 날 수 있는 것은 아닙니다. 펭귄과 타조는 새이지만 날지 못합니다. 비행은 새라는 존재의 정의적인 특성이 아니라 하나의 능력입니다. Flyable 믹스인을 사용하면 모든 새에게 비행 능력을 강제하지 않고도, 비행이 필요한 새에게만 비행 기능을 추가할 수 있습니다.
두 가지 접근 방식은 서로 훌륭하게 조화를 이룹니다:
-- Bird는 Animal을 상속합니다 (is-a 관계)
local Bird = {}
Bird.__index = Bird
setmetatable(Bird, {__index = Animal})
-- Flyable은 능력(mixin)입니다
local Flyable = {}
function Flyable:fly()
print(self.name .. " soars through the sky!")
end
-- 날 수 있는 새에게만 mixin을 적용합니다
applyMixin(Bird, Flyable)이 패턴은 공유된 정체성을 위한 명확한 클래스 계층 구조와 필요할 때마다 추가할 수 있는 유연한 기능이라는 두 가지 장점을 모두 제공합니다.
챌린지
쉬움상속과 믹스인(mixin)을 언제 사용해야 하는지 보여주는 작은 생태계를 만들어 봅시다! Animal 기본 클래스, 이를 상속받는 Bird, 그리고 Swimmable 믹스인을 만들 것입니다. 모든 새는 동물이지만, 모든 새가 수영을 할 수 있는 것은 아니기 때문입니다.
코드는 네 개의 파일로 구성됩니다:
Animal.lua: 이름을 저장하는 생성자:new(name)를 가진 기본 Animal 클래스를 만듭니다.{name} is eating을 출력하는:eat()메서드를 포함하세요. 이것은 모든 동물이 공유하는 핵심 정체성을 나타냅니다.Swimmable.lua:{self.name} is swimming을 출력하는:swim()메서드를 가진 믹스인을 만듭니다. 이것은 하나의 능력입니다. 모든 생명체가 수영을 할 수 있는 것은 아니므로 상속 체인의 일부가 되어서는 안 됩니다.Bird.lua: Animal을 상속받는 Bird 클래스를 만듭니다. 생성자:new(name, canSwim)는 부모 생성자를 호출하고 이 새가 수영할 수 있는지 여부를 저장해야 합니다. Bird 클래스에 Swimmable 믹스인을 적용하세요. 새 특유의 행동인{name} chirps!를 출력하는:chirp()메서드를 포함하세요.main.lua: 여기에applyMixin헬퍼 함수를 작성하세요. 새의 이름과 수영 가능 여부(yes또는no)에 대한 입력을 읽습니다. Bird 인스턴스를 생성한 다음 그 능력을 보여주세요::eat()(Animal에서 상속됨) 호출,:chirp()(Bird 자체 메서드) 호출, 그리고 새가 수영할 수 있는 경우에만:swim()을 호출합니다.
두 개의 입력을 받게 됩니다:
- 새의 이름 (문자열)
- 새가 수영할 수 있는지 여부:
yes또는no
새가 수영할 수 있는 경우, 출력은 다음과 같아야 합니다:
{name} is eating
{name} chirps!
{name} is swimming새가 수영할 수 없는 경우, 출력은 다음과 같아야 합니다:
{name} is eating
{name} chirps!예를 들어, 입력이 Duck과 yes인 경우 출력은 다음과 같아야 합니다:
Duck is eating
Duck chirps!
Duck is swimming입력이 Sparrow와 no인 경우 출력은 다음과 같아야 합니다:
Sparrow is eating
Sparrow chirps!이 챌린지는 핵심적인 차이점을 보여줍니다: Bird는 Animal의 일종(상속)이지만, 수영은 선택적인 능력(믹스인)입니다. 믹스인은 클래스에 적용되지만, 메인 로직은 개별 새의 능력에 따라 그것을 사용할지 여부를 결정합니다!
치트 시트
객체들이 근본적인 정체성("is-a" 관계)을 공유할 때는 상속을 사용하세요. 객체의 핵심 정체성을 정의하지 않는 기능을 추가할 때는 믹스인을 사용하세요.
상속은 프로토타입 체인을 통해 영구적인 관계를 생성합니다:
local Bird = {}
Bird.__index = Bird
setmetatable(Bird, {__index = Animal})믹스인은 선택적으로 적용할 수 있는 선택적인 기능을 추가합니다:
local Flyable = {}
function Flyable:fly()
print(self.name .. " soars through the sky!")
end
applyMixin(Bird, Flyable)이 두 가지 방식은 함께 작동합니다. 공유된 정체성에는 상속을 사용하고, 일부 인스턴스에만 필요한 유연한 기능에는 믹스인을 사용하세요.
직접 해보기
-- main.lua: 진입점 - 새를 생성하고 그 능력을 시연합니다
-- 클래스에 믹스인을 적용하는 헬퍼 함수
local function applyMixin(class, mixin)
-- TODO: 믹스인의 모든 메서드를 클래스로 복사합니다
end
-- Bird를 require하기 전에 Swimmable 믹스인을 Bird에 적용합니다
-- 참고: Bird.lua 또는 여기에서 이를 처리해야 할 수도 있습니다
local Bird = require('Bird')
local Swimmable = require('Swimmable')
-- Bird 클래스에 믹스인 적용
applyMixin(Bird, Swimmable)
-- 입력 읽기
local name = io.read()
local canSwimInput = io.read()
-- TODO: 입력("yes" 또는 "no")을 바탕으로 새가 수영할 수 있는지 결정합니다
-- TODO: 이름과 수영 능력을 가진 Bird 인스턴스를 생성합니다
-- TODO: eat() 호출 - Animal로부터 상속됨
-- TODO: chirp() 호출 - Bird 자체 메서드
-- TODO: 새가 수영할 수 있는 경우에만 swim() 호출
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.