믹스인 패턴
여러 원본 테이블에서 메서드를 복사해 동작을 조합합니다.
믹스인 패턴은(는) CoddyKit의 무료 Lua Academy 강의입니다. 이것은 4개 중 3번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Lua Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Lua Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
믹스인이란 무엇인가요?
믹스인은 여러 클래스에 복사할 수 있는 메서드 테이블입니다. 상속과 달리 믹스인은 부모-자식 관계를 설정하지 않고 동작을 추가합니다. 하나의 클래스가 여러 믹스인의 동작을 결합할 수 있습니다.
local Flyable={}
function Flyable:fly()
print(self.name.." is flying!")
end
function Flyable:land()
print(self.name.." landed.")
end
local Bird={} Bird.__index=Bird
-- Mix in Flyable
for k,v in pairs(Flyable) do Bird[k]=v end
function Bird.new(name)
return setmetatable({name=name},Bird)
end
Bird.new("Eagle"):fly()여러 믹스인
하나의 클래스에 여러 믹스인을 적용하면 간단한 구성 요소를 결합해 복잡한 동작을 만들 수 있습니다. 이는 수평적 합성으로, 각 믹스인은 서로 독립적입니다.
local Serializable={}
function Serializable:toCSV(fields)
local row={}
for _,f in ipairs(fields) do row[#row+1]=tostring(self[f]) end
return table.concat(row,",")
end
local Loggable={}
function Loggable:log(msg)
print("["..os.date("%H:%M").."]["..self.name.."] "..msg)
end
local Entity={} Entity.__index=Entity
for k,v in pairs(Serializable) do Entity[k]=v end
for k,v in pairs(Loggable) do Entity[k]=v end
function Entity.new(n,v) return setmetatable({name=n,value=v},Entity) end
local e=Entity.new("item",42)
e:log("created"); print(e:toCSV({"name","value"}))믹스인 충돌
두 믹스인이 같은 메서드 이름을 정의하면 마지막으로 적용된 믹스인이 우선합니다. 유지할 믹스인의 버전을 선택하거나 두 버전을 모두 호출하도록 감싸서 충돌을 명시적으로 해결하십시오.
local A={speak=function(self) print(self.name.." (A)") end}
local B={speak=function(self) print(self.name.." (B)") end}
local C={} C.__index=C
for k,v in pairs(A) do C[k]=v end
for k,v in pairs(B) do C[k]=v end -- B.speak overwrites A.speak
-- Explicit resolution:
local origA, origB = A.speak, B.speak
function C:speak() origA(self); origB(self) end -- call both
local c=setmetatable({name="X"},C)
c:speak()include() 도우미
도우미 함수를 사용하면 믹스인을 더 깔끔하게 적용할 수 있습니다. include(Class, Mixin)은 믹스인의 메서드를 클래스에 복사하며, 이미 존재하는 필드는 건너뛰므로 기존 메서드를 덮어쓰지 않습니다.
local function include(cls, mixin)
for k, v in pairs(mixin) do
if cls[k] == nil then
cls[k] = v
end
end
return cls
end
local Comparable={
equals=function(a,b) return a:compareTo(b)==0 end,
lt=function(a,b) return a:compareTo(b)<0 end,
}
local Temp={} Temp.__index=Temp
include(Temp, Comparable)
function Temp.new(c) return setmetatable({c=c},Temp) end
function Temp:compareTo(other) return self.c-other.c end
local a,b=Temp.new(100),Temp.new(50)
print(b.lt and b:lt(a)) -- true (b < a)상태를 사용하는 믹스인
인스턴스마다 별도의 상태가 필요한 믹스인은 믹스인의 초기화 함수에서 해당 상태를 초기화합니다. 클래스 생성자는 믹스인의 초기화 함수를 호출해 인스턴스에 각 믹스인의 상태를 설정합니다.
local Observable={}
function Observable._init(self)
self._listeners={}
end
function Observable:on(event,fn)
self._listeners[event]=self._listeners[event] or {}
self._listeners[event][#self._listeners[event]+1]=fn
end
function Observable:emit(event,...)
for _,fn in ipairs(self._listeners[event] or {}) do fn(...) end
end
local Button={} Button.__index=Button
for k,v in pairs(Observable) do Button[k]=v end
function Button.new(label)
local self=setmetatable({label=label},Button)
Observable._init(self)
return self
end
local btn=Button.new("OK")
btn:on("click",function() print("Clicked!") end)
btn:emit("click")믹스인 모듈
믹스인의 동작을 별도의 모듈 파일로 분리해 패키징하십시오. 클래스 모듈에서 이를 불러온 다음 적용합니다. 이렇게 하면 각 동작의 관심사를 고유한 파일에 보관할 수 있어 코드베이스가 정돈됩니다.
-- In a real project:
-- local Loggable = require("mixins.loggable")
-- local Cacheable = require("mixins.cacheable")
-- local MyClass = {}
-- for k,v in pairs(Loggable) do MyClass[k]=v end
-- for k,v in pairs(Cacheable) do MyClass[k]=v end
local TimestampMixin={}
function TimestampMixin:setCreated() self.createdAt=os.time() end
function TimestampMixin:age() return os.time()-self.createdAt end
local Record={} Record.__index=Record
for k,v in pairs(TimestampMixin) do Record[k]=v end
function Record.new(data)
local self=setmetatable(data or {},Record)
self:setCreated()
return self
end
print(Record.new({}).createdAt>0)역할 기반 믹스인
믹스인 함수를 선택적으로 적용해 실행 중에 객체에 역할을 할당하십시오. 그러면 동작을 클래스 단위가 아니라 인스턴스별로 구성할 수 있습니다.
local Roles={}
Roles.admin={deleteUser=function(self) print(self.name.." deletes user") end}
Roles.editor={editPost=function(self) print(self.name.." edits post") end}
Roles.viewer={viewPost=function(self) print(self.name.." views post") end}
local User={} User.__index=User
function User.new(name,...)
local self=setmetatable({name=name},User)
for _,role in ipairs({...}) do
for k,v in pairs(Roles[role] or {}) do self[k]=v end
end
return self
end
local alice=User.new("Alice","admin","editor")
alice:deleteUser(); alice:editPost()믹스인 적용 확인
집합을 사용해 클래스에 적용된 믹스인을 추적하십시오. 이렇게 하면 중복 적용을 방지하고 클래스의 기능을 확인할 수 있습니다.
local function applyMixin(cls, mixin, name)
cls._mixins = cls._mixins or {}
if cls._mixins[name] then return end -- already applied
for k,v in pairs(mixin) do
if k~="_init" then cls[k]=cls[k] or v end
end
cls._mixins[name] = true
end
local Fly={fly=function(self) print(self.name.." flies") end}
local Bird={} Bird.__index=Bird
applyMixin(Bird,Fly,"Fly")
applyMixin(Bird,Fly,"Fly") -- no-op, already applied
print(Bird._mixins.Fly) -- true믹스인과 상속과 합성
세 가지 합성 방식이 있습니다. 상속은 ~이다 관계와 엄격한 계층 구조를 사용하고, 믹스인은 메서드를 복사해 평면적으로 구성하며, 합성은 ~을 가진다 관계와 위임을 사용합니다. 관계에 따라 선택하십시오. 동물은 생명체이다(상속), 생명체는 날 수 있다(믹스인), 자동차는 엔진을 가진다(합성).
-- Inheritance: "is-a"
-- local Dog = extends(Animal)
-- Mixin: "can-do"
-- include(Dog, Swimmable)
-- Composition: "has-a"
-- local Dog = {}
-- function Dog.new()
-- return {collar=Collar.new(),chip=Chip.new()}
-- end
print("Pick the right tool for the relationship")믹스인 템플릿
표준 믹스인 템플릿에서는 믹스인 테이블에 메서드와 인스턴스별 상태를 위한 선택적 _init, 그리고 충돌 감지를 위한 이름 필드가 포함됩니다. 일관된 패턴을 따르면 여러 프로젝트에서 믹스인을 쉽게 합성할 수 있습니다.
-- Standard mixin template:
local EventMixin = {}
EventMixin._name = "EventMixin"
function EventMixin._init(self)
self._events = {}
end
function EventMixin:on(e, fn)
self._events[e] = self._events[e] or {}
self._events[e][#self._events[e]+1] = fn
end
function EventMixin:trigger(e, ...)
for _, fn in ipairs(self._events[e] or {}) do fn(...) end
end
-- return EventMixin
print("EventMixin ready for use")믹스인 질문
믹스인을 클래스에 어떻게 적용하나요?
복습: 믹스인 패턴
믹스인은 원본 테이블의 메서드를 대상 클래스에 복사하여 상속 없이 동작을 합성합니다. 직렬화, 로깅, 이벤트 처리처럼 여러 부분에 걸쳐 적용되는 관심사에 사용하십시오.
자주 묻는 질문
“믹스인 패턴” 강의는 무료인가요?
네 — “믹스인 패턴” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Lua Academy 강의 전체를 잠금 해제할 수 있습니다. Lua Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
“믹스인 패턴”에서 뭘 배우나요?
여러 원본 테이블에서 메서드를 복사해 동작을 조합합니다. 브라우저에서 직접 실행하는 실습 코드로 Lua Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
Lua Academy을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 Lua Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 3번째 강의입니다.
“믹스인 패턴” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 Lua Academy 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 Lua Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.