Skip to content

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

  1. Trừu tượng hóa: Ẩn chi tiết phức tạp, hiển thị giao diện cần thiết

  2. 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
  3. Interface:

    • Chỉ có pure virtual functions
    • Định nghĩa contract cho classes
  4. 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.

Khóa học C++ miễn phí