Đóng gói (Encapsulation)
📖 Giới thiệu
Đóng gói là nguyên tắc che giấu chi tiết bên trong của lớp và chỉ cung cấp giao diện công khai cần thiết. Điều này giúp bảo vệ dữ liệu và kiểm soát cách truy cập vào chúng.
Ví dụ đơn giản đầu tiên
cpp
#include <iostream>
#include <string>
using namespace std;
class TaiKhoan {
private:
double soDu; // Che giấu số dư
public:
string tenTK;
// Phương thức công khai để truy cập số dư
void gui_tien(double tien) {
if (tien > 0) {
soDu += tien;
cout << "Đã gửi " << tien << " VND" << endl;
}
}
double xem_so_du() {
return soDu;
}
};
int main() {
TaiKhoan tk;
tk.tenTK = "Minh";
tk.gui_tien(1000000); // OK - sử dụng phương thức
cout << "Số dư: " << tk.xem_so_du() << " VND" << endl;
// tk.soDu = 999999999; // ERROR - không thể truy cập trực tiếp
return 0;
}🔧 Cú pháp
Từ khóa truy cập
cpp
class TenLop {
private:
// Chỉ lớp này truy cập được
int thuocTinhRiengTu;
protected:
// Lớp này và lớp con truy cập được
int thuocTinhBaoVe;
public:
// Mọi người đều truy cập được
int thuocTinhCongKhai;
// Getter (lấy dữ liệu)
int lay_gia_tri() const {
return thuocTinhRiengTu;
}
// Setter (đặt dữ liệu với kiểm tra)
void dat_gia_tri(int gia_tri) {
if (gia_tri >= 0) {
thuocTinhRiengTu = gia_tri;
}
}
};Getter và Setter
cpp
class NguoiDung {
private:
int tuoi;
string email;
public:
// Getter
int lay_tuoi() const { return tuoi; }
string lay_email() const { return email; }
// Setter với validation
void dat_tuoi(int tuoi_moi) {
if (tuoi_moi >= 0 && tuoi_moi <= 120) {
tuoi = tuoi_moi;
}
}
void dat_email(string email_moi) {
if (email_moi.find("@") != string::npos) {
email = email_moi;
}
}
};🔬 Phân tích & Giải thích
Lợi ích của Đóng gói
1. Bảo vệ dữ liệu khỏi truy cập trái phép
cpp
class SinhVien {
private:
float diem; // Không thể thay đổi trực tiếp từ bên ngoài
public:
void dat_diem(float diem_moi) {
// Kiểm tra dữ liệu hợp lệ
if (diem_moi >= 0 && diem_moi <= 10) {
diem = diem_moi;
} else {
cout << "Điểm không hợp lệ!" << endl;
}
}
float lay_diem() const {
return diem;
}
};2. Kiểm soát cách dữ liệu được thay đổi
cpp
class QuanLyTien {
private:
double tong_tien;
public:
void them_tien(double tien) {
if (tien > 0) {
tong_tien += tien;
ghi_log("Thêm " + to_string(tien) + " VND");
}
}
private:
void ghi_log(string thong_tin) {
cout << "[LOG] " << thong_tin << endl;
}
};3. Dễ bảo trì và mở rộng
cpp
class NguoiDung {
private:
string mat_khau_ma_hoa; // Nội bộ có thể thay đổi cách lưu trữ
public:
void dat_mat_khau(string mk) {
mat_khau_ma_hoa = ma_hoa(mk); // Giao diện không đổi
}
bool kiem_tra_mat_khau(string mk) {
return mat_khau_ma_hoa == ma_hoa(mk);
}
private:
string ma_hoa(string input) {
// Có thể thay đổi thuật toán mà không ảnh hưởng bên ngoài
return input + "_encrypted";
}
};💻 Ví dụ minh họa
Ví dụ 1: Hệ thống Ngân hàng an toàn
cpp
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class TaiKhoanNganHang {
private:
string soTaiKhoan;
string pin;
double soDu;
vector<string> lichSuGiaoDich;
bool trangThai; // true = hoạt động, false = bị khóa
// Phương thức riêng tư
bool kiem_tra_pin(string pin_nhap) {
return pin == pin_nhap;
}
void ghi_lich_su(string giao_dich) {
lichSuGiaoDich.push_back(giao_dich);
if (lichSuGiaoDich.size() > 100) {
lichSuGiaoDich.erase(lichSuGiaoDich.begin());
}
}
void khoa_tai_khoan() {
trangThai = false;
cout << "Tài khoản đã bị khóa do nhập sai PIN nhiều lần!" << endl;
}
public:
// Constructor
TaiKhoanNganHang(string so_tk, string pin_ban_dau, double so_du_ban_dau) {
soTaiKhoan = so_tk;
pin = pin_ban_dau;
soDu = (so_du_ban_dau >= 50000) ? so_du_ban_dau : 50000;
trangThai = true;
ghi_lich_su("Tạo tài khoản với số dư: " + to_string(soDu));
cout << "Tài khoản " << soTaiKhoan << " đã được tạo!" << endl;
}
// Getter methods
string lay_so_tai_khoan() const {
return soTaiKhoan;
}
bool kiem_tra_trang_thai() const {
return trangThai;
}
// Phương thức gửi tiền
bool gui_tien(double so_tien, string pin_nhap) {
if (!trangThai) {
cout << "Tài khoản đã bị khóa!" << endl;
return false;
}
if (!kiem_tra_pin(pin_nhap)) {
cout << "PIN không đúng!" << endl;
return false;
}
if (so_tien <= 0) {
cout << "Số tiền phải lớn hơn 0!" << endl;
return false;
}
soDu += so_tien;
ghi_lich_su("Gửi tiền: +" + to_string(so_tien));
cout << "Đã gửi " << so_tien << " VND thành công!" << endl;
return true;
}
// Phương thức rút tiền
bool rut_tien(double so_tien, string pin_nhap) {
if (!trangThai) {
cout << "Tài khoản đã bị khóa!" << endl;
return false;
}
if (!kiem_tra_pin(pin_nhap)) {
cout << "PIN không đúng!" << endl;
return false;
}
if (so_tien <= 0) {
cout << "Số tiền phải lớn hơn 0!" << endl;
return false;
}
if (soDu - so_tien < 50000) {
cout << "Không đủ số dư! (Phải giữ lại tối thiểu 50,000 VND)" << endl;
return false;
}
soDu -= so_tien;
ghi_lich_su("Rút tiền: -" + to_string(so_tien));
cout << "Đã rút " << so_tien << " VND thành công!" << endl;
return true;
}
// Kiểm tra số dư
void xem_so_du(string pin_nhap) {
if (!trangThai) {
cout << "Tài khoản đã bị khóa!" << endl;
return;
}
if (kiem_tra_pin(pin_nhap)) {
cout << "Số dư hiện tại: " << soDu << " VND" << endl;
} else {
cout << "PIN không đúng!" << endl;
}
}
// Xem lịch sử giao dịch
void xem_lich_su(string pin_nhap) {
if (!trangThai) {
cout << "Tài khoản đã bị khóa!" << endl;
return;
}
if (kiem_tra_pin(pin_nhap)) {
cout << "\n=== LỊCH SỬ GIAO DỊCH ===" << endl;
for (int i = max(0, (int)lichSuGiaoDich.size() - 5); i < lichSuGiaoDich.size(); i++) {
cout << (i + 1) << ". " << lichSuGiaoDich[i] << endl;
}
cout << "=========================" << endl;
} else {
cout << "PIN không đúng!" << endl;
}
}
// Đổi PIN
bool doi_pin(string pin_cu, string pin_moi) {
if (!trangThai) {
cout << "Tài khoản đã bị khóa!" << endl;
return false;
}
if (!kiem_tra_pin(pin_cu)) {
cout << "PIN cũ không đúng!" << endl;
return false;
}
if (pin_moi.length() < 4) {
cout << "PIN mới phải có ít nhất 4 ký tự!" << endl;
return false;
}
pin = pin_moi;
ghi_lich_su("Đổi PIN thành công");
cout << "Đã đổi PIN thành công!" << endl;
return true;
}
};
int main() {
cout << "=== HỆ THỐNG NGÂN HÀNG ===" << endl;
// Tạo tài khoản
TaiKhoanNganHang tk("123456789", "1234", 1000000);
cout << "\n=== THAO TÁC GIAO DỊCH ===" << endl;
// Các giao dịch hợp lệ
tk.xem_so_du("1234");
tk.gui_tien(500000, "1234");
tk.xem_so_du("1234");
tk.rut_tien(200000, "1234");
tk.xem_so_du("1234");
cout << "\n=== KIỂM TRA BẢO MẬT ===" << endl;
// Thử các thao tác không hợp lệ
tk.xem_so_du("0000"); // PIN sai
tk.rut_tien(100000, "9999"); // PIN sai
tk.gui_tien(-50000, "1234"); // Số tiền âm
tk.rut_tien(2000000, "1234"); // Vượt quá số dư
cout << "\n=== QUẢN LÝ TÀI KHOẢN ===" << endl;
tk.doi_pin("1234", "5678");
tk.xem_lich_su("5678");
return 0;
}Ví dụ 2: Quản lý thông tin cá nhân
cpp
#include <iostream>
#include <string>
#include <regex>
using namespace std;
class ThongTinCaNhan {
private:
string ho_ten;
int tuoi;
string email;
string so_dien_thoai;
double luong;
// Phương thức validation riêng tư
bool kiem_tra_email(const string& email_input) {
regex email_pattern(R"((\w+)(\.\w+)*@(\w+)(\.\w+)+)");
return regex_match(email_input, email_pattern);
}
bool kiem_tra_sdt(const string& sdt) {
if (sdt.length() != 10) return false;
for (char c : sdt) {
if (!isdigit(c)) return false;
}
return sdt[0] == '0';
}
string chu_hoa_chu_cai_dau(string ten) {
if (ten.empty()) return ten;
ten[0] = toupper(ten[0]);
for (int i = 1; i < ten.length(); i++) {
if (ten[i-1] == ' ') {
ten[i] = toupper(ten[i]);
} else {
ten[i] = tolower(ten[i]);
}
}
return ten;
}
public:
// Constructor
ThongTinCaNhan() {
ho_ten = "";
tuoi = 0;
email = "";
so_dien_thoai = "";
luong = 0.0;
}
// Getter methods
string lay_ho_ten() const { return ho_ten; }
int lay_tuoi() const { return tuoi; }
string lay_email() const { return email; }
string lay_so_dien_thoai() const { return so_dien_thoai; }
// Setter methods với validation
bool dat_ho_ten(const string& ten) {
if (ten.empty() || ten.length() > 50) {
cout << "Tên không hợp lệ (độ dài 1-50 ký tự)!" << endl;
return false;
}
ho_ten = chu_hoa_chu_cai_dau(ten);
cout << "Đã cập nhật họ tên: " << ho_ten << endl;
return true;
}
bool dat_tuoi(int tuoi_moi) {
if (tuoi_moi < 0 || tuoi_moi > 120) {
cout << "Tuổi không hợp lệ (0-120)!" << endl;
return false;
}
tuoi = tuoi_moi;
cout << "Đã cập nhật tuổi: " << tuoi << endl;
return true;
}
bool dat_email(const string& email_moi) {
if (!kiem_tra_email(email_moi)) {
cout << "Email không hợp lệ!" << endl;
return false;
}
email = email_moi;
cout << "Đã cập nhật email: " << email << endl;
return true;
}
bool dat_so_dien_thoai(const string& sdt_moi) {
if (!kiem_tra_sdt(sdt_moi)) {
cout << "Số điện thoại không hợp lệ (10 số, bắt đầu bằng 0)!" << endl;
return false;
}
so_dien_thoai = sdt_moi;
cout << "Đã cập nhật SĐT: " << so_dien_thoai << endl;
return true;
}
bool dat_luong(double luong_moi) {
if (luong_moi < 0) {
cout << "Lương không thể âm!" << endl;
return false;
}
luong = luong_moi;
cout << "Đã cập nhật lương: " << luong << " VND" << endl;
return true;
}
// Phương thức hiển thị thông tin
void hien_thi_thong_tin() {
cout << "\n=== THÔNG TIN CÁ NHÂN ===" << endl;
cout << "Họ tên: " << ho_ten << endl;
cout << "Tuổi: " << tuoi << endl;
cout << "Email: " << email << endl;
cout << "SĐT: " << so_dien_thoai << endl;
cout << "Lương: " << luong << " VND" << endl;
cout << "=========================" << endl;
}
// Kiểm tra thông tin đầy đủ
bool thong_tin_day_du() {
return !ho_ten.empty() && tuoi > 0 && !email.empty() &&
!so_dien_thoai.empty() && luong >= 0;
}
// Tính thuế (phương thức sử dụng dữ liệu private)
double tinh_thue() {
if (luong <= 5000000) return 0;
else if (luong <= 10000000) return luong * 0.1;
else if (luong <= 18000000) return luong * 0.15;
else return luong * 0.2;
}
};
int main() {
cout << "=== QUẢN LÝ THÔNG TIN CÁ NHÂN ===" << endl;
ThongTinCaNhan nguoi;
// Nhập thông tin với validation
nguoi.dat_ho_ten("nguyễn văn minh");
nguoi.dat_tuoi(25);
nguoi.dat_email("minh@gmail.com");
nguoi.dat_so_dien_thoai("0987654321");
nguoi.dat_luong(15000000);
nguoi.hien_thi_thong_tin();
cout << "\n=== KIỂM TRA VALIDATION ===" << endl;
// Thử các dữ liệu không hợp lệ
nguoi.dat_tuoi(-5); // Tuổi âm
nguoi.dat_email("email_sai"); // Email sai format
nguoi.dat_so_dien_thoai("123"); // SĐT không đủ số
nguoi.dat_luong(-1000000); // Lương âm
cout << "\n=== TÍNH THUẾ ===" << endl;
cout << "Thuế phải nộp: " << nguoi.tinh_thue() << " VND" << endl;
cout << "\nThông tin đầy đủ: " << (nguoi.thong_tin_day_du() ? "Có" : "Không") << endl;
return 0;
}Ví dụ 3: Game RPG với bảo vệ stats
cpp
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
class NhanVat {
private:
string ten;
int level;
int hp_hien_tai;
int hp_toi_da;
int mp_hien_tai;
int mp_toi_da;
int kinh_nghiem;
int kinh_nghiem_can_thiet;
// Stats riêng tư
int suc_manh;
int phong_thu;
int toc_do;
// Phương thức riêng tư
void tinh_toan_stats() {
hp_toi_da = 100 + level * 20;
mp_toi_da = 50 + level * 10;
kinh_nghiem_can_thiet = level * 100;
}
void level_up() {
level++;
suc_manh += 5;
phong_thu += 3;
toc_do += 2;
tinh_toan_stats();
hp_hien_tai = hp_toi_da; // Full heal khi lên cấp
mp_hien_tai = mp_toi_da;
kinh_nghiem = 0;
cout << "🎉 " << ten << " đã lên cấp " << level << "!" << endl;
}
public:
// Constructor
NhanVat(string ten_nv) {
ten = ten_nv;
level = 1;
suc_manh = 10;
phong_thu = 5;
toc_do = 8;
kinh_nghiem = 0;
tinh_toan_stats();
hp_hien_tai = hp_toi_da;
mp_hien_tai = mp_toi_da;
cout << "Tạo nhân vật: " << ten << endl;
}
// Getter methods
string lay_ten() const { return ten; }
int lay_level() const { return level; }
int lay_hp() const { return hp_hien_tai; }
int lay_mp() const { return mp_hien_tai; }
int lay_suc_manh() const { return suc_manh; }
int lay_phong_thu() const { return phong_thu; }
// Phương thức tấn công
int tan_cong() {
if (mp_hien_tai >= 10) {
mp_hien_tai -= 10;
int damage = suc_manh + (rand() % 10);
cout << ten << " tấn công - Damage: " << damage << endl;
return damage;
} else {
cout << ten << " không đủ MP để tấn công!" << endl;
return 0;
}
}
// Nhận sát thương
void nhan_sat_thuong(int damage) {
int sat_thuong_thuc = max(1, damage - phong_thu);
hp_hien_tai = max(0, hp_hien_tai - sat_thuong_thuc);
cout << ten << " nhận " << sat_thuong_thuc << " sát thương. "
<< "HP còn lại: " << hp_hien_tai << "/" << hp_toi_da << endl;
}
// Hồi phục
void hoi_phuc(int hp_hoi = 0, int mp_hoi = 0) {
if (hp_hoi == 0) hp_hoi = hp_toi_da / 4; // Hồi 25% nếu không chỉ định
if (mp_hoi == 0) mp_hoi = mp_toi_da / 2; // Hồi 50% MP
hp_hien_tai = min(hp_toi_da, hp_hien_tai + hp_hoi);
mp_hien_tai = min(mp_toi_da, mp_hien_tai + mp_hoi);
cout << ten << " hồi phục " << hp_hoi << " HP và " << mp_hoi << " MP" << endl;
}
// Nhận kinh nghiệm
void nhan_kinh_nghiem(int exp) {
if (exp <= 0) return;
kinh_nghiem += exp;
cout << ten << " nhận được " << exp << " kinh nghiệm" << endl;
// Kiểm tra lên cấp
while (kinh_nghiem >= kinh_nghiem_can_thiet) {
level_up();
}
}
// Kiểm tra còn sống
bool con_song() const {
return hp_hien_tai > 0;
}
// Hiển thị thông tin chi tiết
void hien_thong_tin() {
cout << "\n=== " << ten << " ===" << endl;
cout << "Level: " << level << endl;
cout << "HP: " << hp_hien_tai << "/" << hp_toi_da << endl;
cout << "MP: " << mp_hien_tai << "/" << mp_toi_da << endl;
cout << "EXP: " << kinh_nghiem << "/" << kinh_nghiem_can_thiet << endl;
cout << "Sức mạnh: " << suc_manh << endl;
cout << "Phòng thủ: " << phong_thu << endl;
cout << "Tốc độ: " << toc_do << endl;
cout << "===============" << endl;
}
// Sử dụng skill đặc biệt
bool su_dung_skill_dac_biet() {
if (mp_hien_tai >= 30) {
mp_hien_tai -= 30;
cout << "💥 " << ten << " sử dụng skill đặc biệt!" << endl;
return true;
} else {
cout << ten << " không đủ MP cho skill đặc biệt (cần 30 MP)!" << endl;
return false;
}
}
};
int main() {
cout << "=== GAME RPG ===" << endl;
// Tạo nhân vật
NhanVat hero("Chiến Binh");
NhanVat monster("Orc Warrior");
hero.hien_thong_tin();
monster.hien_thong_tin();
cout << "\n=== TRẬN CHIẾN ===" << endl;
int luot = 1;
while (hero.con_song() && monster.con_song()) {
cout << "\n--- Lượt " << luot << " ---" << endl;
// Hero tấn công
int damage = hero.tan_cong();
if (damage > 0) {
monster.nhan_sat_thuong(damage);
}
if (!monster.con_song()) {
cout << "🏆 " << hero.lay_ten() << " chiến thắng!" << endl;
hero.nhan_kinh_nghiem(50);
break;
}
// Monster phản công
int monster_damage = monster.tan_cong();
if (monster_damage > 0) {
hero.nhan_sat_thuong(monster_damage);
}
if (!hero.con_song()) {
cout << "💀 " << hero.lay_ten() << " đã thua!" << endl;
break;
}
// Cứ 3 lượt thì hồi phục một chút
if (luot % 3 == 0) {
hero.hoi_phuc();
}
luot++;
if (luot > 10) { // Giới hạn số lượt để tránh vòng lặp vô tận
cout << "Trận đấu kéo dài quá! Hòa!" << endl;
break;
}
}
cout << "\n=== KẾT QUẢ CUỐI ===" << endl;
hero.hien_thong_tin();
return 0;
}🏋️ Thực hành
Bài tập 1: Lớp Password
Tạo lớp Password với:
- Thuộc tính private: mật khẩu mã hóa
- Phương thức public: đặt mật khẩu, kiểm tra mật khẩu
- Validation: độ dài tối thiểu, có ký tự đặc biệt
Bài tập 2: Quản lý điểm số
Tạo lớp QuanLyDiem với:
- Thuộc tính private: mảng điểm, số lượng môn học
- Getter/Setter cho từng điểm với validation (0-10)
- Phương thức tính điểm trung bình
Bài tập 3: Hệ thống file
Tạo lớp QuanLyFile với:
- Thuộc tính private: đường dẫn file, quyền truy cập
- Phương thức public: đọc, ghi, kiểm tra quyền
- Bảo vệ file khỏi truy cập trái phép
📋 Tóm tắt
Đóng gói (Encapsulation): Che giấu chi tiết nội bộ của lớp
Từ khóa truy cập:
private: Chỉ lớp này truy cậpprotected: Lớp này và lớp conpublic: Mọi nơi đều truy cập
Getter/Setter: Kiểm soát cách truy cập dữ liệu
Lợi ích:
- Bảo vệ dữ liệu
- Validation dữ liệu
- Dễ bảo trì và mở rộng
- Tính bảo mật cao
Bài tiếp theo: Kế thừa - Tìm hiểu cách tạo lớp con từ lớp cha.