8. Идиомы CRTP, PIMPL. Примеры паттернов проектирования - Bridge, Command
Описание файла
PDF-файл из архива "8. Идиомы CRTP, PIMPL. Примеры паттернов проектирования - Bridge, Command", который расположен в категории "". Всё это находится в предмете "проектирование больших систем с++" из 11 семестр (3 семестр магистратуры), которые можно найти в файловом архиве МГУ им. Ломоносова. Не смотря на прямую связь этого архива с МГУ им. Ломоносова, его также можно найти и в других разделах. .
Просмотр PDF-файла онлайн
Текст из PDF
Проектирование больших систем наC++Коноводов В. А.кафедра математической кибернетики ВМКЛекция 827.10.2017Идиома CRTPThe curiously recurring template pattern. Класс отнаследован отшаблонного класса, в котором наследник – аргумент шаблона:template <typename Derived>class CuriousBase {// ...};class Curious : public CuriousBase<Curious> {// ..};Coplien, James O. (February 1995).Идиома CRTPThe curiously recurring template pattern. Класс отнаследован отшаблонного класса, в котором наследник – аргумент шаблона:template <typename Derived>class CuriousBase {// ...};class Curious : public CuriousBase<Curious> {// ..};Coplien, James O. (February 1995).Пример: enable_shared_from_this.Идиома CRTPОграничиваем число объектов класса.#include <stdexcept>template <typename T, size_t maxN>class LimitedInstances {static size_t counter;protected:LimitedInstances() {if (counter >= maxN) {throw std::logic_error("too many instances");}++counter;}~LimitedInstances() {--counter;}};template <typename T, size_t maxN>size_t LimitedInstances<T, maxN>::counter(0);Идиома CRTPclass oneInst: public LimitedInstances<oneInst, 1> {};class twoInst: public LimitedInstances<twoInst, 2> {};int main() {oneInst obj;try {oneInst();} catch (std::logic_error &e) {std::cerr << "Caught: " << e.what() << std::endl;}twoInst obj1;twoInst obj2;try {twoInst();} catch (std::logic_error &e) {std::cerr << "Caught: " << e.what() << std::endl;}};Идиома CRTPIЧасто заменяют динамический полиморфизм черезстатический: в базовом классе вызываем методы класса,которым он параметризован при инстанциированииИдиома PImplPointer to implementation.Метод, при котором члены-данные класса заменяютсяуказателем на класс реализации с этими данными.a.h:#include "myitems.h"class A {TMyItem item1, item2;public:A();// ...};Break compilation dependencies!a.h:class A {struct Impl;Impl *pImpl;public:A();// ...};a.cpp:#include "a.h"#include "myitems.h"struct A::Impl {TMyItem item1, item2;};A::A() : pImpl(new Impl) {}A::~A() {delete pImpl;}Идиома PImpl: C++11a.h:class A {struct Impl;std::unique_ptr<Impl> pImpl;public:A();// ...};a.cpp:#include "a.h"#include "myitems.h"struct A::Impl {TMyItem item1, item2;};A::A() : pImpl(std::make_unique<A::Impl>()) {}Идиома PImpl: C++11a.h:class A {struct Impl;std::unique_ptr<Impl> pImpl;public:A();// ...};a.cpp:#include "a.h"#include "myitems.h"struct A::Impl {TMyItem item1, item2;};A::A() : pImpl(std::make_unique<A::Impl>()) {}main.cpp:#include "a.h"A a; // !errorИдиома PImplНужно обеспечить полноту в точке уничтоженияstd::unique_ptr<A::Impl>.a.h:class A {struct Impl;std::unique_ptr<Impl> pImpl;public:A();~A();// ...};a.cpp:~A::A() = default;Идиома PImplНужны перемещающие функции:a.h:class A {struct Impl;std::unique_ptr<Impl> pImpl;public:A();~A();A(A&& other) = default;A& operator=(A&& other) = default;// ...};И снова та же проблема!Идиома PImplОбъявляем в заголовочном файле, реализуем в файле реализации:a.h:class A {struct Impl;std::unique_ptr<Impl> pImpl;public:A();~A();A(A&& other);A& operator=(A&& other);// ...};a.cpp:A::A(A&& other) = default;A& A::operator=(A&& other) = default;Идиома PImplПотребуются копирующие операции:a.h:class A {struct Impl;std::unique_ptr<Impl> pImpl;public:A();~A();A(A&& other);A& operator=(A&& other);A(const A& other);A& operator=(const A& other);// ...};Идиома PImpla.cpp:A::A(const A& other) : pImpl(nullptr) {if (other.pImpl) {pImpl = std::make_unique<Impl>(*other.Impl);}}A& A::operator=(const A& other) {if (!other.pImpl) {pImpl.reset();} else if (!pImpl) {pImpl = std::make_unique<Impl>(*other.Impl);} else {*pImpl = *other.pImpl;}return *this;}Идиома PImpla.cpp:A::A(const A& other) : pImpl(nullptr) {if (other.pImpl) {pImpl = std::make_unique<Impl>(*other.Impl);}}A& A::operator=(const A& other) {if (!other.pImpl) {pImpl.reset();} else if (!pImpl) {pImpl = std::make_unique<Impl>(*other.Impl);} else {*pImpl = *other.pImpl;}return *this;}В случае std::shared_ptr всё проще!Паттерны проектированияIСпособ построения кода для решения частовстречающихся проблем проектированияIsuccessful storiesIГотовые абстракции для решения классов проблем +унификация деталей и названийIНе нужно их употреблять везде, не нужно им строгоследоватьЭ.
Гамма, Р. Хелм, Р. Джонсон, Дж. Влиссидес. Приемыобъектно-ориентированного проектирования. Паттерныпроектирования.Паттерны проектированияIСпособ построения кода для решения частовстречающихся проблем проектированияIsuccessful storiesIГотовые абстракции для решения классов проблем +унификация деталей и названийIНе нужно их употреблять везде, не нужно им строгоследоватьЭ. Гамма, Р. Хелм, Р. Джонсон, Дж.
Влиссидес. Приемыобъектно-ориентированного проектирования. Паттерныпроектирования. [contains a lot of «ancient» C++ code]Пример: паттерн BridgeЦель: разделить абстракцию и реализацию на две отдельныеиерархии классов так, что их можно изменять независимо другот друга.Почему не наследование: наследование жестко привязываетреализацию к абстракции. Это затрудняет расширение иповторное использование абстракции и ее реализации.Пример: паттерн BridgeЦель: разделить абстракцию и реализацию на две отдельныеиерархии классов так, что их можно изменять независимо другот друга.Почему не наследование: наследование жестко привязываетреализацию к абстракции.
Это затрудняет расширение иповторное использование абстракции и ее реализации.Первая иерархия определяет интерфейс абстракции, доступныйпользователю. Основной класс содержит указатель нареализацию pimpl, который используется для перенаправленияпользовательских запросов в неё.Все детали реализации, связанные с какими-либоособенностями находятся во второй иерархии.Пример: паттерн BridgeAbstraction перенаправляет объекту Implementation запросыклиента.Пример: паттерн BridgeКогда: когда нужно часто изменять реализацию какого-нибудьметода с сохранением APIКогда: когда используется постоянно изменяющаяся внешняябиблиотекаКогда: когда нужно добиться разделения ответственностимежду классамиПример: паттерн BridgeВ чем отличие от PIMPL?IPIMPL — способ скрыть реализацию, в основном для того,чтобы убрать зависимостиIBridge — поддержка множественных реализаций, а вPIMPL обычно не изменяется реализация, это отдельнокомпилируемый классIPIMPL — идиома проектирования на уровне файлов скодом, Bridge — паттерн объектно-ориентированногопроектирования.Пример: паттерн CommandИнкапcулирует запрос в виде объекта, делая возможнойпараметризацию клиентских объектов с другими запросами,организацию очереди или регистрацию запросов, а такжеподдержку отмены операций.Пример: паттерн Commandstruct talk {void operator()(){std::cout << "croak!" << std::endl;}};struct walk {void operator()() {std::cout << "im walking" << std::endl;}};struct jump {void operator()() {std::cout << "jump" << std::endl;}};Пример: паттерн Commandvoid dofunc(std::function<void()> f) {f();}int main() {dofunc(talk{});dofunc(walk{});auto f = jump();dofunc(f);}Пример: паттерн Commandvoid dofunc(std::function<void()> f) {f();}int main() {dofunc(talk{});dofunc(walk{});auto f = jump();dofunc(f);}Если все действия пользователя в программе реализованы в видекомандных объектов, программа может сохранить стек последнихвыполненных команд.Пример: паттерн CommandClient — среда генерации комманд; Receiver — знает, какпровести операцию, связанную с командной; Command —инкапцуляция действия; Invoker — последующие действия скомандой или пулом команд.Пример: паттерн CommandIUndo-RedoIОрганизация очереди при многопоточной обработке;IТранзакционные алгоритмы - регистрация событий ивосстановление после сбоя;.