Composite
In software development the composite pattern is a tree structure like object which combines objects with similar functionality. Often programmers need to manipulate inheriting objects in a similar way thus they can use the composite pattern.
Contents
Composite Design Pattern
When to use:
If differentiation does not exist and objects can be treated in uniform composition.
Advantages:
- Results in code with maximum flexibility.
- Allows a uniform treatment of all objects.
Disadvantages:
- Less compact model.
- Might result in more overhead than when using properties.
- Some methods might not make sense for a leaf class.
Related patterns include:
- Decorator
- Flyweight
- Iterator
- Visitor
UML Diagram
Here is a UML representation of the composite pattern.
Source of the image: http://www.exciton.cs.rice.edu/JavaResources/DesignPatterns/composite.htm
Code Samples
These code samples will demonstrate the user of the composite pattern in real world applications.
Notepad++ - C++
These codes are taken for the window.h and Notepad_plus.h file of the Notepad++ which is distributed under the GNU GNU General Public License agreement and handle various window related task. The leaf must impliment functions such as display and reSizeTo.
//this file is part of notepad++ //Copyright (C)2003 Don HO ( donho@altern.org ) // //This program is free software; you can redistribute it and/or //modify it under the terms of the GNU General Public License //as published by the Free Software Foundation; either //version 2 of the License, or (at your option) any later version. // //This program is distributed in the hope that it will be useful, //but WITHOUT ANY WARRANTY; without even the implied warranty of //MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //GNU General Public License for more details. // //You should have received a copy of the GNU General Public License //along with this program; if not, write to the Free Software //Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #ifndef WINDOW_CONTROL_H #define WINDOW_CONTROL_H #include <windows.h> class Window { public: Window(): _hInst(NULL), _hParent(NULL), _hSelf(NULL){}; virtual ~Window() {}; virtual void init(HINSTANCE hInst, HWND parent) { _hInst = hInst; _hParent = parent; } virtual void destroy() = 0; virtual void display(bool toShow = true) const { ::ShowWindow(_hSelf, toShow?SW_SHOW:SW_HIDE); }; virtual void reSizeTo(RECT & rc) // should NEVER be const !!! { ::MoveWindow(_hSelf, rc.left, rc.top, rc.right, rc.bottom, TRUE); redraw(); }; virtual void reSizeToWH(RECT & rc) // should NEVER be const !!! { ::MoveWindow(_hSelf, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, TRUE); redraw(); }; virtual void redraw() const { ::InvalidateRect(_hSelf, NULL, TRUE); ::UpdateWindow(_hSelf); }; virtual void getClientRect(RECT & rc) const { ::GetClientRect(_hSelf, &rc); }; virtual void getWindowRect(RECT & rc) const { ::GetWindowRect(_hSelf, &rc); }; virtual int getWidth() const { RECT rc; ::GetClientRect(_hSelf, &rc); return (rc.right - rc.left); }; virtual int getHeight() const { RECT rc; ::GetClientRect(_hSelf, &rc); return (rc.bottom - rc.top); }; virtual bool isVisible() const { return (::IsWindowVisible(_hSelf)?true:false); }; HWND getHSelf() const { if (!_hSelf) { ::MessageBox(NULL, "_hSelf == NULL", "class Window", MB_OK); throw int(999); } return _hSelf; }; HWND getHParent() const { return _hParent; }; void getFocus() const { ::SetFocus(_hSelf); }; HINSTANCE getHinst() const { if (!_hInst) { ::MessageBox(NULL, "_hInst == NULL", "class Window", MB_OK); throw int(1999); } return _hInst; }; protected: HINSTANCE _hInst; HWND _hParent; HWND _hSelf; }; #endif //WINDOW_CONTROL_H
class Notepad_plus : public Window { public: Notepad_plus(); void init(HINSTANCE, HWND, const char *cmdLine); // ATTENTION : the order of the destruction is very important // because if the parent's window hadle is destroyed before // the destruction of its childrens' windows handle, // its childrens' windows handle will be destroyed automatically! virtual ~Notepad_plus(){ (NppParameters::getInstance())->destroyInstance(); if (_pTrayIco) delete _pTrayIco; }; void killAllChildren() { _toolBar.destroy(); _rebar.destroy(); if (_pMainSplitter) { _pMainSplitter->destroy(); delete _pMainSplitter; } _mainDocTab.destroy(); _subDocTab.destroy(); _mainEditView.destroy(); _subEditView.destroy(); _invisibleEditView.destroy(); _subSplitter.destroy(); _statusBar.destroy(); _scintillaCtrls4Plugins.destroy(); _dockingManager.destroy(); }; virtual void destroy() { ::DestroyWindow(_hSelf); }; static const char * getClassName() { return _className; }; void setTitleWith(const char *filePath); void getTaskListInfo(TaskListInfo *tli); // For filtering the modeless Dialog message bool isDlgsMsg(MSG *msg) const { for (size_t i = 0; i < _hModelessDlgs.size(); i++) { if (::IsDialogMessage(_hModelessDlgs[i], msg)) return true; } return false; }; bool doOpen(const char *fileName, bool isReadOnly = false); bool doSimpleOpen(const char *fileName); bool doReload(const char *fileName, bool alert = true); void saveScintillaParams(bool whichOne) { ScintillaViewParams svp; ScintillaEditView *pView = (whichOne == SCIV_PRIMARY)?&_mainEditView:&_subEditView; svp._lineNumberMarginShow = pView->hasMarginShowed(ScintillaEditView::_SC_MARGE_LINENUMBER); svp._bookMarkMarginShow = pView->hasMarginShowed(ScintillaEditView::_SC_MARGE_SYBOLE); svp._indentGuideLineShow = pView->isShownIndentGuide(); svp._folderStyle = pView->getFolderStyle(); svp._currentLineHilitingShow = pView->isCurrentLineHiLiting(); svp._wrapSymbolShow = pView->isWrapSymbolVisible(); svp._doWrap = pView->isWrap(); svp._edgeMode = int(pView->execute(SCI_GETEDGEMODE)); svp._edgeNbColumn = int(pView->execute(SCI_GETEDGECOLUMN)); svp._zoom = int(pView->execute(SCI_GETZOOM)); svp._whiteSpaceShow = pView->isInvisibleCharsShown(); svp._eolShow = pView->isEolVisible(); (NppParameters::getInstance())->writeScintillaParams(svp, whichOne); }; void saveGUIParams(){ NppGUI & nppGUI = (NppGUI &)(NppParameters::getInstance())->getNppGUI(); nppGUI._statusBarShow = _statusBar.isVisible(); nppGUI._toolBarStatus = _toolBar.getState(); nppGUI._tabStatus = (TabBarPlus::doDragNDropOrNot()?TAB_DRAWTOPBAR:0) | \ (TabBarPlus::drawTopBar()?TAB_DRAGNDROP:0) | \ (TabBarPlus::drawInactiveTab()?TAB_DRAWINACTIVETAB:0) | \ (_toReduceTabBar?TAB_REDUCE:0) | \ (TabBarPlus::drawTabCloseButton()?TAB_CLOSEBUTTON:0) | \ (TabBarPlus::isDbClk2Close()?TAB_DBCLK2CLOSE:0); nppGUI._splitterPos = _subSplitter.isVertical()?POS_VERTICAL:POS_HORIZOTAL; UserDefineDialog *udd = _pEditView->getUserDefineDlg(); bool b = udd->isDocked(); nppGUI._userDefineDlgStatus = (b?UDD_DOCKED:0) | (udd->isVisible()?UDD_SHOW:0); // Save the position WINDOWPLACEMENT posInfo; posInfo.length = sizeof(WINDOWPLACEMENT); ::GetWindowPlacement(_hSelf, &posInfo); nppGUI._appPos.left = posInfo.rcNormalPosition.left; nppGUI._appPos.top = posInfo.rcNormalPosition.top; nppGUI._appPos.right = posInfo.rcNormalPosition.right - posInfo.rcNormalPosition.left; nppGUI._appPos.bottom = posInfo.rcNormalPosition.bottom - posInfo.rcNormalPosition.top; nppGUI._isMaximized = (IsZoomed(_hSelf) || (posInfo.flags & WPF_RESTORETOMAXIMIZED)); saveDockingParams(); (NppParameters::getInstance())->writeGUIParams(); }; void saveDockingParams() { NppGUI & nppGUI = (NppGUI &)(NppParameters::getInstance())->getNppGUI(); // Save the docking information nppGUI._dockingData._leftWidth = _dockingManager.getDockedContSize(CONT_LEFT); nppGUI._dockingData._rightWidth = _dockingManager.getDockedContSize(CONT_RIGHT); nppGUI._dockingData._topHeight = _dockingManager.getDockedContSize(CONT_TOP); nppGUI._dockingData._bottomHight = _dockingManager.getDockedContSize(CONT_BOTTOM); // clear the conatainer tab information (active tab) nppGUI._dockingData._containerTabInfo.clear(); // create a vector to save the current information vector<PlugingDlgDockingInfo> vPluginDockInfo; vector<FloatingWindowInfo> vFloatingWindowInfo; // save every container vector<DockingCont*> vCont = _dockingManager.getContainerInfo(); for (size_t i = 0 ; i < vCont.size() ; i++) { // save at first the visible Tb's vector<tTbData *> vDataVis = vCont[i]->getDataOfVisTb(); for (size_t j = 0 ; j < vDataVis.size() ; j++) { if (vDataVis[j]->pszName && vDataVis[j]->pszName[0]) { PlugingDlgDockingInfo pddi(vDataVis[j]->pszModuleName, vDataVis[j]->dlgID, i, vDataVis[j]->iPrevCont, true); vPluginDockInfo.push_back(pddi); } } // save the hidden Tb's vector<tTbData *> vDataAll = vCont[i]->getDataOfAllTb(); for (size_t j = 0 ; j < vDataAll.size() ; j++) { if ((vDataAll[j]->pszName && vDataAll[j]->pszName[0]) && (!vCont[i]->isTbVis(vDataAll[j]))) { PlugingDlgDockingInfo pddi(vDataAll[j]->pszModuleName, vDataAll[j]->dlgID, i, vDataAll[j]->iPrevCont, false); vPluginDockInfo.push_back(pddi); } } // save the position, when container is a floated one if (i >= DOCKCONT_MAX) { RECT rc; vCont[i]->getWindowRect(rc); FloatingWindowInfo fwi(i, rc.left, rc.top, rc.right, rc.bottom); vFloatingWindowInfo.push_back(fwi); } // save the active tab ContainerTabInfo act(i, vCont[i]->getActiveTb()); nppGUI._dockingData._containerTabInfo.push_back(act); } // add the missing information and store it in nppGUI unsigned char floatContArray[50]; memset(floatContArray, 0, 50); for (size_t i = 0 ; i < nppGUI._dockingData._pluginDockInfo.size() ; i++) { BOOL isStored = FALSE; for (size_t j = 0; j < vPluginDockInfo.size(); j++) { if (nppGUI._dockingData._pluginDockInfo[i] == vPluginDockInfo[j]) { isStored = TRUE; break; } } if (isStored == FALSE) { int floatCont = 0; if (nppGUI._dockingData._pluginDockInfo[i]._currContainer >= DOCKCONT_MAX) floatCont = nppGUI._dockingData._pluginDockInfo[i]._currContainer; else floatCont = nppGUI._dockingData._pluginDockInfo[i]._prevContainer; if (floatContArray[floatCont] == 0) { RECT *pRc = nppGUI._dockingData.getFloatingRCFrom(floatCont); if (pRc) vFloatingWindowInfo.push_back(FloatingWindowInfo(floatCont, pRc->left, pRc->top, pRc->right, pRc->bottom)); floatContArray[floatCont] = 1; } vPluginDockInfo.push_back(nppGUI._dockingData._pluginDockInfo[i]); } }
Limewire - Java
This code is taken from Limwire project which is distributed under the GNU GNU General Public License agreement and is an open source file sharing software. The first file is taken form the Torrent.java which defines various functions required to handle torrent files. The leaf will then have to implement these functions.
package com.limegroup.bittorrent; interface Torrent { /** * States of a torrent. Some of them are functionally equivalent * to Downloader states. */ public enum TorrentState { WAITING_FOR_TRACKER, VERIFYING, CONNECTING, DOWNLOADING, SAVING, SEEDING, QUEUED, PAUSED, STOPPED, DISK_PROBLEM, TRACKER_FAILURE, SCRAPING, //scraping == requesting from tracker INVALID }; /** * @return true if the torrent is complete. */ public abstract boolean isComplete(); /** * Starts the torrent */ public abstract void start(); /** * Stops the torrent */ public abstract void stop(); public void measureBandwidth(); public float getMeasuredBandwidth(boolean downstream); public boolean isActive(); public TorrentState getState(); public long getNextTrackerRequestTime(); public int getNumConnections(); public int getTriedHostCount(); public int getNumPeers(); public int getNumNonInterestingPeers(); public int getNumChockingPeers(); public long getTotalDownloaded(); public long getAmountLost(); /** * Resumes the torrent. */ public abstract boolean resume(); /** * @return true if paused */ public boolean isPaused(); public boolean isPausable(); /** * Pauses the torrent. */ public abstract void pause(); }
package com.limegroup.bittorrent; import java.util.Collections; import java.util.List; public class FinishedTorrentDownload implements Torrent { private final long totalDownloaded, lost; private final TorrentState state; public FinishedTorrentDownload(Torrent active) { this.totalDownloaded = active.getTotalDownloaded(); this.lost = active.getAmountLost(); this.state = active.getState(); } public List<BTLink> getConnections() { return Collections.emptyList(); } public long getNextTrackerRequestTime() { return -1; } public int getNumNonInterestingPeers() { return 0; } public int getNumConnections() { return 0; } public int getNumPeers() { return 0; } public int getNumChockingPeers() { return 0; } public long getTotalDownloaded() { return totalDownloaded; } public boolean isPausable() { return false; } public boolean isPaused() { return false; } public void pause() { } public boolean resume() { return false; } public float getMeasuredBandwidth(boolean downstream) { return 0; } public TorrentState getState() { return state; } public boolean isActive() { return false; } public boolean isComplete() { return state == TorrentState.SEEDING; } public void measureBandwidth() {} public void start() {} public void stop() {} public long getAmountLost() { return lost; } public int getTriedHostCount() { return -1; } }