0Pricing
Lua Academy · บทเรียน

การเขียนทับและพหุรูป

เขียนทับเมธอดของคลาสแม่และทำให้เกิดการเลือกใช้เมธอดแบบพหุรูปขณะทำงาน

การเขียนทับและพหุรูป เป็นบทเรียน Lua Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Lua Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Lua Academy มีบทเรียนทั้งหมด 4 บทเรียน

การเขียนทับเมธอด

คลาสลูกจะเขียนทับเมธอดของคลาสแม่ด้วยการกำหนดเมธอดที่มีชื่อเดียวกัน เมื่อเรียกใช้กับอินสแตนซ์ของคลาสลูก เมธอดของคลาสลูกจะทำงาน แต่ยังเข้าถึงเมธอดของคลาสแม่ได้ผ่าน Parent.method(self)

local Shape={} Shape.__index=Shape
function Shape:area() return 0 end
function Shape:describe() print(type(self).." area="..self:area()) end

local Circle={} Circle.__index=Circle
setmetatable(Circle,{__index=Shape})
function Circle.new(r) return setmetatable({r=r},Circle) end
function Circle:area() return math.pi*self.r^2 end

local Square={} Square.__index=Square
setmetatable(Square,{__index=Shape})
function Square.new(s) return setmetatable({s=s},Square) end
function Square:area() return self.s^2 end

Circle.new(5):describe()
Square.new(4):describe()

การเลือกใช้แบบพหุรูป

พหุรูปหมายถึงวัตถุต่างชนิดตอบสนองต่อข้อความเดียวกันแตกต่างกัน ฟังก์ชันที่รับ "รูปร่าง" ใด ๆ และเรียกใช้ :area() จะทำงานกับรูปร่างทุกชนิด โดยแต่ละชนิดตอบสนองด้วยการทำงานของตนเอง

local shapes={
  Circle.new(3),
  Square.new(4),
  Circle.new(1),
}

local total=0
for _,shape in ipairs(shapes) do
  total=total+shape:area()
end
print(string.format("Total area: %.2f",total))

การพิมพ์แบบเป็ด

Lua ใช้การพิมพ์แบบเป็ด: หากวัตถุมีเมธอดนั้น ก็ใช้งานได้ ไม่จำเป็นต้องประกาศอินเทอร์เฟซอย่างเป็นทางการ ฟังก์ชันที่เรียกใช้ :speak() จะทำงานกับวัตถุใด ๆ ที่มีเมธอด speak ไม่ว่าคลาสจะเป็นอะไร

local function makeNoise(obj)
  if type(obj.speak)=="function" then
    obj:speak()
  else
    print("(silent)")
  end
end

local Duck={speak=function(self) print("Quack!") end}
local Robot={speak=function(self) print("Beep boop!") end}
local Rock={}

makeNoise(Duck)   -- Quack!
makeNoise(Robot)  -- Beep boop!
makeNoise(Rock)   -- (silent)

พหุรูปของตัวดำเนินการ

เมธอดเมตาอย่าง __add ช่วยให้ตัวดำเนินการรองรับพหุรูปได้ ชนิดข้อมูลต่างกันสามารถตอบสนองต่อ + ด้วยความหมายเฉพาะของตนเอง ตราบใดที่กำหนด __add ไว้

local Vec2={} Vec2.__index=Vec2
Vec2.__add=function(a,b) return Vec2.new(a.x+b.x,a.y+b.y) end
Vec2.__tostring=function(v) return "("..v.x..","..v.y..")" end
function Vec2.new(x,y) return setmetatable({x=x,y=y},Vec2) end

local Color={} Color.__index=Color
Color.__add=function(a,b)
  return Color.new(math.min(255,a.r+b.r),math.min(255,a.g+b.g),math.min(255,a.b+b.b))
end
function Color.new(r,g,b) return setmetatable({r=r,g=g,b=b},Color) end

print(tostring(Vec2.new(1,2)+Vec2.new(3,4)))  -- (4,6)

รูปแบบผู้เยี่ยมชม

รูปแบบผู้เยี่ยมชมแยกการดำเนินการออกจากวัตถุ การดำเนินการจะ "เยี่ยมชม" วัตถุต่างชนิด และเรียกใช้เมธอดที่เหมาะสมตามชนิด ใช้เมื่อการดำเนินการต้องเปลี่ยนไปตามชนิด โดยไม่ต้องแก้ไขชนิดเหล่านั้น

local function printInfo(obj)
  local mt = getmetatable(obj)
  if mt == Circle then
    print(string.format("Circle r=%.1f area=%.2f",obj.r,obj:area()))
  elseif mt == Square then
    print(string.format("Square s=%.1f area=%.2f",obj.s,obj:area()))
  end
end
for _,s in ipairs({Circle.new(3),Square.new(4)}) do
  printInfo(s)
end

ตารางอินเทอร์เฟซ

กำหนดอินเทอร์เฟซเป็นตารางชื่อเมธอด ตรวจสอบว่าวัตถุมีเมธอดที่จำเป็นครบทั้งหมดก่อนนำไปใช้แบบพหุรูป

local function implements(obj, interface)
  for _, method in ipairs(interface) do
    if type(obj[method]) ~= "function" then
      return false, "missing: "..method
    end
  end
  return true
end

local Drawable = {"draw","resize","move"}

local Widget = {
  draw=function(self) print("draw "..self.type) end,
  resize=function(self,w,h) self.w,self.h=w,h end,
  move=function(self,x,y) self.x,self.y=x,y end,
  type="button"
}

local ok,err = implements(Widget,Drawable)
print(ok,err)  -- true nil

การตรวจสอบว่ามีเมธอด

ก่อนเรียกใช้เมธอดเสริมแบบพหุรูป ให้ตรวจสอบว่าวัตถุนั้นมีเมธอดดังกล่าวหรือไม่ วิธีนี้ทำให้รองรับความสามารถเสริมได้โดยไม่ต้องกำหนดคลาสฐานร่วมกัน

local function maybeUpdate(obj, dt)
  if type(obj.update)=="function" then
    obj:update(dt)
  end
end

local objects={
  {x=0,y=0,update=function(self,dt) self.x=self.x+10*dt end},
  {x=0,y=0},  -- no update
  {x=0,y=0,update=function(self,dt) self.y=self.y+5*dt end},
}

for _,o in ipairs(objects) do maybeUpdate(o,1) end
for _,o in ipairs(objects) do print(o.x,o.y) end

ตารางเลือกใช้ตามชนิด

แทนที่จะใช้สายโซ่ if/elseif ให้ใช้ตารางเลือกใช้ โดยจับคู่ชื่อชนิดกับฟังก์ชันจัดการ นี่คือรูปแบบพหุรูปที่ขับเคลื่อนด้วยตาราง

local handlers={
  number  = function(v) return "num:"..v end,
  string  = function(v) return "str:\"".."..v.."\"" end,
  boolean = function(v) return "bool:"..(v and "T" or "F") end,
  table   = function(v) return "tbl:#"..tostring(#v) end,
}

local function describe(v)
  local h=handlers[type(v)]
  return h and h(v) or "unknown"
end

print(describe(42))
print(describe("hello"))
print(describe({1,2,3}))

ค่าที่ส่งกลับแบบแปรผันร่วม

เมธอดของคลาสลูกสามารถส่งกลับชนิดที่เฉพาะเจาะจงกว่าเมธอดของคลาสแม่ได้ (การส่งกลับแบบแปรผันร่วม) เนื่องจาก Lua เป็นภาษาที่ตรวจสอบชนิดแบบไดนามิก จึงเป็นเรื่องปกติ เช่น ตัวสร้างของคลาสลูกส่งกลับอินสแตนซ์ของคลาสลูก ไม่ใช่เพียงคลาสแม่

local Base={} Base.__index=Base
function Base.new() return setmetatable({type="base"},Base) end
function Base:copy() return Base.new() end

local Child={} Child.__index=Child
setmetatable(Child,{__index=Base})
function Child.new(extra)
  local self=setmetatable({type="child"},Child)
  self.extra=extra; return self
end
function Child:copy() return Child.new(self.extra) end  -- covariant

local c=Child.new("hello")
local c2=c:copy()
print(c2.extra,getmetatable(c2)==Child)  -- hello true

สรุปพหุรูป

พหุรูปใน Lua ที่ใช้จริงคือการมีชื่อเมธอดเดียวกัน แต่มีพฤติกรรมต่างกันตามชนิด ทำได้ผ่านการเขียนโปรแกรมเชิงวัตถุโดยใช้เมตาเทเบิล การตรวจสอบแบบพิมพ์เป็ด หรือ ตารางเลือกใช้ ไม่จำเป็นต้องมีระบบอินเทอร์เฟซอย่างเป็นทางการ เพียงตั้งชื่อเมธอดให้สอดคล้องกัน

-- Three ways to achieve polymorphism:
-- 1. Metatable inheritance + method override
-- 2. Duck typing: just call it if it exists
-- 3. Dispatch table: map type -> handler

local function area(shape)
  -- Duck typing approach:
  assert(type(shape.area)=="function","needs area()")
  return shape:area()
end

print(area({area=function() return 16 end}))  -- 16

คำถามเกี่ยวกับพหุรูป

พหุรูปทำงานใน Lua อย่างไร

ทบทวน: การเขียนทับและพหุรูป

เขียนทับเมธอดของคลาสแม่ด้วยการกำหนดเมธอดนั้นใหม่ในคลาสลูก Lua จะค้นหาการเรียกจากล่างขึ้นบนผ่านสายโซ่ __index จึงเกิดการเลือกใช้แบบพหุรูปขณะโปรแกรมทำงานได้โดยธรรมชาติ

คำถามที่พบบ่อย

บทเรียน “การเขียนทับและพหุรูป” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “การเขียนทับและพหุรูป” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Lua Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Lua Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “การเขียนทับและพหุรูป”

เขียนทับเมธอดของคลาสแม่และทำให้เกิดการเลือกใช้เมธอดแบบพหุรูปขณะทำงาน คุณปฏิบัติ Lua Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Lua Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน Lua Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน

บทเรียน “การเขียนทับและพหุรูป” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน Lua Academy นี้ได้ไหม

ได้ บทเรียน Lua Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. การสืบทอดเดี่ยวด้วยการเชื่อมโยง __index
  2. การเรียกเมธอดของคลาสแม่ด้วย super
  3. รูปแบบมิกซิน
  4. การเขียนทับและพหุรูป
← กลับไปที่ Lua Academy