标记接口模式(Marker Interface Pattern)在 C++ 中的实现

1. 模式定义

标记接口(Marker Interface)是一种不包含任何方法或数据的特殊接口,仅用于标记类的元数据信息。其主要目的是通过类型系统在编译期或运行期为类附加语义标签,实现类型检测或策略选择。

2. 核心思想
  • 空接口:不声明任何成员函数(纯虚函数)。
  • 类型标签:通过继承关系传递类型信息。
  • 多态检测:利用 dynamic_cast 或模板特化检测继承关系。

3. C++ 实现步骤(结合编译期与运行期)
步骤 1:定义标记接口
// 标记接口:可序列化
class Serializable {};

// 标记接口:线程安全
class ThreadSafe {};
步骤 2:实现具体类
class Document : public Serializable {
public:
    void save() const { std::cout << "Saving document...\n"; }
};

class Logger : public Serializable, public ThreadSafe {
public:
    void log() { std::cout << "Logging thread-safe...\n"; }
};

class Calculator {  // 无任何标记
public:
    void compute() { std::cout << "Computing...\n"; }
};
步骤 3:利用标记进行逻辑控制
方式 1:运行期检测(dynamic_cast)
void processObject(void* obj) {
    // 检测是否可序列化
    if (auto* serializable = dynamic_cast<Serializable*>(static_cast<Object*>(obj))) {
        std::cout << "Object is serializable\n";
        // 安全调用具体类方法(需向下转型)
        if (auto* doc = dynamic_cast<Document*>(serializable)) {
            doc->save();
        }
    }

    // 检测是否线程安全
    if (dynamic_cast<ThreadSafe*>(static_cast<Object*>(obj))) {
        std::cout << "Object is thread-safe\n";
    }
}
方式 2:编译期检测(模板 + SFINAE)
// 利用 std::is_base_of 进行类型特征检查
template <typename T>
void serializeIfPossible(const T& obj) {
    if constexpr (std::is_base_of_v<Serializable, T>) {
        std::cout << "Serializing: ";
        obj.save();  // 直接调用,编译期安全检查
    } else {
        std::cout << "Cannot serialize this type\n";
    }
}

4. 完整代码示例
#include <iostream>
#include <type_traits>

// 标记接口定义
class Serializable {};
class ThreadSafe {};

// 具体类实现
class Document : public Serializable {
public:
    void save() const { std::cout << "Document saved.\n"; }
};

class Logger : public Serializable, public ThreadSafe {
public:
    void log() const { std::cout << "Logging safely.\n"; }
};

class Calculator {  // 无标记
public:
    void compute() const { std::cout << "Computing...\n"; }
};

// 编译期处理(模板元编程)
template <typename T>
void process(const T& obj) {
    obj.compute();  // 所有类型通用操作

    if constexpr (std::is_base_of_v<Serializable, T>) {
        std::cout << ">> Serializing: ";
        obj.save();
    }

    if constexpr (std::is_base_of_v<ThreadSafe, T>) {
        std::cout << ">> Thread-safe operation: ";
        obj.log();
    }
}

int main() {
    Document doc;
    Logger logger;
    Calculator calc;

    // 编译期分派
    process(doc);     // 输出序列化
    process(logger);  // 输出序列化和线程安全
    process(calc);    // 仅输出计算

    return 0;
}
5. 输出结果
Computing...
>> Serializing: Document saved.
Computing...
>> Serializing: Logging safely.
>> Thread-safe operation: Logging safely.
Computing...

6. 关键优势
  1. 类型安全:编译期检测避免运行时错误。
  2. 解耦:业务逻辑与标记接口分离,符合开闭原则。
  3. 灵活扩展:新增标记无需修改已有类(如添加 Cloneable 接口)。
  4. 多态增强:配合 dynamic_cast 实现运行时分派。
7. 适用场景
  • 需要为类附加元数据(如序列化、线程安全)。
  • 策略模式中根据不同标记选择不同算法。
  • 框架中自动识别组件特性(如 ORM 中的持久化标记)。
8. 对比其他技术
技术优势劣势
标记接口类型安全,编译期优化需继承(可能受限于单继承)
属性(C#/Java)无需继承,可附加任意元数据C++ 原生不支持
枚举 + 变量简单直观缺乏类型约束,易出错

推荐阅读

更多推荐