menu_book LESSON MODE

OOP Ch.3 — Encapsulation

calendar_today Đăng 29 May 2026
OOP Ch.3 — Encapsulation
school

NOW LEARNING

OOP Ch.3 — Encapsulation

🏗️ OOP Series · Lập trình Hướng đối tượng với Python
🔒
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!

🏦

Đó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()!
🚦

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!
🔑

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

@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
@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 đủ!
🚀 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!

🧩 Kiểm tra nhanh!

Chapter 3 — Encapsulation

1️⃣Private attribute được đặt tên thế nào?
→ __ten (2 dấu gạch dưới đầu)
2️⃣@property dùng để làm gì?
→ Tạo getter/setter dùng như attribute
3️⃣Name mangling là gì?
→ Python đổi __ten thành _Class__ten
4️⃣Tại sao cần setter thay vì gán trực tiếp?
→ Để validate dữ liệu trước khi lưu
OOP Series: Chapter 3 / 10
🔒
🧬
Tiếp theo · Chapter 4
Inheritance — Kế Thừa
Tái sử dụng code bằng cách tạo class con kế thừa từ class cha. super(), override và mở rộng chức năng.

NEXT STEP

Tiếp theo nên học gì

route