IT Notes

Паттерн Null Object

Паттерн Null Object является довольно простой концепцией ООП, но легко позволяет устранить ненужные условные конструкции и тем самым упростить понимание кода. Для этого и предназначен полиморфизм в первую очередь.

Идея паттерна Null Object заключается в том, что в качестве одной из реализаций некоторого интерфейса добавляется Null-класс, который обладает нейтральным поведением. Учтите, что он не может быть абстрактным, поскольку ожидается создание его экземпляров.

Рассмотрим принцип использование Null Object на примере. Предположим, что мы разрабатываем приложение для различных текстовых преобразований. Для этого определим следующий интерфейсный класс:

class TextConverter {
public:
    virtual ~TextConverter() { }
    virtual QString convert( const QString& text ) = 0;
};

Реализуем на основе этого интерфейса класс, который приводит все символы входного текста к верхнему регистру:

class UpperCaseTextConverter : public TextConverter {
public:
    QString convert( const QString& text ) {
        return text.toUpper();
    }
};

Без паттерна Null Object мы уже можем воспользоваться соответствующей функциональностью:

TextConverter* converter = NULL;

// … возможно, где-то вызывается converter = new UpperCaseTextConverter;

static const QString text = "Hello, world!";
if( converter )  {
    qDebug() << converter->convert( text );
}

// …

delete converter;

Однако обратите внимание на проверку перед вызовом функции convert(). Она необходима, поскольку указатель converter может указывать на NULL. Чтобы избежать этой проверки, применим Null Object:

class NullTextConverter : public TextConverter {
public:
    QString convert( const QString& text ) {
        // Ничего не делаем
        return text;
    }
};

Теперь осталось принять условное соглашение о том, что указатель TextConverter* изначально должен быть инициализирован не NULL, а экземпляром класса NullTextConverter (или каким-нибудь другим конкретным экземпляром):

TextConverter* converter = new NullTextConverter;

// … возможно, где-то вызывается converter = new UpperCaseTextConverter;

static const QString text = "Hello, world!";
// Можем смело вызывать, поскольку указатель всегда должен быть инициализирован:
qDebug() << converter->convert( text );

// …

delete converter;

Главное в этой ситуации - не забывать инициализировать указатель с помощью Null-объекта, иначе вы быстро запутаетесь и в вашем коде появятся ошибки.

Однако стоит признать, что этот паттерн больше подходит для тех языков программирования, где предусмотрена сборка мусора (например, Java). Но и на С++ его можно вполне успешно применять.

Таким образом, паттерн Null Object удачно дополняет архитектуру ООП-программ. А в сочетании с другими структурными решениями позволяет упростить код и существенно сократить число проверок.

Похожие публикации