Dùng chung và riêng
Thuộc phần Lập trình hướng đối tượng trong hành trình Lua của Coddy. Bài học 35 trên 70.
Khi làm việc với tính kế thừa, bạn cần hiểu mỗi phần dữ liệu nằm ở đâu. Một số thuộc tính được dùng chung: được định nghĩa trong lớp cha và có thể truy cập từ tất cả các lớp con. Những thuộc tính khác là riêng biệt: được tạo riêng trong lớp con hoặc đối tượng.
Hãy xem xét hệ thống phân cấp này:
local Animal = {}
Animal.__index = Animal
Animal.kingdom = "Animalia" -- Dùng chung: định nghĩa trên lớp (class)
function Animal:new(name)
local obj = {name = name} -- Riêng biệt: lưu trên từng instance
setmetatable(obj, Animal)
return obj
endlocal Dog = {}
Dog.__index = Dog
setmetatable(Dog, {__index = Animal})
Dog.sound = "bark" -- Dùng chung cho mọi đối tượng Dog
function Dog:new(name, breed)
local obj = Animal.new(Animal, name)
obj.breed = breed -- Riêng biệt: mỗi đối tượng Dog có breed riêng
setmetatable(obj, Dog)
return obj
endKhi tạo một đối tượng Dog, một số giá trị tồn tại trực tiếp trên đối tượng, còn những giá trị khác được tìm thấy thông qua chuỗi tra cứu:
local rex = Dog:new("Rex", "German Shepherd")
-- Chỉ thuộc instance này (được lưu trên rex)
print(rex.name) -- "Rex"
print(rex.breed) -- "German Shepherd"-- Dùng chung (tìm thấy qua tra cứu)
print(rex.sound) -- "bark" (từ Dog)
print(rex.kingdom) -- "Animalia" (từ Animal)Điểm khác biệt chính: các trường được gán cho obj trong hàm khởi tạo là riêng biệt với từng đối tượng, còn các trường được định nghĩa trực tiếp trên bảng lớp sẽ được chia sẻ giữa mọi đối tượng của lớp đó và các lớp con.
Thử thách
DễHãy khám phá cách dữ liệu đi qua hệ thống kế thừa bằng cách xây dựng hệ thống lớp Creature và Dragon. Bạn sẽ thấy rõ thuộc tính nào được chia sẻ giữa mọi instance và thuộc tính nào là riêng của từng đối tượng.
Bạn sẽ sắp xếp code trong ba tệp:
Creature.lua: Định nghĩa lớp cha với thuộc tính cấp lớp được chia sẻworld = "Fantasy Realm"mà mọi sinh vật đều dùng chung. Thêm hàm khởi tạo:new(name)để lưu tên sinh vật dưới dạng thuộc tính của instance.Dragon.lua: Định nghĩa lớp con kế thừa từ Creature. Thêm thuộc tính cấp lớp được chia sẻelement = "fire"mà mọi rồng đều dùng chung. Hàm khởi tạo:new(name, color)cần gọi hàm khởi tạo của lớp cha để đặt tên, sau đó thêmcolorlàm thuộc tính riêng của từng instance rồng.main.lua: Tạo hai con rồng khác nhau và kiểm tra các thuộc tính của chúng để minh họa sự khác biệt giữa dữ liệu dùng chung và dữ liệu riêng.
Bạn sẽ nhận hai đầu vào:
- Tên và màu của con rồng thứ nhất, được phân tách bằng dấu phẩy (ví dụ:
Smaug,red) - Tên và màu của con rồng thứ hai, được phân tách bằng dấu phẩy (ví dụ:
Draco,gold)
Trong tệp chính, hãy tạo cả hai con rồng với tên và màu tương ứng. Sau đó, in ra thông tin sau theo đúng thứ tự này:
namecủa con rồng thứ nhấtcolorcủa con rồng thứ nhấtnamecủa con rồng thứ haicolorcủa con rồng thứ haielementcủa con rồng thứ nhất (được chia sẻ từ lớp Dragon)elementcủa con rồng thứ hai (được chia sẻ từ lớp Dragon)worldcủa con rồng thứ nhất (được chia sẻ từ lớp Creature)
Ví dụ, nếu đầu vào là Smaug,red và Draco,gold, đầu ra sẽ là:
Smaug
red
Draco
gold
fire
fire
Fantasy RealmHãy chú ý rằng name và color khác nhau ở mỗi con rồng (thuộc tính riêng của từng instance), còn element và world giống nhau ở cả hai (thuộc tính cấp lớp dùng chung, được tìm thấy thông qua chuỗi tra cứu).
Tự làm thử
-- main.lua: Tạo các con rồng và minh họa thuộc tính chung và thuộc tính riêng
local Dragon = require('Dragon')
-- Đọc đầu vào của con rồng thứ nhất (tên,màu sắc)
local input1 = io.read()
-- Đọc đầu vào của con rồng thứ hai (tên,màu sắc)
local input2 = io.read()
-- TODO: Phân tích đầu vào thứ nhất để lấy tên và màu sắc
-- Gợi ý: Dùng string.match với mẫu "([^,]+),([^,]+)"
-- TODO: Phân tích đầu vào thứ hai để lấy tên và màu sắc
-- TODO: Tạo con rồng thứ nhất với tên và màu sắc của nó
-- TODO: Tạo con rồng thứ hai với tên và màu sắc của nó
-- TODO: In lần lượt các mục sau:
-- 1. Tên của con rồng thứ nhất
-- 2. Màu sắc của con rồng thứ nhất
-- 3. Tên của con rồng thứ hai
-- 4. Màu của con rồng thứ hai
-- 5. Phần tử của con rồng thứ nhất (dùng chung từ lớp Dragon)
-- 6. Phần tử của con rồng thứ hai (dùng chung từ lớp Dragon)
-- 7. Thế giới của con rồng thứ nhất (dùng chung từ lớp Creature)
Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.
Tất cả bài học trong Lập trình hướng đối tượng
1Khái niệm “self”
Bảng với hàm“self” tường minhCú pháp dấu hai chấmDấu chấm và dấu hai chấmÔn tập: Điểm chuyển động4Dự án: Ngân hàng số
Thiết lập dự ánPhương thức nạp tiền2Mẫu Prototype cho lớp
Khái niệm PrototypeLiên kết bằng __indexHàm khởi tạo :new()Khởi tạo thuộc tínhCác instance độc lậpÔn tập: Nhà máy ô tô5Nạp chồng toán tử trong OOP
Cộng các đối tượngTrừ các đối tượngNối các đối tượngSo sánh các đối tượng (<, >)Ôn tập: phép tính với ví tiền8Đóng gói
Quy ước đặt tênClosure để bảo mậtTruy cập qua closureBảng chỉ đọcLogic kiểm tra hợp lệTổng kết: Két an toàn11Mẫu thiết kế (cơ bản)
Hàm tạo đối tượngBảng SingletonMẫu IteratorObserver (Listener)Ôn tập: Factory Logger3Trạng thái và hành vi của đối tượng
Biến instancePhương thức getterPhương thức setterThuộc tính tính toánĐịnh dạng chuỗiKiểm tra tính bằng nhauÔn tập: điểm số học sinh6Cơ bản về kế thừa
Thiết lập kế thừaKế thừa phương thứcMở rộng hàm khởi tạoThêm phương thức cho lớp conDùng chung và riêngÔn tập: hệ phân cấp hình dạngTự luyện tập: Trình biên dịch Lua online