Saturday, January 21, 2017

Design Pattern: Sử dụng Template để cài đặt mẫu thiết kế Singleton trong C++

Singleton là một mẫu thiết kế thông dụng được sử dụng để đảm bảo chỉ có một đối tượng duy nhất được tạo ra và có thể truy xuất mọi lúc, mọi nơi trong trong chương trình. Tôi thường sử dụng mẫu này trong Configurations, Logger, Manager,...

Để đảm bảo được hai được tính này, khi cài đặt bạn phải chú ý:
  • Phải sử dụng đối tượng static và hàm static getInstanse.
  • Hàm khởi tạo (Contructor) và hàm hủy (Detructor) phải là private => Điều này đảm bảo có duy nhất một thể hiện, trừ hàm getInstance ra thì không có cách nào khác để tạo/lấy đổi tượng.
Dưới đây là cài đặt một lớp Singleton cơ bản:
Tệp Singleton.h:
#ifndef SINGLETON_H
#define SINGLETON_H

class Singleton {
private:
    static Singleton m_instance;
    Singleton();
public:
    static Singleton* getInstance();
    
    void yourMethod();
};

#endif /* SINGLETON_H */

Tệp Singleton.cpp:
#include "Singleton.h"
#include <iostream>

using namespace std;

Singleton Singleton::m_instance;

Singleton* Singleton::getInstance() {
    return &m_instance;
}

Singleton::Singleton() {
}

void Singleton::yourMethod() {
    cout << "Implement your method at here";
}

Trong hàm main, bạn sử dụng đoạn code sau đê chạy:
Singleton* test = Singleton::getInstance();
test->yourMethod();

Cách này có nhược điểm là nếu có nhiều lớp cần sử dụng cơ chế này thì mỗi lớp phải cài đặt riêng. Để áp dụng cơ chế chung và tái sử dụng được khi cài đặt bạn nên sử dụng Nguyên mẫu (Template). Cụ thể cài đặt như sau:

Tệp Singleton.h
#ifndef SINGLETON_H
#define SINGLETON_H
#include <stddef.h>

template<typename T>
class Singleton {
private:
    friend T;
    Singleton() {}
    virtual ~Singleton() {};
    
public:
    static T* getInstance() {
 static T instance;
 return &instance;
    }
};

#endif /* SINGLETON_H */
Các lớp khác kế thừa lại lớp này để có thể sử dụng lại được ngay cơ chế Singleton mà không phải cài đặt lại => Đây là ưu điểm cách này. Dưới đây là ví dụ tạo lớp Manager kế thừa lại cơ chế này từ lớp Singleton:

Tệp Manager.h:
#ifndef MANAGER_H
#define MANAGER_H

#include "Singleton.h"

class Manager : public Singleton<Manager> {
public:
    void manage();
    
private:
    friend Singleton<Manager>;
    Manager();
    virtual ~Manager();
};

#endif /* MANAGER_H */

Tệp Manager.cpp:
#include "Manager.h"
#include <iostream>

using namespace std;

Manager::Manager() : Singleton<Manager>() {
}

Manager::~Manager() {
}

void Manager::manage() {
    cout << "Implement at here";
}

Trong hàm main, bạn sử dụng lớp này như sau:

Manager* t = Manager::getInstance();
t->manage();

Khi cài đặt Singleton sử dụng Template, bạn cần chú ý:
  • Phải sử dụng từ khóa friend để đảm bảo hàm khởi tạo và hàm hủy phải là private.
  • Template là mẫu khỏi tạo trong thời gian biên dịch (compile-time), chứ không phải trong thời gian liên kết (link-time) trong khi các khối mã trong tệp c/cpp thì chỉ được biết đến trong thời gian liên kết => Chính vì vậy mà các Template chỉ được được đặt trong tệp .h

No comments:

Post a Comment