+2

[C++ OOP Thực Chiến] Bài 2: Class và Object - Ngừng code "bún rối", hãy tư duy như một nhà thiết kế hệ thống!

Chào các bạn, mình đã quay trở lại với series OOP Thực chiến đây!

Ở [Bài 1], chúng ta đã thấy std::vector bá đạo thế nào khi giúp ta lưu trữ hàng loạt dữ liệu. Nhưng giả sử bạn đang code một hệ thống Đặt vé xem phim. Mỗi một chiếc vé sẽ có: Tên phim, Số ghế, Giá tiền, Trạng thái (đã bán hay chưa).

Nếu dùng kiểu lập trình thủ tục (Procedural Programming) của C cũ, bạn sẽ phải tạo ra 4 cái vector riêng biệt để lưu 4 thông tin này, rồi loay hoay viết các hàm rời rạc để xử lý chúng. Code một hồi, mã nguồn của bạn sẽ rối tung lên như một mớ bún!

Đó là lúc chúng ta cần đến "phép thuật" cốt lõi của OOP: Class và Object.

1. Class (Lớp) - Bản thiết kế hệ thống

Dưới góc nhìn của một kỹ sư,** Class** giống như một bản thiết kế (Blueprint) hoặc một cái khuôn đúc. Nó không phải là dữ liệu thật, mà nó định nghĩa xem: một thực thể trong hệ thống của bạn sẽ có "hình thù" như thế nào và "làm" được những gì.

Một Class bao gồm 2 thành phần chính:

  • Thuộc tính (Properties / Attributes): Dữ liệu mà nó lưu trữ (Tên phim, Số ghế...). Trạng thái của dữ liệu.
  • Phương thức (Methods / Behaviors): Các hàm thao tác trên dữ liệu đó (Hàm đặt vé, Hàm hủy vé...).

Bạn định nghĩa Class một lần, và tái sử dụng nó mãi mãi.

2. Object (Đối tượng) - Thực thể sống trong RAM

Nếu Class là "Bản thiết kế cái ô tô", thì Object chính là "Chiếc ô tô thật đang chạy ngoài đường".

Khi bạn sử dụng cái khuôn (Class) để tạo ra một dữ liệu cụ thể trong bộ nhớ (RAM), dữ liệu đó được gọi là Object (hay Instance - Bản thể). Từ một Class MovieTicket, bạn có thể đúc ra hàng triệu Object khác nhau: vé của bạn, vé của mình, vé ghế VIP, vé ghế thường... Mỗi Object sẽ giữ một trạng thái dữ liệu độc lập, không ai đụng chạm ai.

3. Code Demo: Định hình hệ thống Đặt vé

Hãy xem cách chúng ta "gói" dữ liệu và hàm xử lý lại với nhau bằng C++:

#include <iostream>
#include <string>

using namespace std;

// 1. CLASS: Bản thiết kế chiếc vé xem phim
class MovieTicket {
private:
    // Thuộc tính (Data) - Tạm thời giấu kín bên trong (private)
    string movieName;
    string seatNumber;
    double price;
    bool isBooked;

public:
    // Constructor (Hàm khởi tạo): Được gọi tự động khi một Object ra đời
    MovieTicket(string name, string seat, double ticketPrice) {
        movieName = name;
        seatNumber = seat;
        price = ticketPrice;
        isBooked = false; // Mặc định vé mới tạo là chưa ai đặt
    }

    // Phương thức (Behavior) - Các hành động mà vé có thể thực hiện
    void bookTicket() {
        if (!isBooked) {
            isBooked = true;
            cout << "[SUCCESS] Dat thanh cong ghe " << seatNumber 
                 << " cho phim " << movieName << "!\n";
        } else {
            cout << "[FAILED] Xin loi, ghe " << seatNumber << " da co nguoi dat!\n";
        }
    }

    void printTicketInfo() {
        cout << "--- TICKET INFO ---\n"
             << "Phim: " << movieName << " | Ghe: " << seatNumber 
             << " | Gia: $" << price 
             << " | Trang thai: " << (isBooked ? "Da ban" : "Con trong") << "\n"
             << "-------------------\n";
    }
};

int main() {
    // 2. OBJECT: Đúc ra các thực thể cụ thể từ khuôn MovieTicket
    
    // Object 1: Vé xem Avengers ghế A1
    MovieTicket ticket1("Avengers: Endgame", "A1", 10.5);
    
    // Object 2: Vé xem Batman ghế VIP1
    MovieTicket ticket2("The Batman", "VIP1", 25.0);

    // Kiểm tra trạng thái ban đầu
    ticket1.printTicketInfo();

    // Thao tác với Object thông qua các phương thức của nó
    ticket1.bookTicket(); // Đặt vé lần 1: Thành công
    ticket1.bookTicket(); // Đặt vé lần 2: Thất bại vì đã có người đặt

    // Object 2 hoạt động hoàn toàn độc lập với Object 1
    ticket2.printTicketInfo();

    return 0;
}

Nhận xét: Thay vì phải truyền lắt nhắt từng biến movieName, seatNumber vào các hàm rời rạc, giờ đây cú pháp ticket1.bookTicket() cực kỳ "thuận tự nhiên" và dễ đọc. Object ticket1 tự biết nó đang giữ dữ liệu gì và tự xử lý logic của riêng nó!

Tạm kết & Gợi mở

Tuyệt vời! Các bạn đã nắm được cách nhóm các dữ liệu và logic liên quan lại thành một thực thể duy nhất bằng Class và Object. Đây là bước đệm đầu tiên để xây dựng một kiến trúc phần mềm (Software Architecture) vững chắc.

Nhưng khoan đã, bạn có để ý trong đoạn code trên, mình đã đặt các biến dữ liệu như movieName, price vào một khu vực tên là private không? Tại sao mình không phơi bày nó ra cho dễ dùng?

Hãy tưởng tượng nếu ai đó can thiệp từ bên ngoài và gán ticket1.price = 0; (hack giá vé về 0 đồng) thì hệ thống của chúng ta sẽ toang ngay lập tức! Việc che giấu những chi tiết phức tạp, nhạy cảm bên trong và chỉ phơi bày ra những giao diện cần thiết cho người dùng sử dụng được gọi là nghệ thuật Trừu tượng hóa.

Và đó chính là chủ đề của bài tiếp theo! Hẹn gặp lại các bạn ở Bài 3: Trừu tượng (Abstraction) là gì? - Nghệ thuật thiết kế API "chống ngu" trong C++. Đừng quên Upvote và để lại bình luận nếu bạn có thắc mắc nhé!


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí