Menu
CoddyTech

Mẫu Builder

Thuộc phần Lập trình hướng đối tượng trong hành trình C++ của Coddy. Bài học 93 trên 104.

Mẫu Builder tách biệt quá trình xây dựng một đối tượng phức tạp khỏi cách biểu diễn đối tượng đó, giúp bạn tạo các cấu hình khác nhau theo từng bước. Cách này đặc biệt hữu ích khi một đối tượng có nhiều tham số tùy chọn hoặc cần một trình tự xây dựng cụ thể.

Thay vì dùng hàm khởi tạo có nhiều tham số (dễ gây nhầm lẫn), Builder cung cấp các phương thức có tên rõ ràng cho từng tùy chọn cấu hình:

#include <iostream>
#include <string>

class Pizza {
public:
    std::string dough;
    std::string sauce;
    std::string topping;
    bool cheese;
    
    void describe() const {
        std::cout << dough << " dough, " << sauce << " sauce, "
                  << topping << (cheese ? ", with cheese" : "") << "\n";
    }
};

class PizzaBuilder {
private:
    Pizza pizza;
    
public:
    PizzaBuilder& setDough(const std::string& d) {
        pizza.dough = d;
        return *this;
    }
    
    PizzaBuilder& setSauce(const std::string& s) {
        pizza.sauce = s;
        return *this;
    }
    
    PizzaBuilder& setTopping(const std::string& t) {
        pizza.topping = t;
        return *this;
    }
    
    PizzaBuilder& addCheese() {
        pizza.cheese = true;
        return *this;
    }
    
    Pizza build() { return pizza; }
};

int main() {
    Pizza margherita = PizzaBuilder()
        .setDough("thin")
        .setSauce("tomato")
        .setTopping("basil")
        .addCheese()
        .build();
    
    margherita.describe();
}

Mỗi phương thức setter trả về tham chiếu đến builder (return *this), cho phép nối chuỗi phương thức để tạo giao diện linh hoạt. Phương thức build() hoàn tất và trả về đối tượng đã được tạo.

Dùng Builder khi tạo đối tượng có nhiều thành phần tùy chọn, khi bạn muốn code khởi tạo dễ đọc hoặc khi cùng một quy trình khởi tạo cần tạo ra nhiều cách biểu diễn khác nhau.

challenge icon

Thử thách

Dễ

Hãy xây dựng hệ thống Computer Builder cho phép người dùng từng bước cấu hình máy tính cá nhân theo nhu cầu. Đây là tình huống lý tưởng để áp dụng mẫu Builder: máy tính có nhiều linh kiện tùy chọn, và chúng ta muốn có cách rõ ràng, dễ đọc để lắp ráp các cấu hình khác nhau.

Bạn sẽ sắp xếp code trong ba tệp:

  • Computer.h: Định nghĩa lớp sản phẩm đại diện cho một máy tính đã được cấu hình đầy đủ.

    Lớp Computer của bạn cần lưu các linh kiện sau dưới dạng thành viên private: cpu (chuỗi), ram (chuỗi), storage (chuỗi), gpu (chuỗi) và hasWifi (giá trị boolean). Mặc định, khởi tạo hasWifi thành false và các chuỗi thành rỗng.

    Thêm phương thức public có tên showSpecs() để hiển thị cấu hình máy tính. Với mỗi linh kiện đã được thiết lập (chuỗi không rỗng), hãy in linh kiện đó trên một dòng riêng. Chỉ in khả năng kết nối wifi khi tính năng này được bật.

  • ComputerBuilder.h: Tạo lớp builder để xây dựng máy tính bằng giao diện thông suốt.

    Lớp ComputerBuilder cần có một thành viên Computer private để dần hoàn thiện máy tính. Triển khai các phương thức sau, mỗi phương thức trả về một tham chiếu đến builder để cho phép gọi nối tiếp các phương thức:

    • setCPU(const std::string& cpu)
    • setRAM(const std::string& ram)
    • setStorage(const std::string& storage)
    • setGPU(const std::string& gpu)
    • addWifi(): bật khả năng kết nối wifi

    Đồng thời, triển khai phương thức build() để trả về đối tượng Computer đã hoàn thiện và đặt lại builder để có thể sử dụng lại.

  • main.cpp: Kết hợp mọi thứ để xây dựng một máy tính theo cấu hình tùy chỉnh.

    Đọc bốn đầu vào:

    1. Mẫu CPU (chuỗi)
    2. Thông số RAM (chuỗi)
    3. Thông số bộ lưu trữ (chuỗi)
    4. Có thêm wifi hay không: yes hoặc no

    Dùng ComputerBuilder để xây dựng máy tính với CPU, RAM và bộ lưu trữ đã nhập. Nếu đầu vào wifi là yes, hãy gọi thêm addWifi(). Lưu ý rằng cấu hình này không chỉ định GPU: builder cần xử lý linh kiện tùy chọn một cách phù hợp.

    Sau khi xây dựng xong, gọi showSpecs() trên máy tính vừa tạo để hiển thị cấu hình.

Phương thức showSpecs() cần xuất kết quả theo định dạng sau, chỉ hiển thị các linh kiện đã được thiết lập:

CPU: [value]
RAM: [value]
Storage: [value]
GPU: [value]
Wifi: Enabled

Ví dụ, với đầu vào Intel i7-12700K, 32GB DDR5, 1TB NVMe SSD và yes:

CPU: Intel i7-12700K
RAM: 32GB DDR5
Storage: 1TB NVMe SSD
Wifi: Enabled

Với đầu vào AMD Ryzen 5 5600X, 16GB DDR4, 512GB SSD và no:

CPU: AMD Ryzen 5 5600X
RAM: 16GB DDR4
Storage: 512GB SSD

Hãy chú ý cách mẫu Builder giúp quá trình xây dựng dễ đọc và linh hoạt. Bạn có thể dễ dàng thêm hoặc bỏ qua linh kiện, còn việc gọi nối tiếp các phương thức tạo nên đoạn code rõ ràng, dễ hiểu. Dòng GPU không xuất hiện vì chúng ta chưa thiết lập GPU, cho thấy builder xử lý linh kiện tùy chọn một cách tự nhiên.

Tự làm thử

#include <iostream>
#include <string>
#include "ComputerBuilder.h"

using namespace std;

int main() {
    // Đọc các giá trị đầu vào
    string cpu, ram, storage, wifiChoice;
    getline(cin, cpu);
    getline(cin, ram);
    getline(cin, storage);
    getline(cin, wifiChoice);

    // TODO: Tạo một instance của ComputerBuilder
    
    // TODO: Gọi nối tiếp các phương thức để thiết lập CPU, RAM và bộ nhớ lưu trữ
    
    // TODO: Nếu wifiChoice là "yes", gọi thêm addWifi()
    
    // TODO: Gọi build() để lấy đối tượng Computer
    
    // TODO: Gọi showSpecs() trên máy tính đã tạo để hiển thị cấu hình

    return 0;
}
quiz iconTự kiểm tra

Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.

Tất cả bài học trong Lập trình hướng đối tượng

Tự luyện tập: Trình biên dịch C++ online