Trừu tượng hóa (Abstraction)
📖 Giới thiệu
Trừu tượng hóa là việc ẩn chi tiết phức tạp và chỉ hiển thị những tính năng cần thiết. Trong C++, trừu tượng hóa được thực hiện thông qua abstract classes và interfaces (pure virtual functions).
Ví dụ đơn giản đầu tiên
cpp
#include <iostream>
#include <string>
using namespace std;
// Abstract class - Lớp trừu tượng
class PhuongTien {
protected:
string ten;
int toc_do_hien_tai;
public:
PhuongTien(string t) : ten(t), toc_do_hien_tai(0) {}
// Pure virtual functions - bắt buộc phải override
virtual void khoi_dong() = 0;
virtual void dung_lai() = 0;
virtual void tang_toc() = 0;
// Virtual function với implementation
virtual void hien_trang_thai() {
cout << ten << " đang chạy với tốc độ " << toc_do_hien_tai << " km/h" << endl;
}
virtual ~PhuongTien() {}
};
class XeHoi : public PhuongTien {
public:
XeHoi(string ten_xe) : PhuongTien(ten_xe) {}
void khoi_dong() override {
cout << ten << " khởi động động cơ!" << endl;
toc_do_hien_tai = 0;
}
void dung_lai() override {
cout << ten << " đạp phanh và dừng lại!" << endl;
toc_do_hien_tai = 0;
}
void tang_toc() override {
toc_do_hien_tai += 10;
cout << ten << " tăng tốc độ lên " << toc_do_hien_tai << " km/h" << endl;
}
};
int main() {
// PhuongTien pt; // ERROR - không thể tạo object từ abstract class
XeHoi xe("Toyota");
xe.khoi_dong();
xe.tang_toc();
xe.hien_trang_thai();
xe.dung_lai();
return 0;
}🔧 Cú pháp
Abstract Class
cpp
class AbstractClass {
public:
// Pure virtual function
virtual void phuong_thuc_bat_buoc() = 0;
// Virtual function với implementation
virtual void phuong_thuc_co_san() {
cout << "Implementation mặc định" << endl;
}
// Non-virtual function
void phuong_thuc_thuong() {
cout << "Không thể override" << endl;
}
virtual ~AbstractClass() {}
};Interface (Multiple Pure Virtual Functions)
cpp
class Interface {
public:
virtual void method1() = 0;
virtual void method2() = 0;
virtual void method3() = 0;
virtual ~Interface() {}
};Multiple Inheritance với Interfaces
cpp
class ConcreteClass : public Interface1, public Interface2 {
public:
// Phải implement tất cả pure virtual functions
void method1() override { }
void method2() override { }
void method3() override { }
};🔬 Phân tích & Giải thích
Abstract Class vs Interface
Abstract Class:
- Có thể có cả pure virtual và virtual functions
- Có thể có member variables
- Có thể có constructor và destructor
- Thể hiện quan hệ "is-a"
Interface (Pure Abstract Class):
- Chỉ có pure virtual functions
- Không có member variables (trừ static const)
- Thể hiện quan hệ "can-do"
Khi nào sử dụng Abstraction
1. Định nghĩa contract: Các lớp con phải tuân theo
cpp
class DatabaseConnection {
public:
virtual bool connect() = 0;
virtual void disconnect() = 0;
virtual bool execute_query(string sql) = 0;
virtual ~DatabaseConnection() {}
};2. Plugin Architecture: Cho phép mở rộng tính năng
cpp
class Plugin {
public:
virtual string get_name() = 0;
virtual void execute() = 0;
virtual ~Plugin() {}
};3. Framework Design: Cung cấp structure chung
cpp
class GameState {
public:
virtual void enter() = 0;
virtual void update() = 0;
virtual void exit() = 0;
virtual ~GameState() {}
};💻 Ví dụ minh họa
Ví dụ 1: Hệ thống File I/O
cpp
#include <iostream>
#include <string>
#include <vector>
#include <fstream>
using namespace std;
// Abstract base class cho File operations
class FileHandler {
protected:
string file_path;
bool is_open;
public:
FileHandler(string path) : file_path(path), is_open(false) {}
// Pure virtual functions - bắt buộc implement
virtual bool open() = 0;
virtual void close() = 0;
virtual bool is_file_open() = 0;
// Virtual functions với default implementation
virtual string get_file_path() const {
return file_path;
}
virtual void print_status() {
cout << "File: " << file_path << " - "
<< (is_open ? "Mở" : "Đóng") << endl;
}
virtual ~FileHandler() {
if (is_open) {
close();
}
}
};
// Interface cho Read operations
class ReadableFile {
public:
virtual string read_line() = 0;
virtual string read_all() = 0;
virtual vector<string> read_lines() = 0;
virtual ~ReadableFile() {}
};
// Interface cho Write operations
class WritableFile {
public:
virtual bool write_line(const string& content) = 0;
virtual bool write_all(const string& content) = 0;
virtual bool append_line(const string& content) = 0;
virtual ~WritableFile() {}
};
// Concrete class cho Text File Reading
class TextFileReader : public FileHandler, public ReadableFile {
private:
ifstream file_stream;
public:
TextFileReader(string path) : FileHandler(path) {}
bool open() override {
file_stream.open(file_path);
is_open = file_stream.is_open();
if (is_open) {
cout << "✅ Mở file đọc: " << file_path << endl;
} else {
cout << "❌ Không thể mở file: " << file_path << endl;
}
return is_open;
}
void close() override {
if (is_open) {
file_stream.close();
is_open = false;
cout << "🔒 Đóng file: " << file_path << endl;
}
}
bool is_file_open() override {
return is_open && file_stream.is_open();
}
string read_line() override {
if (!is_file_open()) {
cout << "❌ File chưa được mở!" << endl;
return "";
}
string line;
if (getline(file_stream, line)) {
return line;
}
return "";
}
string read_all() override {
if (!is_file_open()) {
cout << "❌ File chưa được mở!" << endl;
return "";
}
string content, line;
while (getline(file_stream, line)) {
content += line + "\n";
}
return content;
}
vector<string> read_lines() override {
vector<string> lines;
if (!is_file_open()) {
cout << "❌ File chưa được mở!" << endl;
return lines;
}
string line;
while (getline(file_stream, line)) {
lines.push_back(line);
}
cout << "📖 Đọc được " << lines.size() << " dòng" << endl;
return lines;
}
};
// Concrete class cho Text File Writing
class TextFileWriter : public FileHandler, public WritableFile {
private:
ofstream file_stream;
bool append_mode;
public:
TextFileWriter(string path, bool append = false)
: FileHandler(path), append_mode(append) {}
bool open() override {
if (append_mode) {
file_stream.open(file_path, ios::app);
} else {
file_stream.open(file_path);
}
is_open = file_stream.is_open();
if (is_open) {
cout << "✅ Mở file ghi: " << file_path
<< (append_mode ? " (append mode)" : " (write mode)") << endl;
} else {
cout << "❌ Không thể mở file: " << file_path << endl;
}
return is_open;
}
void close() override {
if (is_open) {
file_stream.close();
is_open = false;
cout << "🔒 Đóng file: " << file_path << endl;
}
}
bool is_file_open() override {
return is_open && file_stream.is_open();
}
bool write_line(const string& content) override {
if (!is_file_open()) {
cout << "❌ File chưa được mở!" << endl;
return false;
}
file_stream << content << endl;
cout << "✍️ Ghi dòng: " << content << endl;
return true;
}
bool write_all(const string& content) override {
if (!is_file_open()) {
cout << "❌ File chưa được mở!" << endl;
return false;
}
file_stream << content;
cout << "✍️ Ghi toàn bộ nội dung (" << content.length() << " ký tự)" << endl;
return true;
}
bool append_line(const string& content) override {
if (!is_file_open()) {
cout << "❌ File chưa được mở!" << endl;
return false;
}
file_stream << content << endl;
cout << "➕ Thêm dòng: " << content << endl;
return true;
}
};
// File Manager sử dụng polymorphism
class FileManager {
public:
static void demo_file_operations() {
cout << "=== DEMO FILE OPERATIONS ===" << endl;
// Tạo file và ghi dữ liệu
TextFileWriter writer("demo.txt");
if (writer.open()) {
writer.write_line("Dòng 1: Xin chào!");
writer.write_line("Dòng 2: Đây là demo file I/O");
writer.write_line("Dòng 3: Sử dụng abstraction");
writer.close();
}
cout << "\n--- Đọc file ---" << endl;
// Đọc file
TextFileReader reader("demo.txt");
if (reader.open()) {
reader.print_status();
cout << "\nĐọc từng dòng:" << endl;
vector<string> lines = reader.read_lines();
for (int i = 0; i < lines.size(); i++) {
cout << "Dòng " << (i+1) << ": " << lines[i] << endl;
}
reader.close();
}
cout << "\n--- Append thêm dữ liệu ---" << endl;
// Append thêm dữ liệu
TextFileWriter appender("demo.txt", true);
if (appender.open()) {
appender.append_line("Dòng 4: Được thêm sau");
appender.append_line("Dòng 5: Append mode");
appender.close();
}
// Đọc lại toàn bộ
cout << "\n--- Đọc lại toàn bộ file ---" << endl;
TextFileReader reader2("demo.txt");
if (reader2.open()) {
string all_content = reader2.read_all();
cout << "Toàn bộ nội dung:\n" << all_content << endl;
reader2.close();
}
}
// Polymorphic function
static void process_file(FileHandler* handler) {
handler->print_status();
if (handler->open()) {
cout << "File được xử lý thành công!" << endl;
handler->close();
} else {
cout << "Không thể xử lý file!" << endl;
}
}
};
int main() {
FileManager::demo_file_operations();
cout << "\n=== POLYMORPHIC FILE HANDLING ===" << endl;
// Sử dụng polymorphism
vector<FileHandler*> handlers;
handlers.push_back(new TextFileReader("demo.txt"));
handlers.push_back(new TextFileWriter("output.txt"));
for (FileHandler* handler : handlers) {
FileManager::process_file(handler);
delete handler;
}
return 0;
}Ví dụ 2: Game State Manager
cpp
#include <iostream>
#include <string>
#include <memory>
#include <vector>
using namespace std;
// Abstract Game State
class GameState {
protected:
string state_name;
bool is_active;
public:
GameState(string name) : state_name(name), is_active(false) {}
// Pure virtual functions
virtual void enter() = 0;
virtual void update() = 0;
virtual void exit() = 0;
virtual void handle_input(char input) = 0;
// Virtual functions với implementation
virtual void render() {
cout << "🎮 State: " << state_name << (is_active ? " [ACTIVE]" : " [INACTIVE]") << endl;
}
virtual string get_name() const {
return state_name;
}
virtual bool get_active_status() const {
return is_active;
}
virtual ~GameState() {
cout << "Hủy state: " << state_name << endl;
}
};
// Interface cho states có thể pause
class PausableState {
public:
virtual void pause() = 0;
virtual void resume() = 0;
virtual bool is_paused() = 0;
virtual ~PausableState() {}
};
// Interface cho states có menu
class MenuState {
public:
virtual void show_menu() = 0;
virtual void select_option(int option) = 0;
virtual int get_selected_option() = 0;
virtual ~MenuState() {}
};
// Concrete State 1 - Main Menu
class MainMenuState : public GameState, public MenuState {
private:
int selected_option;
vector<string> menu_options;
public:
MainMenuState() : GameState("Main Menu"), selected_option(0) {
menu_options = {"Chơi game", "Cài đặt", "Thoát"};
}
void enter() override {
is_active = true;
selected_option = 0;
cout << "🏠 Vào menu chính" << endl;
show_menu();
}
void update() override {
if (is_active) {
cout << "⏰ Cập nhật menu chính..." << endl;
}
}
void exit() override {
is_active = false;
cout << "🚪 Thoát menu chính" << endl;
}
void handle_input(char input) override {
switch (input) {
case 'w':
selected_option = max(0, selected_option - 1);
show_menu();
break;
case 's':
selected_option = min((int)menu_options.size() - 1, selected_option + 1);
show_menu();
break;
case '\n':
select_option(selected_option);
break;
default:
cout << "Phím không hợp lệ! Dùng W/S để di chuyển, Enter để chọn" << endl;
}
}
void show_menu() override {
cout << "\n=== MENU CHÍNH ===" << endl;
for (int i = 0; i < menu_options.size(); i++) {
if (i == selected_option) {
cout << "→ " << menu_options[i] << " ←" << endl;
} else {
cout << " " << menu_options[i] << endl;
}
}
cout << "==================" << endl;
}
void select_option(int option) override {
cout << "✅ Chọn: " << menu_options[option] << endl;
switch (option) {
case 0:
cout << "🎯 Bắt đầu chơi game!" << endl;
break;
case 1:
cout << "⚙️ Mở cài đặt!" << endl;
break;
case 2:
cout << "👋 Thoát game!" << endl;
exit();
break;
}
}
int get_selected_option() override {
return selected_option;
}
};
// Concrete State 2 - Playing Game
class PlayingState : public GameState, public PausableState {
private:
int score;
int level;
bool paused;
int player_x, player_y;
public:
PlayingState() : GameState("Playing"), score(0), level(1), paused(false), player_x(5), player_y(5) {}
void enter() override {
is_active = true;
paused = false;
cout << "🎮 Bắt đầu chơi game!" << endl;
cout << "Điều khiển: WASD để di chuyển, P để pause, Q để quit" << endl;
render_game();
}
void update() override {
if (is_active && !paused) {
score += 10; // Tự động tăng điểm
if (score % 1000 == 0) {
level++;
cout << "🆙 Lên level " << level << "!" << endl;
}
}
}
void exit() override {
is_active = false;
cout << "🏁 Kết thúc game. Điểm cuối: " << score << endl;
}
void handle_input(char input) override {
if (paused && input != 'p') {
cout << "Game đang pause! Nhấn P để tiếp tục" << endl;
return;
}
switch (input) {
case 'w':
player_y = max(0, player_y - 1);
render_game();
break;
case 's':
player_y = min(9, player_y + 1);
render_game();
break;
case 'a':
player_x = max(0, player_x - 1);
render_game();
break;
case 'd':
player_x = min(9, player_x + 1);
render_game();
break;
case 'p':
if (paused) {
resume();
} else {
pause();
}
break;
case 'q':
exit();
break;
default:
cout << "Phím không hợp lệ!" << endl;
}
}
void pause() override {
paused = true;
cout << "⏸️ Game đã pause" << endl;
}
void resume() override {
paused = false;
cout << "▶️ Game tiếp tục" << endl;
render_game();
}
bool is_paused() override {
return paused;
}
void render() override {
GameState::render();
cout << "Điểm: " << score << " | Level: " << level
<< " | Trạng thái: " << (paused ? "PAUSE" : "PLAYING") << endl;
}
private:
void render_game() {
cout << "\n=== GAME MAP ===" << endl;
cout << "Điểm: " << score << " | Level: " << level << endl;
for (int y = 0; y < 10; y++) {
for (int x = 0; x < 10; x++) {
if (x == player_x && y == player_y) {
cout << "🔴"; // Player
} else {
cout << "⬜"; // Empty space
}
}
cout << endl;
}
cout << "=================" << endl;
}
};
// Game State Manager
class GameStateManager {
private:
vector<unique_ptr<GameState>> states;
GameState* current_state;
public:
GameStateManager() : current_state(nullptr) {}
void add_state(unique_ptr<GameState> state) {
states.push_back(move(state));
}
void change_state(const string& state_name) {
// Exit current state
if (current_state) {
current_state->exit();
}
// Find and enter new state
for (auto& state : states) {
if (state->get_name() == state_name) {
current_state = state.get();
current_state->enter();
return;
}
}
cout << "❌ Không tìm thấy state: " << state_name << endl;
}
void update() {
if (current_state) {
current_state->update();
}
}
void render() {
if (current_state) {
current_state->render();
}
}
void handle_input(char input) {
if (current_state) {
current_state->handle_input(input);
}
}
GameState* get_current_state() {
return current_state;
}
};
int main() {
cout << "=== GAME STATE SYSTEM ===" << endl;
// Tạo game state manager
GameStateManager manager;
// Thêm các states
manager.add_state(make_unique<MainMenuState>());
manager.add_state(make_unique<PlayingState>());
// Bắt đầu với main menu
manager.change_state("Main Menu");
// Game loop simulation
cout << "\n=== DEMO GAME LOOP ===" << endl;
// Simulate menu navigation
manager.handle_input('s'); // Di chuyển xuống
manager.handle_input('w'); // Di chuyển lên
manager.handle_input('\n'); // Chọn "Chơi game"
// Chuyển sang playing state
manager.change_state("Playing");
// Simulate gameplay
manager.handle_input('d'); // Di chuyển phải
manager.handle_input('s'); // Di chuyển xuống
manager.handle_input('p'); // Pause
manager.handle_input('w'); // Thử di chuyển khi pause
manager.handle_input('p'); // Resume
manager.handle_input('a'); // Di chuyển trái
// Update và render
manager.update();
manager.render();
cout << "\n=== KẾT THÚC DEMO ===" << endl;
return 0;
}🏋️ Thực hành
Bài tập 1: Shape Calculator
Tạo abstract class Shape với:
- Pure virtual functions:
calculate_area(),calculate_perimeter() - Concrete classes:
Rectangle,Circle,Triangle
Bài tập 2: Plugin System
Tạo interface Plugin với:
- Pure virtual functions:
get_name(),execute(),get_version() - Concrete plugins:
LoggerPlugin,DatabasePlugin
Bài tập 3: Data Processing Pipeline
Tạo abstract class DataProcessor với:
- Pure virtual:
process(),validate() - Concrete:
CSVProcessor,JSONProcessor,XMLProcessor
📋 Tóm tắt
Trừu tượng hóa: Ẩn chi tiết phức tạp, hiển thị giao diện cần thiết
Abstract Class:
- Có ít nhất một pure virtual function
- Không thể tạo object trực tiếp
- Có thể có member variables và methods
Interface:
- Chỉ có pure virtual functions
- Định nghĩa contract cho classes
Lợi ích:
- Code dễ bảo trì
- Tăng tính linh hoạt
- Chuẩn hóa giao diện
- Hỗ trợ polymorphism
Bài tiếp theo: Template - Tìm hiểu về generic programming.