1. 程式人生 > >Decorator裝飾器模式(C++)

Decorator裝飾器模式(C++)

簡而言之,它提供了一種對被裝飾者透明的方法;

例如:一篇文章本身無需知道自己的頁首和頁尾;使用者可以很方便的新增不同的頁首與頁尾 對比Strategy模式:物件需要知道使用的是哪個演算法,該方式對元件不可見,但是呼叫者可以任意數量新增裝飾。

不足:每次裝飾都會引入一個新的小物件,即使每次生成的元件類似,仍每次都新建立裝飾器,會佔用許多額外的儲存空間。

如圖,為具體的組建新增DecA與DecB裝飾

實戰:實現一個為卷子新增頁首、頁尾的簡單程式

效果:

header 2015 is added.
header 'Final Test' is added.
Final test starts!
footer 1 is added.

 關鍵語句:

WhitePaper* wp = new FooterDecorator(new HeaderDecorator(new TestPaper, "'Final Test'"),"2015"));

其中WhitePaper為整個裝飾模式中的父類 ,TestPaper為例項物件,使用Footer和Header來新增裝飾

#include <cstdio>
//#include<crtdbg.h>
using namespace std;

/*Decorator模式: BaseClass=WhitePaper*/

class WhitePaper {
public:
	virtual ~WhitePaper() {};
	virtual void PrintContents();
};
void WhitePaper::PrintContents() {
	printf("This is a white paper.\n");
}

/*各種裝飾器的共同父類*/
class Decorator : public WhitePaper {
public:
	Decorator(WhitePaper *);
	virtual ~Decorator() { delete _wPaper; }; //!!!!!!!!!!!!
	virtual void PrintContents();
private:
	WhitePaper *_wPaper;
};
Decorator::Decorator(WhitePaper* pw) {
	_wPaper = pw;
	printf("Decorator constructed.\n");
}

void Decorator::PrintContents() {
	_wPaper->PrintContents();
}
/*頁首裝飾器*/
class HeaderDecorator : public Decorator {
public:
	HeaderDecorator(WhitePaper*, const char* str);
	~HeaderDecorator() {};
	virtual void PrintContents();
private:
	const char* s;
};
HeaderDecorator::HeaderDecorator(WhitePaper* pw, const char* str):Decorator(pw){
	printf("Header %s constructed.\n", str);
	s = str;
}

void HeaderDecorator::PrintContents() {
	printf("header %s is added.\n", this->s);
	Decorator::PrintContents();
}
/*頁尾裝飾器*/
class FooterDecorator : public Decorator {
public:
	FooterDecorator(WhitePaper*, int n);
	~FooterDecorator() {};
	virtual void PrintContents();
private:
	int _n;
};
FooterDecorator::FooterDecorator(WhitePaper* pw, int n) :Decorator(pw) {
	printf("Foot constructed.\n");
	_n = n;
}
void FooterDecorator::PrintContents() {
	Decorator::PrintContents();
	printf("footer %d is added.\n", this->_n);
}

/*實體類*/
class TestPaper : public WhitePaper{
public:
	void PrintContents();
};
void TestPaper::PrintContents() {
	printf("Final test starts!\n");
}

int main() {
	WhitePaper* wp = new TestPaper;
	wp = new FooterDecorator(new HeaderDecorator(new HeaderDecorator(wp, "'Final Test'"), "2015"),1);
	wp->PrintContents();
	delete wp;
	getchar();
	//_CrtDumpMemoryLeaks(); //檢測記憶體溢位
	return 0;
}

附:與堆記憶體有關的雜談

使用父類指標釋放子類物件時,父類的解構函式一定要是virtual的!!

如圖:

黑點為基類指標,V-Table表示類的虛擬函式表。若未宣告為virtual,則DecB的綠色部分和黑色部分被釋放,子類成員記憶體洩漏。宣告為virtual後解構函式被覆蓋,這樣呼叫的才是正確的版本。

從上面的記憶體情況同時可以看出,如果對pBase不作處理,則第一個實體和第二個實體仍然會陰魂不散,因為我們丟失了所有訪問它們的渠道。故Decorator的解構函式中對pBase指向的內容一定不能忘記呼叫delete。