Quan hệ có-một
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 49 trên 70.
Trong suốt khóa học này, bạn đã dùng tính kế thừa (inheritance) để chia sẻ hành vi giữa các lớp: Dog “là một” Animal. Nhưng kế thừa không phải lúc nào cũng là công cụ phù hợp.
Đôi khi, các đối tượng không cùng một cây gia phả; thay vào đó, một đối tượng chỉ đơn giản là chứa một đối tượng khác. Đây được gọi là kết hợp (composition), mô hình hóa mối quan hệ “có một”.
Hãy xem xét một chiếc ô tô. Ô tô không phải là một loại động cơ. Nó có một động cơ. Động cơ là một đối tượng riêng biệt nằm bên trong ô tô:
local Engine = {}
Engine.__index = Engine
function Engine:new(horsepower)
local obj = {horsepower = horsepower}
setmetatable(obj, Engine)
return obj
end
local Car = {}
Car.__index = Car
function Car:new(model, engine)
local obj = {
model = model,
engine = engine -- Lưu đối tượng (object) Engine bên trong Car
}
setmetatable(obj, Car)
return obj
endKhi tạo một chiếc xe, bạn truyền một đối tượng động cơ có sẵn vào hàm khởi tạo của nó:
local myEngine = Engine:new(200)
local myCar = Car:new("Sedan", myEngine)
print(myCar.engine.horsepower) -- 200Xe không kế thừa từ động cơ. Xe chỉ giữ một tham chiếu đến động cơ. Nhờ vậy, hai lớp vẫn độc lập. Bạn có thể thay bằng một động cơ khác hoặc dùng cùng lớp động cơ cho xe máy.
Tính hợp thành mang lại cho bạn sự linh hoạt mà kế thừa không thể có: bạn ghép các đối tượng từ nhiều phần thay vì buộc chúng vào một hệ thống phân cấp gia đình cứng nhắc.
Thử thách
DễHãy cùng xây dựng một hệ thống máy tính bằng cách kết hợp! Thay vì dùng kế thừa, bạn sẽ tạo một Computer có một CPU bên trong: thể hiện mối quan hệ “có một”.
Bạn sẽ sắp xếp code trong ba tệp:
CPU.lua: Tạo một lớp CPU với hàm khởi tạo:new(brand, speed)để lưu tên hãng và tốc độ tính bằng GHz. Thêm phương thức:getInfo()trả về một chuỗi theo định dạng:{brand} @ {speed}GHzComputer.lua: Tạo một lớp Computer có hàm khởi tạo:new(model, cpu)nhận tên kiểu máy và một đối tượng CPU. Computer lưu đối tượng CPU bên trong nó (kết hợp!). Thêm phương thức:describe()để in ra kiểu máy tính và thông tin CPU.main.lua: Kết hợp tất cả! Đọc đầu vào gồm hãng CPU, tốc độ CPU và kiểu máy tính. Trước tiên, tạo một đối tượng CPU, sau đó truyền đối tượng đó vào hàm khởi tạo của Computer. Cuối cùng, gọi phương thức:describe()của máy tính.
Bạn sẽ nhận ba đầu vào:
- Hãng CPU (một chuỗi)
- Tốc độ CPU tính bằng GHz (một số)
- Tên kiểu máy tính (một chuỗi)
Đầu ra của bạn cần hiển thị mô tả máy tính, bao gồm thông tin từ CPU bên trong nó:
Model: {model}
CPU: {brand} @ {speed}GHzVí dụ, nếu đầu vào là Intel, 3.5 và WorkStation, đầu ra sẽ là:
Model: WorkStation
CPU: Intel @ 3.5GHzLưu ý rằng Computer không kế thừa từ CPU. Nó chỉ giữ một tham chiếu đến CPU. Phương thức :describe() của Computer truy cập dữ liệu CPU thông qua đối tượng được lưu, cho thấy cách kết hợp giúp bạn xây dựng các đối tượng phức tạp từ những phần đơn giản hơn!
Tự làm thử
-- main.lua
-- Kết hợp mọi thứ bằng cách sử dụng phép hợp thành
local CPU = require('CPU')
local Computer = require('Computer')
-- Đọc các đầu vào
local cpuBrand = io.read()
local cpuSpeed = tonumber(io.read())
local computerModel = io.read()
-- TODO: Tạo đối tượng CPU với thương hiệu và tốc độ
-- TODO: Tạo đối tượng Computer với mẫu máy và đối tượng CPU
-- TODO: Gọi phương thức describe() của máy tính để in đầu ra
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ạng9Kết hợp và mixin
Quan hệ có-mộtỦy quyềnMixin đơn giảnÁp dụng nhiều mixinMixin và kế thừaÔn tập: Lắp ráp robotTự luyện tập: Trình biên dịch Lua online