Blog Datasheets Home About me Clients My work Services Contact

G2Labs Grzegorz Grzęda

Implementing the Null Object Pattern in C++ and C

April 30, 2023

Implementing the Null Object Pattern in C++ and C

In programming, especially when developing complex applications, maintaining a consistent structure is crucial. However, you might encounter scenarios where you need to implement a placeholder functionality that essentially does nothing. This is where the Null Object pattern becomes handy.

The Logger Example: A C++ Approach

Consider a simple calculator application in C++. The application performs basic tasks and logs each step to the standard output.

Project Structure

1
2
3
4
5
6
7
null-object-pattern-example-cpp \
    Makefile
    main.cpp
    ILog.hpp
    ConsoleLog.hpp
    CalcModel.hpp
    NullLog.hpp

ILog Interface

ILog.hpp is an interface with a single method for logging:

1
2
3
4
5
// ILog.hpp
struct ILog {
    virtual ~ILog() {}
    virtual void log(const char *) = 0;
};

ConsoleLog Implementation

ConsoleLog.hpp prints messages to stdout:

1
2
3
4
5
6
7
8
9
// ConsoleLog.hpp
#include "ILog.hpp"
#include <iostream>

struct ConsoleLog : ILog {
    void log(const char *text) {
        std::cout << "[LOG] " << text << std::endl;
    }
};

CalcModel Class

CalcModel.hpp is a simple calculator with logging:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// CalcModel.hpp
#include "ILog.hpp"

struct CalcModel {
    CalcModel(ILog *log) : log(log) {}

    void setOperand(int operand) {
        // ... Set operand logic
        log->log("Setting operand");
    }

    // Additional methods like compute(), getResult(), etc.

private:
    ILog *log;
};

Main Function

The main.cpp file manages inputs and logs operations:

1
2
3
4
5
6
7
8
// main.cpp
#include "ConsoleLog.hpp"
#include "CalcModel.hpp"
// Additional includes

int main() {
    // Code to initialize and use CalcModel and ConsoleLog
}

Introducing Null Object Pattern

To disable logging as per a client’s request, we introduce NullLog:

1
2
3
4
5
6
7
8
// NullLog.hpp
#include "ILog.hpp"

struct NullLog : ILog {
    void log(const char *text) override {
        // Do nothing
    }
};

In main.cpp, we conditionally use NullLog:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// main.cpp
#include "ConsoleLog.hpp"
#include "NullLog.hpp"
#include "CalcModel.hpp"
// Additional includes

#define USE_NULL_LOG

int main() {
    #ifdef USE_NULL_LOG
    ILog *log = new NullLog();
    #else
    ILog *log = new ConsoleLog();
    #endif

    // Rest of the code
}

Extending the Example: A C Approach

In C, implementing the Null Object pattern can be more challenging due to the lack of polymorphism. However, we can use function pointers to achieve a similar effect.

Logger in C

Define a logger function pointer and two logging functions:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// logger.h
#ifndef LOGGER_H
#define LOGGER_H

typedef void (*Logger)(const char *text);

void console_log(const char *text);
void null_log(const char *text);

#endif
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// logger.c
#include "logger.h"
#include <stdio.h>

void console_log(const char *text) {
    printf("[LOG] %s\n", text);
}

void null_log(const char *text) {
    // Do nothing
}

Using Logger in a C Program

In your C program, you can now choose which logger to use:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// main.c
#include "logger.h"

int main() {
    Logger log = console_log; // or null_log

    log("This is a log message");

    // Rest of the code
}

Conclusion

The Null Object pattern in C++ and C offers a way to seamlessly switch between active functionality and a ‘do-nothing’ behavior. It’s particularly useful in scenarios where you need to maintain a consistent structure but require the flexibility to disable certain functionalities without altering the overall architecture. This pattern not only keeps your code clean but also prevents the system from branching into multiple versions for different needs.


➡️ Design patterns and SOLID principles: A comprehensive overview


⬅️ Abstract factory pattern


Go back to Posts.