أساليب النسخ وself
عرّف الأساليب باستخدام صياغة النقطتين ومعامل self الضمني
أساليب النسخ وself درس مجاني في Lua Academy على CoddyKit. هذا هو الدرس 3 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Lua Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Lua Academy 4 دروس في المجموع.
self هي النسخة
عند استدعاء obj:method()، تمرر Lua القيمة obj تلقائيًا بوصفها المعامل الأول. ووفقًا للعرف، يُسمى هذا المعامل self. ومن خلال self، يصل الأسلوب إلى بيانات النسخة ويستدعي أساليب أخرى.
local Counter = {}
Counter.__index = Counter
function Counter.new(start)
return setmetatable({value=start or 0, step=1}, Counter)
end
function Counter:increment()
self.value = self.value + self.step
end
function Counter:getValue() return self.value end
local c = Counter.new(10)
c:increment(); c:increment()
print(c:getValue()) -- 12تسلسل الأساليب
أعد self من الأساليب لإتاحة التسلسل: obj:method1():method2():method3(). يعيد كل استدعاء الكائن نفسه، مما يتيح أسلوبًا انسيابيًا. ويعمل ذلك بأفضل صورة مع الأساليب التي تعدّل الحالة.
local Builder = {}
Builder.__index = Builder
function Builder.new()
return setmetatable({parts={}},Builder)
end
function Builder:add(s) self.parts[#self.parts+1]=s; return self end
function Builder:addAll(t)
for _,v in ipairs(t) do self.parts[#self.parts+1]=v end
return self
end
function Builder:build() return table.concat(self.parts," ") end
local result = Builder.new()
:add("Hello")
:add("from")
:addAll({"Lua","OOP"})
:build()
print(result) -- Hello from Lua OOPاستدعاء أساليب أخرى
يمكن للأساليب استدعاء أساليب أخرى على النسخة نفسها عبر self:otherMethod(). ويعزز ذلك إعادة استخدام الشفرة داخل الصنف. ويمكن أن تكون الدوال المساعدة الخاصة دوالًا محلية عادية تتلقى self صراحةً.
local Stack = {}
Stack.__index = Stack
function Stack.new() return setmetatable({_data={},_size=0},Stack) end
function Stack:push(v) self._size=self._size+1; self._data[self._size]=v end
function Stack:pop()
if self:isEmpty() then return nil end
local v=self._data[self._size]; self._data[self._size]=nil
self._size=self._size-1; return v
end
function Stack:peek()
return not self:isEmpty() and self._data[self._size] or nil
end
function Stack:isEmpty() return self._size==0 end
function Stack:size() return self._size end
local s=Stack.new(); s:push(1);s:push(2);s:push(3)
print(s:pop(),s:peek(),s:size()) -- 3 2 2تحذير بشأن تجاوز الأساليب
يؤدي تعيين حقل في النسخة بالاسم نفسه لأسلوب في الصنف إلى حجب الأسلوب لهذه النسخة. ويكون ذلك خطأً عادةً — لذا انتبه عندما يمكن أن تتعارض أسماء حقول النسخة مع أسماء الأساليب.
local Dog = {}
Dog.__index = Dog
function Dog.new(n) return setmetatable({name=n},Dog) end
function Dog:speak() print(self.name..": Woof!") end
local d = Dog.new("Rex")
d:speak() -- Rex: Woof!
-- Accidental shadow:
d.speak = "loud" -- now d.speak is a string, not a function
local ok,err = pcall(function() d:speak() end)
print(ok, err) -- false attempt to call a string valueself في دوال الاستدعاء اللاحق
يؤدي تمرير أسلوب بوصفه دالة استدعاء لاحق إلى فقدان self الضمني. غلّف الأسلوب داخل إغلاق لالتقاط self.
local Timer = {}
Timer.__index = Timer
function Timer.new(n) return setmetatable({count=n},Timer) end
function Timer:tick()
self.count=self.count-1
print("Ticking, count:", self.count)
end
local t = Timer.new(3)
-- Wrong: loses self
-- local cb = t.tick -- cb(t) works but cb() doesn't
-- Correct: closure captures self
local cb = function() t:tick() end
cb(); cb(); cb() -- Ticking 3 timesالأساليب الساكنة مقابل أساليب النسخ
يُستدعى الأسلوب الساكن على الصنف، لا على نسخة. ولا يحتوي على self (أو يتلقى الصنف بوصفه self). لا يوجد في Lua تمييز رسمي بينهما — ووفقًا للعرف، استخدم . (وليس :) للأساليب الساكنة.
local MathUtils = {}
MathUtils.__index = MathUtils
-- Static method (no instance needed)
function MathUtils.clamp(v, lo, hi)
return math.max(lo, math.min(hi, v))
end
-- Instance method (needs self)
function MathUtils:scale(factor)
self.value = self.value * factor
end
print(MathUtils.clamp(15, 0, 10)) -- 10
local m = setmetatable({value=5},MathUtils)
m:scale(3)
print(m.value) -- 15أساليب Mixin
أضف أساليب من جدول Mixin إلى صنف دون توارث رسمي. وهذا تركيب مسطح: انسخ دوال Mixin أو ارجع إليها داخل جدول الصنف.
local Serializable = {}
function Serializable:serialize()
local parts={}
for k,v in pairs(self) do
if type(v)~="function" then
parts[#parts+1]=k.."="..tostring(v)
end
end
return "{"..table.concat(parts,",").."}"
end
local User = {}
User.__index = User
User.serialize = Serializable.serialize -- mixin!
function User.new(n,a) return setmetatable({name=n,age=a},User) end
local u = User.new("Alice",30)
print(u:serialize())الأساليب المجرّدة
لا تفرض Lua الأساليب المجرّدة، ولكن يمكنك تعريف أسلوب في صنف أساسي يرفع خطأً، مما يجبر الأصناف الفرعية على تجاوزه. وهذا عرف يدل على أن الأسلوب "يجب تجاوزه".
local Shape = {}
Shape.__index = Shape
function Shape.new() return setmetatable({},Shape) end
function Shape:area()
error(type(self).." must implement area()",2)
end
function Shape:describe()
print("Shape area:", self:area())
end
local sq={}
setmetatable(sq,{__index=Shape})
function sq:area() return 4*4 end
sq:describe() -- Shape area: 16جداول الأساليب
بالنسبة إلى الأصناف التي تحتوي على أساليب كثيرة، نظّم الأساليب في مجموعات حسب الوظيفة. تشترك جميع الأساليب في الجدول نفسه في البحث عبر جدول البيانات الوصفية نفسه، ولكن يمكنك تقسيمها حسب الاهتمام باستخدام مساحات أسماء فرعية.
local Entity = {}
Entity.__index = Entity
Entity.Physics = {}
function Entity.Physics:applyForce(fx,fy)
self.vx=(self.vx or 0)+fx
self.vy=(self.vy or 0)+fy
end
Entity.Render = {}
function Entity.Render:draw()
print(string.format("Draw at (%g,%g)",self.x or 0,self.y or 0))
end
-- Mix into main class
for k,v in pairs(Entity.Physics) do Entity[k]=v end
for k,v in pairs(Entity.Render) do Entity[k]=v end
function Entity.new(x,y) return setmetatable({x=x,y=y},Entity) end
local e=Entity.new(10,20); e:draw(); e:applyForce(5,0); print(e.vx)التفويض مقابل التوارث
فضّل التفويض (الاحتفاظ بمرجع) على سلاسل التوارث العميقة. يمكن للصنف أن يفوّض إلى كائن آخر بدلًا من التوارث منه، مما يحافظ على انخفاض الترابط ويبقي كل صنف مركزًا على مهمته.
local Logger = {}
Logger.__index = Logger
function Logger.new(name) return setmetatable({name=name},Logger) end
function Logger:log(msg) print("["..self.name.."] "..msg) end
local Service = {}
Service.__index = Service
function Service.new(name)
return setmetatable({
name=name,
logger=Logger.new(name) -- delegation
},Service)
end
function Service:start()
self.logger:log("starting...") -- delegate logging
print(self.name, "running")
end
Service.new("AuthService"):start()تحقق سريع
ما الفرق بين obj:method() وobj.method()؟
مراجعة: أساليب النسخ
الملخص:
- صيغة الاستدعاء باستخدام النقطتين:
obj:method()==obj.method(obj) - أعد self لإتاحة تسلسل الأساليب
- استدعِ أساليب أخرى:
self:otherMethod() - دوال الاستدعاء اللاحق: غلّفها في إغلاق للحفاظ على self
- الأساليب الساكنة: استخدم تدوين النقطة وفقًا للعرف
- فضّل التفويض على التوارث العميق لتحقيق المرونة
الأسئلة الشائعة
هل درس «أساليب النسخ وself» مجاني؟
نعم — نص درس «أساليب النسخ وself» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Lua Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Lua Academy 4 دروس في المجموع.
ماذا ستتعلم في «أساليب النسخ وself»؟
عرّف الأساليب باستخدام صياغة النقطتين ومعامل self الضمني تتمرن على Lua Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ Lua Academy؟
لا تُشترط خبرة سابقة. Lua Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 3 من أصل 4.
كم من الوقت يستغرق درس «أساليب النسخ وself»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس Lua Academy هذا؟
نعم. كل درس في Lua Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.