Как называется принцип, реализованный в приведенном коде?
Не могу найти, как называется принцип, реализованный в приведенном ниже коде:
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 шт):
Это шаблон проектирования фабричный метод.
- Позволяет создавать экземпляры неизвестного типа, при условии что тип - наследник интерфейса.
- Скрывает реализацию от пользователя. Без этого шаблона пользователь имеет информацию о структуре и размере экземпляров класса - обычно лишнюю.
Из второго пункта есть следствия: пользователи класса не зависят от деталей его реализации - их не нужно перекомпилировать при изменении класса, только при изменении интерфейса. Код пользователей перекомпилируется реже и компилируется быстрее.
Это просто вынос фабричного метода 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;
}
Программист может не знать о деталях порождаемого класса, только о порождающем.