【设计模式】其它经典模式-空对象模式(Null Object Pattern)

空对象模式(Null Object Pattern)(C++ 示例)

模式核心思想

用行为合理的空对象替代 nullptr 检查,消除客户端代码中的判空逻辑,避免空指针异常,提供默认的安全行为。

模式结构
  1. 抽象接口 (AbstractObject)
    定义客户端依赖的通用接口
  2. 真实对象 (RealObject)
    实现具体业务逻辑
  3. 空对象 (NullObject)
    实现无操作的默认行为(安全替代品)
  4. 客户端 (Client)
    通过抽象接口操作对象,无需判空

C++ 实现示例

场景描述

实现一个日志系统,当未配置日志器时使用安全的空日志器,避免客户端判空。

代码实现
#include <iostream>
#include <string>
#include <memory>

// 1. 抽象接口
class ILogger {
public:
    virtual void log(const std::string& message) = 0;
    virtual ~ILogger() = default;
};

// 2. 真实对象
class ConsoleLogger : public ILogger {
public:
    void log(const std::string& message) override {
        std::cout << "[LOG] " << message << std::endl;
    }
};

// 3. 空对象(关键实现)
class NullLogger : public ILogger {
public:
    void log(const std::string&) override {
        // 安全无操作(替代nullptr)
    }
};

// 4. 对象工厂(封装空对象创建)
class LoggerFactory {
public:
    static std::unique_ptr<ILogger> createLogger(bool enableLogging) {
        if (enableLogging) {
            return std::make_unique<ConsoleLogger>();
        }
        return std::make_unique<NullLogger>(); // 返回空对象而非nullptr
    }
};

// 5. 客户端代码(无判空逻辑)
void processTransaction(int amount, ILogger& logger) {
    logger.log("Transaction started");
    // 业务逻辑...
    logger.log("Processed amount: " + std::to_string(amount));
}

int main() {
    auto logger = LoggerFactory::createLogger(false); // 关闭日志
    
    // 安全调用(无需检查nullptr)
    processTransaction(100, *logger);
    
    // 启用真实日志
    auto activeLogger = LoggerFactory::createLogger(true);
    processTransaction(200, *activeLogger);
}
关键输出
[LOG] Transaction started
[LOG] Processed amount: 200

模式优势

  1. 消除判空检查
    客户端代码不再需要 if(logger != nullptr) 防御性检查
  2. 防止空指针崩溃
    空对象提供安全默认行为
  3. 简化客户端代码
    业务逻辑更清晰,专注核心流程
  4. 符合开闭原则
    新增日志类型不影响客户端
  5. 明确表达空语义
    比返回 nullptr 更具表达力

适用场景

  1. 需要安全替代 nullptr 的场合
  2. 客户端不应处理对象缺失逻辑时
  3. 系统存在大量判空检查时
  4. 需要提供默认无操作行为时

重要提示:当对象缺失是严重错误时(需显式处理),不适合使用此模式。

推荐阅读