柚子快報(bào)邀請(qǐng)碼778899分享:設(shè)計(jì)模式 —— 裝飾器模式
柚子快報(bào)邀請(qǐng)碼778899分享:設(shè)計(jì)模式 —— 裝飾器模式
設(shè)計(jì)模式 —— 裝飾器模式
什么是裝飾器模式通俗解釋
Component(組件接口)ConcreteComponent(具體組件)Decorator(裝飾器接口)ConcreteDecorator(具體裝飾器)裝飾器模式優(yōu)缺點(diǎn)優(yōu)點(diǎn)缺點(diǎn)
今天我們來(lái)看一個(gè)新的設(shè)計(jì)模式——裝飾器模式。
什么是裝飾器模式
裝飾器模式(Decorator Pattern)是一種設(shè)計(jì)模式,屬于結(jié)構(gòu)型模式的范疇。它的主要目的是動(dòng)態(tài)地給一個(gè)對(duì)象添加額外的職責(zé)(功能),而不需要修改該對(duì)象的結(jié)構(gòu)。裝飾器模式通過(guò)創(chuàng)建包裝對(duì)象來(lái)包裹原始對(duì)象,這些包裝對(duì)象提供了與原始對(duì)象相同的接口,但可以在調(diào)用前后添加新的處理或責(zé)任。
裝飾器模式的組成部分包括:
Component(組件接口):定義了所有對(duì)象共有的操作接口,無(wú)論是裝飾器還是被裝飾的對(duì)象都要實(shí)現(xiàn)這個(gè)接口。ConcreteComponent(具體組件):實(shí)現(xiàn)了Component接口的具體對(duì)象,也就是被裝飾的原始對(duì)象。Decorator(裝飾器抽象類):持有Component的引用,定義了與Component接口一致的抽象操作,這樣裝飾器就可以替代被裝飾對(duì)象。同時(shí),裝飾器抽象類提供了一個(gè)構(gòu)造方法,用于傳入被裝飾的對(duì)象。ConcreteDecorator(具體裝飾器):實(shí)現(xiàn)了裝飾器抽象類,負(fù)責(zé)具體的裝飾邏輯,可以添加額外的職責(zé)或行為。
裝飾器模式的優(yōu)勢(shì):
靈活性:可以在運(yùn)行時(shí)動(dòng)態(tài)地添加或移除對(duì)象的功能,而無(wú)需修改對(duì)象的代碼。遵循開(kāi)放封閉原則:對(duì)擴(kuò)展開(kāi)放(可以輕松添加新的裝飾器),對(duì)修改封閉(不需要改變現(xiàn)有的組件代碼)。重用性:裝飾器可以復(fù)用,而且可以組合使用,通過(guò)不同的裝飾器組合來(lái)創(chuàng)造復(fù)雜的對(duì)象行為。替代繼承:相比多層繼承,裝飾器模式提供了更靈活的替代方案來(lái)擴(kuò)展功能,減少了類的復(fù)雜度。
使用場(chǎng)景:
需要?jiǎng)討B(tài)、透明地給對(duì)象增加職責(zé)時(shí)。當(dāng)不能采用繼承達(dá)到擴(kuò)展目的,或者采用繼承會(huì)導(dǎo)致類爆炸時(shí)(繼承層次過(guò)深或過(guò)多)。當(dāng)需要為對(duì)象添加職責(zé),但又不想硬編碼到對(duì)象中時(shí),以保持對(duì)象的可復(fù)用性。
通俗解釋
想象一下你去一家咖啡店買咖啡?;A(chǔ)的咖啡就像是一杯簡(jiǎn)單的美式,沒(méi)有任何額外添加。裝飾器模式就像是在咖啡店中添加調(diào)料的過(guò)程。
基礎(chǔ)咖啡就像是裝飾器模式中的“被裝飾的對(duì)象”,就是那個(gè)最原始、最基本的東西,比如一杯純咖啡。
裝飾器則是那些可以往咖啡里加的東西,比如牛奶、糖、巧克力粉等。每個(gè)裝飾器都圍繞著“咖啡”這個(gè)核心,你可以選擇添加一個(gè)或多個(gè)裝飾器來(lái)定制你的咖啡。
在程序里,如果我們要給一個(gè)功能(比如基礎(chǔ)的“打印日志”功能)增加新能力,比如添加時(shí)間戳、或者改變字體顏色,而不改變?cè)镜摹按蛴∪罩尽贝a,就可以用裝飾器模式。就像是給咖啡加糖不改變咖啡本質(zhì),但讓咖啡變甜了一樣。
所以,裝飾器模式就是一種設(shè)計(jì)方法,它允許我們?cè)诓恍薷脑紝?duì)象的基礎(chǔ)上,通過(guò)添加一層層“裝飾”(附加功能)來(lái)擴(kuò)展對(duì)象的功能,就像你點(diǎn)咖啡時(shí)一層層添加各種調(diào)料,卻還是那杯咖啡,只是功能(味道)更豐富了。
Component(組件接口)
我們這里以咖啡舉例,首先我們要完成一個(gè)基礎(chǔ)的接口:
class CoffeeBase
{
public:
// 聲明為純虛函數(shù),要求任何繼承此類的子類必須實(shí)現(xiàn)該函數(shù)
// 獲取咖啡的描述信息,如名稱、口味等
virtual std::string getDescription() const = 0;
// 聲明為純虛函數(shù),要求任何繼承此類的子類必須實(shí)現(xiàn)該函數(shù)
// 返回咖啡的成本價(jià)格
virtual double getCost() const = 0;
// 虛析構(gòu)函數(shù)的聲明,確保通過(guò)基類指針或引用來(lái)刪除派生類對(duì)象時(shí),
// 能夠正確調(diào)用到派生類的析構(gòu)函數(shù),防止資源泄露
virtual ~CoffeeBase();
};
// 虛析構(gòu)函數(shù)的定義,這里是一個(gè)空的實(shí)現(xiàn)
// 對(duì)于基類CoffeeBase來(lái)說(shuō),可能不需要在析構(gòu)函數(shù)中執(zhí)行具體操作,
// 但定義它是必要的,尤其是在有多態(tài)使用場(chǎng)景下,確保析構(gòu)鏈的完整
CoffeeBase::~CoffeeBase() {}
ConcreteComponent(具體組件)
我們提供一個(gè)具體組件,SimpleCoffee:
// 簡(jiǎn)單coffee類,繼承自CoffeeBase基類
class SimpleCoffee : public CoffeeBase
{
public:
// 重寫從基類CoffeeBase繼承的getDescription函數(shù)
// 此函數(shù)返回一個(gè)描述簡(jiǎn)單咖啡的字符串,這里是"This is a simple coffee"
std::string getDescription() const override
{
return "This is a simple coffee";
}
// 重寫從基類CoffeeBase繼承的getCost函數(shù)
// 此函數(shù)返回簡(jiǎn)單咖啡的成本價(jià)格,設(shè)定為5.0元
double getCost() const override
{
return 5.0;
}
};
Decorator(裝飾器接口)
我們還要實(shí)現(xiàn)一個(gè)裝飾器接口:
// 裝飾器基類,繼承自CoffeeBase,用于給咖啡添加額外功能或裝飾
class CoffeeDecorator : public CoffeeBase
{
protected:
// 持有一個(gè)指向CoffeeBase的指針,用于存儲(chǔ)被裝飾的咖啡對(duì)象
CoffeeBase* baseDecorator;
public:
// 構(gòu)造函數(shù),接收一個(gè)CoffeeBase類型的指針作為參數(shù)
// 這個(gè)參數(shù)實(shí)際上是被裝飾的基礎(chǔ)咖啡實(shí)例
CoffeeDecorator(CoffeeBase* coffee)
: baseDecorator(coffee) // 初始化baseDecorator指向傳入的coffee
{
// 構(gòu)造函數(shù)體為空,初始化已經(jīng)在成員初始化列表中完成
}
// 重寫getDescription函數(shù),委托給被裝飾的CoffeeBase對(duì)象來(lái)獲取描述
// 這樣可以保證裝飾后的咖啡描述首先展示的是原始咖啡的信息
std::string getDescription() const override
{
return baseDecorator->getDescription();
}
// 重寫getCost函數(shù),同樣委托給被裝飾的CoffeeBase對(duì)象來(lái)計(jì)算成本
// 使得裝飾過(guò)程不影響原始咖啡成本的計(jì)算,裝飾器類可在此基礎(chǔ)上累加額外成本
double getCost() const override
{
return baseDecorator->getCost();
}
};
ConcreteDecorator(具體裝飾器)
具體裝飾器實(shí)現(xiàn)具體的功能:
// 實(shí)現(xiàn)加牛奶的裝飾器,繼承自CoffeeDecorator
class MilkAdd : public CoffeeDecorator
{
public:
// 構(gòu)造函數(shù),接收一個(gè)CoffeeBase類型的指針,用于裝飾的原始咖啡
MilkAdd(CoffeeBase* coffee)
: CoffeeDecorator(coffee) // 通過(guò)基類構(gòu)造函數(shù)初始化基類部分,傳入被裝飾的咖啡實(shí)例
{
// 構(gòu)造函數(shù)體為空,因?yàn)槌跏蓟呀?jīng)在成員初始化列表中完成
}
// 重寫getDescription函數(shù),在原有咖啡描述后添加" Milk"
// 表示這杯咖啡添加了牛奶
std::string getDescription() const override
{
return baseDecorator->getDescription() + " Milk";
}
// 重寫getCost函數(shù),在原有咖啡成本基礎(chǔ)上增加3元
// 代表添加牛奶的服務(wù)費(fèi)用
double getCost() const override
{
return baseDecorator->getCost() + 3;
}
};
// 實(shí)現(xiàn)加糖的裝飾器,同樣繼承自CoffeeDecorator
class SugarAdd : public CoffeeDecorator
{
public:
// 構(gòu)造函數(shù),接收一個(gè)CoffeeBase類型的指針,即要裝飾的咖啡
SugarAdd(CoffeeBase* coffee)
: CoffeeDecorator(coffee) // 通過(guò)基類構(gòu)造函數(shù)初始化,傳入被裝飾的咖啡實(shí)例
{
// 構(gòu)造函數(shù)體為空
}
// 重寫getDescription函數(shù),在原有咖啡描述后追加" Sugar"
// 表明這杯咖啡添加了糖
std::string getDescription() const override
{
return baseDecorator->getDescription() + " Sugar";
}
// 重寫getCost函數(shù),在原有咖啡成本上增加2元
// 代表加糖的額外費(fèi)用
double getCost() const override
{
return baseDecorator->getCost() + 2;
}
};
我們可以一一對(duì)應(yīng):
這段代碼展示了裝飾器模式的各個(gè)組成部分,具體如下:
Component(組件接口):在這個(gè)例子中,CoffeeBase類扮演了組件接口的角色。它定義了一個(gè)咖啡對(duì)象的基本操作接口,包括getDescription()用于獲取咖啡的描述和getCost()用于獲取成本價(jià)格,還有一個(gè)虛析構(gòu)函數(shù)確保通過(guò)基類指針可以安全地刪除派生類對(duì)象。ConcreteComponent(具體組件):SimpleCoffee類是CoffeeBase的子類,代表了具體組件。它實(shí)現(xiàn)了getDescription()和getCost(),提供了基礎(chǔ)咖啡的描述和成本。Decorator(裝飾器接口):雖然在這個(gè)實(shí)現(xiàn)中沒(méi)有顯式定義一個(gè)獨(dú)立的裝飾器接口類,但CoffeeDecorator類實(shí)際上承擔(dān)了裝飾器接口的角色。它繼承自CoffeeBase,并持有一個(gè)CoffeeBase* baseDecorator指針,通過(guò)這個(gè)指針調(diào)用基礎(chǔ)咖啡的操作,同時(shí)定義了與CoffeeBase相同的接口方法,允許裝飾器可以像咖啡一樣被操作。ConcreteDecorator(具體裝飾器):MilkAdd和SugarAdd類是具體的裝飾器類,它們繼承自CoffeeDecorator。每個(gè)裝飾器類都在其getDescription()和getCost()方法中添加了額外的功能(例如價(jià)格和描述中的" Milk"或" Sugar"),同時(shí)調(diào)用基類的相應(yīng)方法以保持原有的功能,體現(xiàn)了裝飾器模式的“疊加”特性。
這是整體的代碼:
// 使用#pragma once指令防止頭文件被多次包含
#pragma once
// 引入iostream和string頭文件,以便進(jìn)行輸入輸出和字符串操作
#include
#include
// 定義咖啡基礎(chǔ)接口類,所有的咖啡類型都需要實(shí)現(xiàn)這個(gè)接口
class CoffeeBase
{
public:
// 純虛函數(shù),獲取咖啡的描述信息,子類必須實(shí)現(xiàn)
virtual std::string getDescription() const = 0;
// 純虛函數(shù),獲取咖啡的成本價(jià)格,子類必須實(shí)現(xiàn)
virtual double getCost() const = 0;
// 虛析構(gòu)函數(shù),確保通過(guò)基類指針可以正確刪除派生類對(duì)象
virtual ~CoffeeBase();
};
// 基類CoffeeBase的析構(gòu)函數(shù)定義
CoffeeBase::~CoffeeBase() {}
// 簡(jiǎn)單咖啡類,實(shí)現(xiàn)CoffeeBase接口,代表不加任何調(diào)料的原始咖啡
class SimpleCoffee : public CoffeeBase
{
public:
// 重寫getDescription函數(shù),返回簡(jiǎn)單咖啡的描述信息
std::string getDescription() const override
{
return "This is a simple coffee";
}
// 重寫getCost函數(shù),返回簡(jiǎn)單咖啡的成本價(jià)格
double getCost() const override
{
return 5.0;
}
};
// 裝飾器基類,繼承自CoffeeBase,用于給咖啡添加額外屬性或裝飾
class CoffeeDecorator : public CoffeeBase
{
protected:
// 持有一個(gè)指向CoffeeBase的指針,用于存儲(chǔ)被裝飾的咖啡實(shí)例
CoffeeBase* baseDecorator;
public:
// 構(gòu)造函數(shù),接受一個(gè)CoffeeBase指針,初始化被裝飾的咖啡對(duì)象
CoffeeDecorator(CoffeeBase* coffee)
:baseDecorator(coffee)
{}
// 重寫getDescription函數(shù),調(diào)用被裝飾咖啡的getDescription
std::string getDescription() const override
{
return baseDecorator->getDescription();
}
// 重寫getCost函數(shù),調(diào)用被裝飾咖啡的getCost
double getCost() const override
{
return baseDecorator->getCost();
}
};
// 牛奶裝飾器類,為咖啡添加牛奶
class MilkAdd : public CoffeeDecorator
{
public:
// 構(gòu)造函數(shù),接受一個(gè)CoffeeBase指針,用于裝飾的咖啡
MilkAdd(CoffeeBase* coffee)
:CoffeeDecorator(coffee)
{}
// 重寫getDescription,在原有描述后添加" Milk"
std::string getDescription() const override
{
return baseDecorator->getDescription() + " Milk";
}
// 重寫getCost,在原價(jià)基礎(chǔ)上增加3元(牛奶費(fèi)用)
double getCost() const override
{
return baseDecorator->getCost() + 3;
}
};
// 糖裝飾器類,為咖啡添加糖
class SugarAdd : public CoffeeDecorator
{
public:
// 構(gòu)造函數(shù),接受一個(gè)CoffeeBase指針,用于裝飾的咖啡
SugarAdd(CoffeeBase* coffee)
: CoffeeDecorator(coffee)
{}
// 重寫getDescription,在原有描述后添加" Sugar"
std::string getDescription() const override
{
return baseDecorator->getDescription() + " Sugar";
}
// 重寫getCost,在原價(jià)基礎(chǔ)上增加2元(糖費(fèi)用)
double getCost() const override
{
return baseDecorator->getCost() + 2;
}
};
裝飾器模式優(yōu)缺點(diǎn)
裝飾器模式(Decorator Pattern)作為一種常用的設(shè)計(jì)模式,有其獨(dú)特的優(yōu)點(diǎn)和缺點(diǎn),下面是其主要特點(diǎn)概述:
優(yōu)點(diǎn)
靈活性和可擴(kuò)展性:裝飾器模式允許在不修改原有類代碼的情況下動(dòng)態(tài)地?cái)U(kuò)展對(duì)象功能,為對(duì)象添加新的職責(zé)。這使得系統(tǒng)易于擴(kuò)展,遵循了開(kāi)閉原則。組合模式的替代:相比于通過(guò)繼承來(lái)擴(kuò)展功能,裝飾器模式提供了更靈活的選擇,可以避免類層次過(guò)深的問(wèn)題,因?yàn)檠b飾器可以按需疊加,每個(gè)裝飾器只關(guān)注添加單一功能。透明性:對(duì)于客戶端而言,裝飾過(guò)的對(duì)象和未裝飾的對(duì)象使用方式相同,因?yàn)樗鼈兌甲裱嗤慕涌?。這使得客戶端代碼可以不關(guān)心對(duì)象是否被裝飾以及如何被裝飾,提高了代碼的可讀性和可維護(hù)性。重用性:裝飾器類可以被多次使用,也可以用來(lái)裝飾不同的對(duì)象,增加了代碼的復(fù)用性。
缺點(diǎn)
過(guò)度使用導(dǎo)致復(fù)雜性:如果過(guò)度使用裝飾器,可能會(huì)導(dǎo)致系統(tǒng)中有很多細(xì)小的裝飾器類,這會(huì)使得類的結(jié)構(gòu)變得復(fù)雜,難以理解與維護(hù)。調(diào)試?yán)щy:由于裝飾器層層嵌套,當(dāng)出現(xiàn)錯(cuò)誤時(shí),調(diào)試和定位問(wèn)題可能變得較為復(fù)雜,特別是當(dāng)裝飾器鏈很長(zhǎng)時(shí)。性能考量:每添加一個(gè)裝飾器,就增加了一層間接調(diào)用,極端情況下可能會(huì)影響執(zhí)行效率,盡管在大多數(shù)現(xiàn)代系統(tǒng)中這通常不是主要問(wèn)題。不易理解:對(duì)于不熟悉裝飾器模式的新開(kāi)發(fā)者來(lái)說(shuō),理解裝飾器如何運(yùn)作以及如何正確使用它們可能需要時(shí)間。
總的來(lái)說(shuō),裝飾器模式在需要?jiǎng)討B(tài)、靈活地?cái)U(kuò)展對(duì)象功能的場(chǎng)景下非常有用,但需要權(quán)衡其帶來(lái)的潛在復(fù)雜性與維護(hù)成本。合理使用可以極大提高軟件的可維護(hù)性和靈活性。
柚子快報(bào)邀請(qǐng)碼778899分享:設(shè)計(jì)模式 —— 裝飾器模式
精彩內(nèi)容
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場(chǎng)。
轉(zhuǎn)載請(qǐng)注明,如有侵權(quán),聯(lián)系刪除。