Composite

From CDOT Wiki
Revision as of 23:38, 14 February 2007 by Jsafavi- (talk | contribs)
Jump to: navigation, search

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.

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.

Composite Design 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++

This code is taken for the window.h file of the Notepad++ and handles 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 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;
	}
}