Design Patterns
Материал из Wiki.crossplatform.ru
Порождающие паттерны проектирования
Абстрактная фабрика (Abstract Factory, Factory), др. название Инструментарий (Kit)
#include <iostream> class CLanguage { public: virtual void generate() = 0; }; class CPytnon : public CLanguage { public: void generate() { std::cout << "Generate python code" << std::endl; } }; class CJava : public CLanguage { public: void generate() { std::cout << "Generate java code" << std::endl; } }; class CLangFactory { public: virtual CLanguage* createLanguage() = 0; }; class CPythonFactory : public CLangFactory { public: CLanguage* createLanguage(){ return new CPytnon(); } }; class CJavaFactory : public CLangFactory { public: CLanguage* createLanguage() { return new CJava(); } }; class CCodeGenerator { public: CCodeGenerator(CLangFactory* factory) { CLanguage *pLang = factory->createLanguage(); pLang->generate(); delete pLang; delete factory; } }; CLangFactory* createCodeFactory() { int nLang = -1; std::cout << "Enter Language type (0: Python, 1: Java): "; std::cin >> nLang; switch( nLang ) { case 0: return new CPythonFactory(); case 1: return new CJavaFactory(); default: std::cout << "Error choice language..." << std::endl; return 0; } } int main() { CLangFactory *plf = createCodeFactory(); if( plf ) CCodeGenerator cg( plf ); return 0; }
Одиночка (Singleton)
Статический
class CSingleton { private: static CSingleton m_singleton; private: CSingleton() {} ~CSingleton() {} CSingleton(const CSingleton &) {} CSingleton & operator=(const CSingleton &) { return *this; } public: static CSingleton *instance() { return &m_singleton; } }; CSingleton CSingleton::m_singleton; int main(int , char **) { CSingleton *p = CSingleton::instance(); // ... return 0; }
Динамический
class CSingleton { private: static CSingleton *m_pSingleton; static int m_nCounter; private: CSingleton() {} ~CSingleton() {} CSingleton(const CSingleton &) {} CSingleton & operator=(const CSingleton &) { return *this; } public: static CSingleton *instance() { if( m_nCounter == 0 ) { m_pSingleton = new CSingleton(); } m_nCounter++; return m_pSingleton; } static CSingleton *freeInstance() { if( m_nCounter > 0 ) { m_nCounter--; if( m_nCounter == 0 ) { delete m_pSingleton; m_pSingleton = 0; } } } }; CSingleton *CSingleton::m_pSingleton = 0; int CSingleton::m_nCounter = 0; int main(int , char **) { CSingleton *p = CSingleton::instance(); return 0; }
Шаблонный
class CClass { public: virtual ~CClass(){ } }; template <class T> class CTypedSingleton; template<class T> class CTypedWrapper : public T, private CTypedSingleton<T> { public: void operator delete(void *p) { CTypedSingleton<T>::free(); } }; template <class T> class CTypedSingleton { static T* m_self; static int m_refcount; protected: CTypedSingleton(){} CTypedSingleton(const CTypedSingleton&){} virtual ~CTypedSingleton(){ m_self = 0; } CTypedSingleton &operator=(const CTypedSingleton&){} public: static T *init() { if(!m_self) m_self = new CTypedWrapper<T>; m_refcount++; return m_self; } static void free() { if( m_refcount > 0) { --m_refcount; if( m_refcount == 0) { delete m_self; m_self = 0; } } } }; template <class T> T *CTypedSingleton<T>::m_self = 0; template <class T> int CTypedSingleton<T>::m_refcount = 0; int main(int , char **) { CClass *p = CTypedSingleton<CClass>::init(); delete p; return 0; }
Многопоточный (double-checked locking)
#include <cstdlib> #include <boost/thread/once.hpp> template <class T> class Singleton { public: static T& Instance(); private: Singleton() {}; Singleton(const Singleton&); Singleton& operator=(const Singleton&); virtual ~Singleton() {}; static void Init() { boost::call_once(m_init, &Singleton<T>::InitImpl); } static void InitImpl() { m_me = new T; atexit(&Singleton<T>::FreeInstance); } static void FreeInstance() { boost::call_once(m_done, &Singleton<T>::FreeImpl); }; static void FreeImpl() { delete m_me; m_me = NULL; } friend int atexit(void (__cdecl *func)(void)); private: static T* volatile m_me; static boost::once_flag m_init, m_done; }; template <class T> T* Singleton<T>::m_me = NULL; template <class T> boost::once_flag Singleton<T>::m_init = BOOST_ONCE_INIT; template <class T> boost::once_flag Singleton<T>::m_done = BOOST_ONCE_INIT; template <class T> T& Singleton<T>::Instance() { Init(); return (*m_me); }
Прототип (Prototype)
#include <iostream> // Прототип class CPrototype { public: virtual CPrototype* clone() const = 0; }; // Прототип сайта class CSitePrototype : public CPrototype { private: int m_nPages; public: CSitePrototype(int nPages) : m_nPages( nPages ){} CSitePrototype(const CSitePrototype &r) : m_nPages( r.m_nPages ){} virtual CSitePrototype* clone() const { return new CSitePrototype( *this ); } void setPages(int nPages) { m_nPages = nPages; } int getPages() const { return m_nPages; } void printPages() const { std::cout << "Pages: " << m_nPages << std::endl; } }; // Клиентская сторона void clientSide() { CPrototype *pPrototype = new CSitePrototype( 256 ); for (int n = 0; n < 10; n++) { CSitePrototype *pSite = static_cast<CSitePrototype*>( pPrototype->clone() ); pSite->setPages( pSite->getPages() * n ); pSite->printPages(); delete pSite; } delete pPrototype; pPrototype = 0; } int main() { clientSide(); return 0; }
Создатель экземпляров класса (Creator)
template <typename TItem> class CDataStorage { private: std::vector<TItem*> m_items; public: CDataStorage () {}; ~CDataStorage () { for (auto it = m_items.begin (); it != m_items.end(); ++it) delete *it; } std::size_t createItem () { std::size_t item_index = m_items.size (); m_items.push_back (new TItem()); return item_index; } std::size_t size () const { return m_items.size ();} TItem & itemAt (std::size_t index) { return *m_items[index];} const TItem & itemAt (std::size_t index) const { return *m_items[index];} }; int main(int argc, char* argv[]) { CDataStorage<int> data; data.createItem (); data.createItem (); int &item1 = data.itemAt (0); item1 = 42; std::cout << data.itemAt (0); }
Строитель (Builder)
#include <string> #include <iostream> class COs { private: int m_nType; int m_nCore; std::string m_szName; public: void setType( int nType ) { m_nType = nType; } void setCore( int nCore ) { m_nCore = nCore; } void setName( std::string szName ) { m_szName = szName; } void print() { std::cout << "Os type: " << m_nType << " core: " << m_nCore << " name: " << m_szName << std::endl; } }; class COsBuilder { protected: COs *m_pOs; public: void createNewOs() { m_pOs = new COs(); } COs *os() { return m_pOs; } virtual void setOsType() = 0; virtual void setOsCore() = 0; virtual void setOsName() = 0; }; class CLinuxBuilder : public COsBuilder { public: void setOsType() { m_pOs->setType( 0 ); } void setOsCore() { m_pOs->setCore( 11111 ); } void setOsName() { m_pOs->setName( "Red hat" ); } }; class CWindowsBuilder : public COsBuilder { public: void setOsType() { m_pOs->setType( 1 ); } void setOsCore() { m_pOs->setCore( 22222 ); } void setOsName() { m_pOs->setName( "Windows 8" ); } }; class CSysAdmin { private: COsBuilder *m_pBuilder; public: CSysAdmin(): m_pBuilder(0){} virtual ~CSysAdmin() { freeBuilder(); } void freeBuilder() { if( m_pBuilder ) { delete m_pBuilder; m_pBuilder = 0; } } void setBuilder(COsBuilder* pBuilder) { freeBuilder(); m_pBuilder = pBuilder; } void construct() { m_pBuilder->createNewOs(); m_pBuilder->setOsType(); m_pBuilder->setOsCore(); m_pBuilder->setOsName(); } void printOs(){ m_pBuilder->os()->print(); } }; int main() { CSysAdmin sys; sys.setBuilder(new CLinuxBuilder); sys.construct(); sys.printOs(); sys.setBuilder(new CWindowsBuilder); sys.construct(); sys.printOs(); }
Фабричный метод (Factory Method) или Виртуальный конструктор (Virtual Constructor)
#include <iostream> class CDocument { public: virtual void save() = 0; }; class CWord: public CDocument { public: void save() { std::cout << "Save word document." << std::endl; } }; class CExcel: public CDocument { public: void save() { std::cout << "Save excel document." << std::endl; } }; class CApplication { int m_nTypeDocument; public: CApplication( int nTypeDocument ):m_nTypeDocument(nTypeDocument){} void setTypeDocument(int nTypeDocument ){ m_nTypeDocument = nTypeDocument; } void saveFile() { CDocument *pDoc = getTypeDocument(); if( pDoc ) pDoc->save(); } CDocument *getTypeDocument() { switch( m_nTypeDocument ) { case 0: return new CWord(); case 1: return new CExcel(); } return 0; } }; int main() { CApplication app( 0 ); app.saveFile(); // Word app.setTypeDocument( 1 ); app.saveFile(); // Excel return 0; }
Структурные паттерны проектирования классов/обьектов
Адаптер (Adapter)
class CDisplay { public: void drawMessage (int x, int y, const std::string &message) { std::cout << "Display at " << x << " " << y << ": " << message << std::endl; } int width () { return 200; } int height () { return 100; } }; class CDisplayAdapter { private: CDisplay *m_pDisplay; public: CDisplayAdapter (CDisplay *display) { m_pDisplay = display;} void message (float x, float y, const std::string &message) { int display_x = x * m_pDisplay->width (); int display_y = y * m_pDisplay->height (); m_pDisplay->drawMessage (display_x, display_y, message); } }; int main(int argc, char* argv[]) { CDisplay *output = new CDisplay (); CDisplayAdapter display_adapter (output); display_adapter.message (0.5f, 0.5f, "I'm center!"); }
Декоратор (Decorator) или Оболочка (Wrapper)
Предназначен для динамического расширения функциональности объекта (добавления дополнительного поведения).
#include <iostream> class CWidget { public: virtual void draw() = 0; }; class CDialog : public CWidget { public: void draw(){ std::cout << "Draw CDialog" << std::endl; } }; class CToolBar : public CWidget { CWidget *m_pWrap; public: CToolBar( CWidget *pWrap ): m_pWrap(pWrap){} void draw(){ std::cout << "Draw CToolBar" << std::endl; m_pWrap->draw(); } }; class CStatusBar : public CWidget { CWidget *m_pWrap; public: CStatusBar( CWidget *pWrap ): m_pWrap(pWrap){} void draw(){ m_pWrap->draw(); std::cout << "Draw CStatusBar" << std::endl; } }; int main( int argc, char **argv) { CWidget *pDlg = new CDialog(); // Диалог без тулбара и статусбара pDlg->draw(); std::cout << std::endl; CWidget *pDlgT = new CToolBar( new CDialog() ); // Диалог c тулбаром и без статусбара pDlgT->draw(); std::cout << std::endl; CWidget *pDlgTS = new CToolBar( new CStatusBar( new CDialog() ) ); // Диалог c тулбаром и c статусбаром pDlgTS->draw(); return 0; }
где, CToolBar и CStatusBar являются декораторами.
Результат:
Draw CDialog Draw CToolBar Draw CDialog Draw CToolBar Draw CDialog Draw CStatusBar
Заместитель (Proxy) или Суррогат (Surrogate)
Контролирует доступ к другому объекту, перехватывая все вызовы.
#include <iostream> #include <string> class CImage { std::string m_szName; public: CImage(std::string szName):m_szName(szName){} void draw(){ std::cout << "Drawing image: " << m_szName << std::endl; } void printName()const { std::cout << "CImage::printName" << std::endl; } }; class CProxyImage { CImage *m_pImage; std::string m_szName; public: CProxyImage( std::string szName): m_pImage(0), m_szName(szName){} void draw() { if( !m_pImage ) m_pImage = new CImage(m_szName); m_pImage->draw(); } void printName()const { if( m_pImage ) { m_pImage->printName(); return; } std::cout << "CProxyImage::printName" << std::endl; } }; int main( int argc, char **argv) { CProxyImage image( "testImage.png" ); image.printName(); image.draw(); image.printName(); return 0; }
Результат:
CProxyImage::printName Drawing image: testImage.png CImage::printName
Информационный эксперт (Information Expert)
Компоновщик (Composite)
class CDrawable { public: CDrawable () {}; virtual ~CDrawable () {}; virtual void draw () = 0; }; class CCircle : public CDrawable { public: virtual void draw () { std::cout << "I'm a circle!" << std::endl;} }; class CRectangle : public CDrawable { public: virtual void draw () { std::cout << "I'm a rectangle!" << std::endl;} }; class CDrawableGroup : public CDrawable { private: std::list<CDrawable*> m_children; public: CDrawableGroup () {}; virtual ~CDrawableGroup () { while (!m_children.empty ()) { delete m_children.back (); m_children.pop_back (); }; }; void addChild (CDrawable *drawable) { m_children.push_back (drawable); }; void removeChild (CDrawable *drawable) { m_children.remove (drawable); delete drawable; }; virtual void draw () { for (auto it = m_children.begin (); it != m_children.end (); ++it) (*it)->draw (); }; }; int main(int argc, char* argv[]) { CDrawableGroup figures; figures.addChild (new CCircle ()); CDrawableGroup *rect_group = new CDrawableGroup (); rect_group->addChild (new CRectangle ()); rect_group->addChild (new CRectangle ()); figures.addChild (rect_group); figures.draw (); }
Мост (Bridge), Handle (описатель) или Тело (Body)
#include <iostream> class CWindowImp { public: virtual void drawWindow() = 0; }; class CLinuxWindow : public CWindowImp { void drawWindow() { std::cout << "Draw linux window" << std::endl; } }; class CWinWindow : public CWindowImp { void drawWindow() { std::cout << "Draw Windows window" << std::endl; } }; class CMacWindow : public CWindowImp { void drawWindow() { std::cout << "Draw Mac window" << std::endl; } }; class CWindowFactory { public: enum TYPES { LINUX = 0, WINDOWS, MAC }; // будем считать, что у нас фабрика синглтон и все нормально с освобождением памяти... :) static CWindowImp *getWindowImp( CWindowFactory::TYPES type ) { switch( type ) { case LINUX: return new CLinuxWindow(); break; case WINDOWS: return new CWinWindow(); break; case MAC: return new CMacWindow(); break; } return 0; } static CWindowImp *makeWindow() { // Будем считать, что мы узнали какая у нас ОС return getWindowImp(CWindowFactory::LINUX); } }; class CWindow { public: void draw(){ getWindowImp()->drawWindow(); } CWindowImp *getWindowImp(){ return CWindowFactory::makeWindow(); } }; int main() { CWindow win; win.draw(); }
Низкая связанность (Low Coupling)
Приспособленец (Flyweight)
Устойчивый к изменениям (Protected Variations)
Фасад (Facade)
Паттерны проектирования поведения классов/обьектов
Интерпретатор (Interpreter )
Итератор (Iterator) или Курсор (Cursor)
Команда (Command), Действие (Action) или Транзакция (Транзакция)
Наблюдатель (Observer), Опубликовать - подписаться (Publish - Subscribe) или Delegation Event Model
#include <iostream> #include <vector> #include <algorithm> class CObserver; class CModel { std::vector<CObserver*> m_views; int m_nValue; public: void attach(CObserver *po) { m_views.push_back( po ); } void detach(CObserver *po) { std::vector<CObserver*>::iterator iter = std::find( m_views.begin(), m_views.end(), po); if( iter != m_views.end() ) { m_views.erase( iter ); } } void setValue(int val) { m_nValue = val; notify(); } int getValue() { return m_nValue; } void notify(); }; class CObserver { CModel *m_pModel; public: CObserver(CModel *pMod) { m_pModel = pMod; m_pModel->attach(this); } virtual ~CObserver() { m_pModel.detach(this); } virtual void update() = 0; protected: CModel *getModel() { return m_pModel; } }; class CViewDiagram: public CObserver { public: CViewDiagram(CModel *pMod): CObserver(pMod){} void update() { std::cout << "CViewDiagram: " << getModel()->getValue() << std::endl; } }; class CViewDocument: public CObserver { public: CViewDocument(CModel *pMod): CObserver(pMod){} void update() { std::cout << "CViewDocument: " << getModel()->getValue() << std::endl; } }; void CModel::notify() { for (size_t i = 0; i < m_views.size(); i++) m_views[i]->update(); } int main() { CModel model; CViewDocument doc( &model ); CViewDiagram diagr( &model ); for( int n= 0; n < 10; ++n) { model.setValue( n ); if( n ==5 ) model.detach( &diagr ); } }
Вывод:
CViewDocument: 0 CViewDiagram: 0 CViewDocument: 1 CViewDiagram: 1 CViewDocument: 2 CViewDiagram: 2 CViewDocument: 3 CViewDiagram: 3 CViewDocument: 4 CViewDiagram: 4 CViewDocument: 5 CViewDiagram: 5 CViewDocument: 6 CViewDocument: 7 CViewDocument: 8 CViewDocument: 9
Не разговаривайте с неизвестными (Don't talk to strangers)
Посетитель (Visitor)
Посредник (Mediator)
Состояние (State)
#include <iostream> class CSocket; class CStateOpen; class CStateRead; class CStateClose; class CState { public: virtual ~CState() {} virtual void open(CSocket *s) { std::cout << "Already open socket" << std::endl; } virtual void read(CSocket *s) { std::cout << "Already read socket" << std::endl; } virtual void close(CSocket *s) { std::cout << "Already close socket" << std::endl; } }; class CSocket { CState *m_pCurrentState; public: CSocket(); void setCurrent(CState *p){ m_pCurrentState = p; } void open() { m_pCurrentState->open(this); } void read() { m_pCurrentState->read(this); } void close() { m_pCurrentState->close(this); } }; class CStateOpen: public CState { public: CStateOpen(){ std::cout << " CStateOpen" << std::endl; } ~CStateOpen(){ std::cout << " ~CStateOpen" << std::endl; } virtual void read(CSocket *s); virtual void close(CSocket *s); }; class CStateRead: public CState { public: CStateRead(){ std::cout << " CStateRead" << std::endl; } ~CStateRead(){ std::cout << " ~CStateRead" << std::endl; } virtual void open(CSocket *s) { std::cout << " Socket already open..." << std::endl; } virtual void close(CSocket *s); }; class CStateClose: public CState { public: CStateClose(){ std::cout << " CStateClose" << std::endl; } ~CStateClose(){ std::cout << " ~CStateClose" << std::endl; } virtual void open(CSocket *s) { std::cout << " Open closing socket" << std::endl; s->setCurrent( new CStateOpen()); } virtual void read(CSocket *s) { std::cout << " Error: Don't read closing socket" << std::endl; } }; CSocket::CSocket() { m_pCurrentState = new CStateClose(); } void CStateOpen::read(CSocket *s) { std::cout << " Read socket" << std::endl; s->setCurrent( new CStateRead()); } void CStateOpen::close(CSocket *s) { std::cout << " Close socket after opening" << std::endl; s->setCurrent( new CStateClose()); } void CStateRead::close(CSocket *s) { std::cout << " Close socket after reading" << std::endl; s->setCurrent( new CStateClose()); } int main() { void(CSocket::*ptrs[])() = { &CSocket::open, &CSocket::read, &CSocket::close }; CSocket sock; int num; while( 1 ) { std::cout << "Enter 0/2: "; std::cin >> num; (sock.*ptrs[num])(); } }
CStateClose Enter 0/2: 0 Open closing socket CStateOpen Enter 0/2: 1 Read socket CStateRead Enter 0/2: 2 Close socket after reading CStateClose Enter 0/2: 0 Open closing socket CStateOpen Enter 0/2: 2 Close socket after opening CStateClose Enter 0/2: 1 Error: Don't read closing socket
Стратегия (Strategy)
Хранитель (Memento)
Цепочка обязанностей (Chain of Responsibility)
Шаблонный метод (Template Method)
#include <iostream> class CApp { public: void openDocument() { //... doOpenDocument(); //... } protected: virtual void doOpenDocument() { std::cout << "Open simple document" << std::endl; } }; class CXmlApp: public CApp { protected: void doOpenDocument() { std::cout << "Open XML document" << std::endl; } }; class CTxtApp: public CApp { protected: void doOpenDocument() { std::cout << "Open TXT document" << std::endl; } }; int main() { CApp *pApps[] = { &CApp(), &CXmlApp(), &CTxtApp() }; for( int n = 0; n < 3; ++n ) pApps[n]->openDocument(); return 0; }
Вывод:
Open simple document Open XML document Open TXT document