🔒
Encapsulation
Chapter 3 · OOP Series
Encapsulation
— Đóng Gói Dữ Liệu
Bảo vệ dữ liệu bên trong object — kiểm soát ai được đọc, ai được ghi, ai không được đụng vào!
Phần 1 — Encapsulation là gì?
Đóng gói — Không ai được vào tuỳ tiện!
Encapsulation (đóng gói) là nguyên lý che giấu dữ liệu bên trong object, chỉ cho phép truy cập thông qua các method được kiểm soát.
🏧
Như cây ATM — dữ liệu được bảo vệ!
Bạn không thể thò tay vào máy ATM lấy tiền trực tiếp. Bạn phải dùng thẻ + mã PIN (interface kiểm soát). Encapsulation cũng vậy — không ai được trực tiếp thay đổi số dư tài khoản, phải đi qua method
nap_tien() hoặc rut_tien()!
Phần 2 — 3 mức truy cập
Public, Protected và Private
Public
ten
- Mặc định — không có gì đặc biệt
- Ai cũng truy cập được
- Dùng cho data không nhạy cảm
Protected
_ten
- 1 dấu gạch dưới đầu
- Quy ước: "nội bộ, cẩn thận"
- Vẫn truy cập được, nhưng không nên
Private
__ten
- 2 dấu gạch dưới đầu
- Name mangling — khó truy cập trực tiếp
- Chỉ dùng trong nội bộ class
class NguoiDung: def __init__(self, ten, tuoi, mat_khau): self.ten = ten # public — ai cũng đọc được self._tuoi = tuoi # protected — nội bộ, cẩn thận self.__mat_khau = mat_khau # private — không được đụng! u = NguoiDung("Nam", 13, "abc123") print(u.ten) # ✅ OK print(u._tuoi) # ⚠️ Được nhưng không nên print(u.__mat_khau) # ❌ AttributeError!
AttributeError: 'NguoiDung' object has no attribute '__mat_khau'
🔐 Python đổi tên
__mat_khau thành _NguoiDung__mat_khau — gọi là name mangling. Điều này khiến attribute private khó truy cập từ bên ngoài!Phần 3 — Getter & Setter
Getter & Setter — Cửa chính thức vào dữ liệu
Thay vì truy cập trực tiếp, tạo các method getter (đọc) và setter (ghi) để kiểm soát và validate dữ liệu:
📖 Getter — Đọc dữ liệu
Method trả về giá trị của private attribute. Có thể thêm logic kiểm tra trước khi trả về.
✏️ Setter — Ghi dữ liệu
Method nhận giá trị mới và validate trước khi lưu. Chặn dữ liệu sai ngay tại đây!
class HocSinh: def __init__(self, ten, diem): self.ten = ten self.__diem = 0 self.set_diem(diem) ← dùng setter ngay trong __init__! def get_diem(self): ← GETTER return self.__diem def set_diem(self, diem): ← SETTER với validation if 0 <= diem <= 10: self.__diem = diem else: print(f"❌ Điểm {diem} không hợp lệ! (0–10)") hs = HocSinh("Nam", 8.5) print(hs.get_diem()) # 8.5 hs.set_diem(9.0) hs.set_diem(15) # bị chặn! print(hs.get_diem()) # vẫn 9.0
8.5
❌ Điểm 15 không hợp lệ! (0–10)
9.0
❌ Điểm 15 không hợp lệ! (0–10)
9.0
Phần 4 — @property: Pythonic hơn!
@property — Getter/Setter theo phong cách Python
Python có decorator @property giúp dùng getter/setter như attribute thông thường — gọn hơn rất nhiều!
class HinhTron: def __init__(self, ban_kinh): self.__ban_kinh = 0 self.ban_kinh = ban_kinh ← tự động gọi setter! @property def ban_kinh(self): ← GETTER — dùng như attribute return self.__ban_kinh @ban_kinh.setter def ban_kinh(self, r): ← SETTER — validate if r < 0: raise ValueError("Bán kính không thể âm!") self.__ban_kinh = r @property def dien_tich(self): ← computed property (chỉ đọc) return 3.14159 * self.__ban_kinh ** 2 h = HinhTron(5) print(h.ban_kinh) # dùng như attribute, không cần () print(h.dien_tich) # tự tính — readonly! h.ban_kinh = 7 # gán như attribute — setter tự gọi print(h.dien_tich)
5
78.53975
153.93791
78.53975
153.93791
✨ @property là cách Pythonic nhất! Bạn dùng
h.ban_kinh thay vì h.get_ban_kinh() — trông sạch hơn nhiều trong khi vẫn có validation đầy đủ!Phần 5 — Bài tập tổng hợp
🚀 Dự án nhỏ
Xây dựng Class TaiKhoan hoàn chỉnh
class TaiKhoan: def __init__(self, chu_tk, so_du_ban_dau=0): self.chu_tk = chu_tk # public self.__so_du = 0 # private self.so_du = so_du_ban_dau # setter kiểm tra @property def so_du(self): return self.__so_du @so_du.setter def so_du(self, gia_tri): if gia_tri < 0: raise ValueError("Số dư không được âm!") self.__so_du = gia_tri def nap(self, tien): self.so_du += tien print(f"✅ Nạp {tien:,}đ → Số dư: {self.so_du:,}đ") def rut(self, tien): if tien > self.so_du: print("❌ Không đủ tiền!"); return self.so_du -= tien print(f"💸 Rút {tien:,}đ → Còn: {self.so_du:,}đ") tk = TaiKhoan("Nam", 1_000_000) tk.nap(500_000) tk.rut(200_000) tk.rut(2_000_000)
✅ Nạp 500,000đ → Số dư: 1,500,000đ
💸 Rút 200,000đ → Còn: 1,300,000đ
❌ Không đủ tiền!
💸 Rút 200,000đ → Còn: 1,300,000đ
❌ Không đủ tiền!
🧩 Kiểm tra nhanh!
Chapter 3 — Encapsulation
Private attribute được đặt tên thế nào?
→ __ten (2 dấu gạch dưới đầu)
→ __ten (2 dấu gạch dưới đầu)
@property dùng để làm gì?
→ Tạo getter/setter dùng như attribute
→ Tạo getter/setter dùng như attribute
Name mangling là gì?
→ Python đổi __ten thành _Class__ten
→ Python đổi __ten thành _Class__ten
Tại sao cần setter thay vì gán trực tiếp?
→ Để validate dữ liệu trước khi lưu
→ Để validate dữ liệu trước khi lưu