Как называется принцип, реализованный в приведенном коде?

Не могу найти, как называется принцип, реализованный в приведенном ниже коде:

ILog является абстрактным классом, который является общедоступным (программист может подключить этот класс к своему проекту).

ilog.h


class ILog 
{
public:
    
    ILog()                             = default;
    ILog& operator=(const ILog& other) = default;
    ILog& operator=(ILog&& other)      = default;
    ILog(const ILog& other)            = default;
    ILog(ILog&& other)                 = default;
    virtual ~ILog()                    = default;

    virtual void write(const quint32& level, const QString& message) noexcept = 0;
    
    // Реализовано в скрытом классе для программиста.
    static ILog* createInstance() noexcept;
};

SimpleLog реализовывает write метод и createInstance. Данный класс скрыт от программиста (подключить его при помощи include в свой проект нельзя).

simplelog.h


class SimpleLog : public ILog
{
public:
    
    SimpleLog();
    SimpleLog& operator=(const SimpleLog& other) = default;
    SimpleLog& operator=(SimpleLog&& other)      = default;
    SimpleLog(const SimpleLog& other)            = default;
    SimpleLog(SimpleLog&& other)                 = default;
    virtual ~SimpleLog()                         = default;

    void write(const quint32& level, const QString& message) noexcept override;
};

simplelog.cpp


void SimpleLog::write(const quint32& level, const QString& message) noexcept
{
    // ...
}

SimpleLog::SimpleLog() : ILog()
{}

ILog* ILog::createInstance() noexcept
{
    return new SimpleLog;
}

И теперь мы можем использовать наш Logger. Пример приведен в main.cpp:

main.cpp

#include "ilog.h"

int main()
{
    ILog* log = ILog::createInstance();
    
    log->write(1, "Example");

    // ...

    delete log;
    
}

Также меня интересует, популярен ли этот принцип и в каких случаях лучше всего его использовать.

PS Важно отметить, что при использовании этого принципа при создании статической библиотеки, программист сможет подключать только ilog.h. Он и не знает о simplelog.h (доступ к этому классу скрыт).


UPD Для более ясной картины, я добавил еще один пример, реализующий этот принцип, описанный ниже.

mediasettings.h

class MediaSettings 
{
public:

    MediaSettings()          = default;
    virtual ~MediaSettings() = default;
    virtual bool isSupported() noexcept = 0;

    static MediaSettings* createAudioSettings() noexcept;
    static MediaSettings* createVideoSettings() noexcept;
};

audiosettings.h

class AudioSettings : public MediaSettings
{
public:

    AudioSettings();
    virtual ~AudioSettings() = default;
    bool isSupported() noexcept override;
};

audiosettings.cpp

AudioSettings::AudioSettings() : MediaSettings()
{    
}

bool AudioSettings::isSupported() noexcept
{
    // ...
}

MediaSettings* MediaSettings::createAudioSettings() noexcept
{
    return new AudioSettings;
}

videosettings.h

class VideoSettings : public MediaSettings
{
public:

    VideoSettings();
    virtual ~VideoSettings() = default;
    bool isSupported() noexcept override;
};

videosettings.cpp

VideoSettings::VideoSettings() : MediaSettings()
{    
}

bool VideoSettings::isSupported() noexcept
{
    // ...
}

MediaSettings* MediaSettings::createVideoSettings() noexcept
{
    return new VideoSettings;
}

main.cpp

int main()
{
    MediaSettings* audioSettings = MediaSettings::createAudioSettings();
    MediaSettings* videoSettings = MediaSettings::createVideoSettings();
    
    if (audioSettings->isSupported() && 
        videoSettings->isSupported()) {
        // ...
    }
    
    delete audioSettings;
    delete videoSettings;
}

Ответы (2 шт):

Автор решения: Stanislav Volodarskiy

Это шаблон проектирования фабричный метод.

  1. Позволяет создавать экземпляры неизвестного типа, при условии что тип - наследник интерфейса.
  2. Скрывает реализацию от пользователя. Без этого шаблона пользователь имеет информацию о структуре и размере экземпляров класса - обычно лишнюю.

Из второго пункта есть следствия: пользователи класса не зависят от деталей его реализации - их не нужно перекомпилировать при изменении класса, только при изменении интерфейса. Код пользователей перекомпилируется реже и компилируется быстрее.

→ Ссылка
Автор решения: vp_arth

Это просто вынос фабричного метода ILog::createInstance в единицу трансляции связанную с реализацией интерфейса.

Плюс в том, что можно переключать реализацию просто заменив объектный файл на другой при линковке.

Минус в том, что невозможно в одной программе использовать более одной реализации интерфейса, возникнет конфликт.


Более каноничная реализация фабричного метода предполагает параллельную иерархию конструирующих классов с общим абстрактным предком.
Предельно упрощённо:

class ILog {public: virtual void log() = 0; virtual ~ILog()=default;};
class ILogCreator {public: virtual ILog* create() = 0; virtual ~ILogCreator()=default;};

class LogA: public ILog {public: void log();};
void LogA::log() { std::cout << "A::log()\n";}

class LogACreator: public ILogCreator {public: ILog* create();};
ILog* LogACreator::create() { return new LogA();}

int main()
{
    ILogCreator* logCreator = new LogACreator();
    ILog* log = logCreator->create();
    log->log();

    delete log;
    delete logCreator;
}

Программист может не знать о деталях порождаемого класса, только о порождающем.

→ Ссылка