Skip to content

📐 Abstract Classes — Lớp trừu tượng

Abstract class định nghĩa interface mà derived classes phải implement. Không thể tạo object từ abstract class.

Pure Virtual Functions

cpp
class Shape {
public:
    // Pure virtual — không có implementation
    virtual double area() const = 0;    // "= 0" làm nó pure
    virtual void draw() const = 0;
    
    // Virtual destructor (không pure)
    virtual ~Shape() = default;
};

int main() {
    // ❌ Error: cannot instantiate abstract class
    // Shape s;
    
    // ✅ Nhưng có thể dùng pointer/reference
    Shape* ptr = nullptr;
}

Abstract vs Concrete Classes

TypePure Virtual?Can Instantiate?
AbstractCó ít nhất 1❌ No
ConcreteKhông có✅ Yes
cpp
// Abstract — có pure virtual
class Drawable {
public:
    virtual void draw() const = 0;
};

// Abstract — kế thừa nhưng chưa implement hết
class Shape : public Drawable {
    // Vẫn abstract vì chưa implement draw()
};

// Concrete — implement tất cả pure virtuals
class Circle : public Shape {
public:
    void draw() const override {  // ✅ Bắt buộc implement
        std::cout << "Drawing Circle\n";
    }
};

Interface Pattern (Pure Abstract Class)

C++ không có interface keyword như Java/C#, nhưng ta có thể mô phỏng:

cpp
// Interface = Pure abstract class (chỉ có pure virtuals)
class ILogger {
public:
    virtual void log(const std::string& message) = 0;
    virtual void setLevel(int level) = 0;
    virtual ~ILogger() = default;
};

class ISerializable {
public:
    virtual std::string serialize() const = 0;
    virtual void deserialize(const std::string& data) = 0;
    virtual ~ISerializable() = default;
};

// Concrete class implementing interfaces
class FileLogger : public ILogger {
public:
    void log(const std::string& message) override {
        std::cout << "[FILE] " << message << std::endl;
    }
    
    void setLevel(int level) override {
        // Implementation
    }
};

class ConsoleLogger : public ILogger {
public:
    void log(const std::string& message) override {
        std::cout << "[CONSOLE] " << message << std::endl;
    }
    
    void setLevel(int level) override {
        // Implementation
    }
};

Naming Convention

cpp
// Prefixed with 'I' để biết là interface
class IObserver { };
class ISubject { };
class IRepository { };

// Hoặc suffix với 'Interface' / 'Base'
class LoggerInterface { };
class RepositoryBase { };

Dependency Injection với Interfaces

cpp
#include <iostream>
#include <memory>

// Interface
class IDatabase {
public:
    virtual void save(const std::string& data) = 0;
    virtual std::string load(int id) = 0;
    virtual ~IDatabase() = default;
};

// Concrete implementations
class MySQLDatabase : public IDatabase {
public:
    void save(const std::string& data) override {
        std::cout << "[MySQL] Saving: " << data << std::endl;
    }
    
    std::string load(int id) override {
        return "[MySQL] Data for id " + std::to_string(id);
    }
};

class MongoDatabase : public IDatabase {
public:
    void save(const std::string& data) override {
        std::cout << "[MongoDB] Saving: " << data << std::endl;
    }
    
    std::string load(int id) override {
        return "[MongoDB] Data for id " + std::to_string(id);
    }
};

// Service depends on INTERFACE, not concrete class
class UserService {
    std::unique_ptr<IDatabase> db_;
    
public:
    // Inject dependency
    explicit UserService(std::unique_ptr<IDatabase> db)
        : db_(std::move(db)) {}
    
    void createUser(const std::string& name) {
        db_->save("User: " + name);
    }
    
    void getUser(int id) {
        std::cout << db_->load(id) << std::endl;
    }
};

int main() {
    // Dễ dàng swap implementations!
    auto mysqlService = UserService(std::make_unique<MySQLDatabase>());
    mysqlService.createUser("Alice");
    
    auto mongoService = UserService(std::make_unique<MongoDatabase>());
    mongoService.createUser("Bob");
    
    return 0;
}

Abstract Class với Partial Implementation

cpp
class Document {
protected:
    std::string content_;
    
public:
    // Concrete method — shared implementation
    void setContent(const std::string& content) {
        content_ = content;
    }
    
    std::string getContent() const {
        return content_;
    }
    
    // Pure virtual — must be implemented by derived
    virtual void save(const std::string& path) = 0;
    virtual void load(const std::string& path) = 0;
    
    virtual ~Document() = default;
};

class PDFDocument : public Document {
public:
    void save(const std::string& path) override {
        std::cout << "Saving PDF to " << path << std::endl;
    }
    
    void load(const std::string& path) override {
        std::cout << "Loading PDF from " << path << std::endl;
    }
};

class WordDocument : public Document {
public:
    void save(const std::string& path) override {
        std::cout << "Saving Word to " << path << std::endl;
    }
    
    void load(const std::string& path) override {
        std::cout << "Loading Word from " << path << std::endl;
    }
};

Template Method Pattern

cpp
class Game {
public:
    // Template method — defines algorithm skeleton
    void play() {
        initialize();
        while (!isGameOver()) {
            processInput();
            update();
            render();
        }
        cleanup();
    }
    
protected:
    // Hook methods — can be overridden
    virtual void initialize() { }
    virtual void cleanup() { }
    
    // Pure virtual — MUST be implemented
    virtual void processInput() = 0;
    virtual void update() = 0;
    virtual void render() = 0;
    virtual bool isGameOver() const = 0;
    
public:
    virtual ~Game() = default;
};

class ChessGame : public Game {
protected:
    void initialize() override {
        std::cout << "Setting up chess board...\n";
    }
    
    void processInput() override {
        std::cout << "Getting move...\n";
    }
    
    void update() override {
        std::cout << "Updating game state...\n";
    }
    
    void render() override {
        std::cout << "Drawing board...\n";
    }
    
    bool isGameOver() const override {
        return false;  // Simplified
    }
};

Khi nào dùng Abstract Class vs Interface?

Use CaseRecommendation
Define contract onlyPure interface (all pure virtual)
Share some implementationAbstract class with mix
Multiple "is-a" relationshipsMultiple interfaces
Single hierarchyAbstract base class

📚 Tổng kết

ConceptKey Takeaway
Pure virtual (= 0)Method không có implementation
Abstract classCó ít nhất 1 pure virtual, không instantiate được
InterfacePure abstract class (chỉ pure virtuals)
Dependency InjectionDepend on abstractions, not concretions
Template MethodDefine skeleton, defer steps to subclasses

➡️ Tiếp theo

Tiếp theo: RPG Class System — Áp dụng Inheritance & Polymorphism vào game project.