網(wǎng)頁設(shè)計與制作的理解紹興seo公司
文章目錄
- 一、橋接模式
- 二、std::error_code與設(shè)計模式(橋接模式)
- 參考
一、橋接模式
在C++中,橋接模式通常涉及以下幾個角色:
- 抽象類接口(Abstraction):定義抽象部分的接口,并維護(hù)一個指向?qū)崿F(xiàn)部分的指針。
- 具體抽象類(ConcreteAbstraction):繼承自抽象類,實現(xiàn)抽象部分的接口。
- 實現(xiàn)類接口(Implementor):定義實現(xiàn)部分的接口。
- 具體實現(xiàn)類(ConcreteImplementor):實現(xiàn)實現(xiàn)類接口,并提供具體的實現(xiàn)。
以下是一些可能適合使用橋接模式的具體場景:
- 圖形界面工具包中的窗口和操作系統(tǒng)之間的連接,使得可以在不同的操作系統(tǒng)上使用相同的窗口和控件。
- 手機應(yīng)用程序中的不同手機平臺和不同功能的組合,例如在不同的手機上實現(xiàn)相同的應(yīng)用,或者在同一手機上實現(xiàn)不同的應(yīng)用。
- 汽車制造業(yè)中,汽車品牌和引擎類型之間的組合,使得可以在不同的品牌車型上使用不同的引擎。
- 電視機制造業(yè)中,不同的電視品牌和不同的顯示技術(shù)之間的組合,使得可以在不同的品牌電視上使用不同的顯示技術(shù)。
總之,橋接模式適用于需要將抽象部分和實現(xiàn)部分分離的場景,以實現(xiàn)靈活性、可擴展性和解耦的設(shè)計。它可以幫助處理多個維度上的變化,并在運行時動態(tài)地切換抽象和實現(xiàn)的關(guān)系。
eg:手機品牌和軟件是兩個概念,不同的軟件可以在不同的手機上,不同的手機可以有相同的軟件,兩者都具有很大的變動性。
如果我們單獨以手機品牌或手機軟件為基類來進(jìn)行繼承擴展的話,無疑會使類的數(shù)目劇增并且耦合性很高,(如果更改品牌或增加軟件都會增加很多的變動)兩種方式的結(jié)構(gòu)如下:
以將兩者抽象出來兩個基類分別是PhoneBrand和PhoneSoft,那么在品牌類中聚合一個軟件對象的基類將解決軟件和手機擴展混亂的問題,這樣兩者的擴展就相對靈活,剪短了兩者的必要聯(lián)系,結(jié)構(gòu)圖如下:
// 實現(xiàn)類接口
class Implementor {
public:virtual void operationImpl() = 0;
};// 具體實現(xiàn)類 A
class ConcreteImplementorA : public Implementor {
public:void operationImpl() override {// 具體實現(xiàn) A// ...}
};// 具體實現(xiàn)類 B
class ConcreteImplementorB : public Implementor {
public:void operationImpl() override {// 具體實現(xiàn) B// ...}
};// 抽象類
class Abstraction {
protected:Implementor* implementor;public:Abstraction(Implementor* impl) : implementor(impl) {}virtual void operation() = 0;
};// 具體類 A
class ConcreteAbstractionA : public Abstraction {
public:ConcreteAbstractionA(Implementor* impl) : Abstraction(impl) {}void operation() override {// 具體類 A 的操作// ...implementor->operationImpl(); // 調(diào)用實現(xiàn)類接口// ...}
};// 具體類 B
class ConcreteAbstractionB : public Abstraction {
public:ConcreteAbstractionB(Implementor* impl) : Abstraction(impl) {}void operation() override {// 具體類 B 的操作// ...implementor->operationImpl(); // 調(diào)用實現(xiàn)類接口// ...}
};int main() {// 創(chuàng)建具體實現(xiàn)類對象Implementor* implA = new ConcreteImplementorA();Implementor* implB = new ConcreteImplementorB();// 創(chuàng)建具體類對象,并傳入具體實現(xiàn)類對象Abstraction* abstractionA = new ConcreteAbstractionA(implA);Abstraction* abstractionB = new ConcreteAbstractionB(implB);// 調(diào)用具體類的操作abstractionA->operation();abstractionB->operation();delete abstractionA;delete abstractionB;delete implA;delete implB;return 0;
}
二、std::error_code與設(shè)計模式(橋接模式)
std::error_code 類圖
標(biāo)準(zhǔn)庫提供了創(chuàng)建std::error_code的方法,參數(shù)傳std::errc就行。
//std::error_code make_error_code( std::errc e ) noexcept;#include <system_error>
#include <iostream>int main(){std::error_code ec = std::make_error_code(std::errc::invalid_argument);std::cout<<ec.message()<<"\n";// 將輸出Invalid argument
}
稍微看一下std::make_error_code的實現(xiàn):
- generic_category函數(shù)其實是一個全局函數(shù),返回的是std::error_category的單例。
- 源碼很簡單,通過構(gòu)造std::error_code的函數(shù)可以看到std::error_code由兩部分組成,一部分是錯誤碼的值,一部分是std::error_category的單例。再回過頭來看std::error_code類圖就很清楚了。
- 這里有個問題,std::error_code的如果僅僅是通過std::make_error_code去創(chuàng)建,而std::errc的錯誤碼是有限的,如果不夠用的時候,希望用一些專門領(lǐng)域的錯誤碼該怎么辦?
- 這個問題就是如何寫自定義的錯誤碼,std::error_code其實已經(jīng)考慮到這一點了,它是可以擴展支持自定義錯誤碼的。
inline error_code make_error_code(errc errno) noexcept {return error_code(static_cast<int>(errno), std::generic_category());
}const std::error_category& generic_category() noexcept;
std::error_code的設(shè)計實際上是橋接模式,它把抽象和實現(xiàn)分離了,對于錯誤碼來說,抽象代表的是錯誤碼的值,實現(xiàn)代表的是具體的錯誤信息,默認(rèn)情況下std::errc表示錯誤碼的值,而std::error_category表示的是錯誤碼對應(yīng)的具體錯誤信息。
正是因為錯誤碼的值會在不同的領(lǐng)域里含義不同,所以才需要對它做抽象,而具體的錯誤信息也是變化的,不同領(lǐng)域里錯誤信息也不同,所以這里非常適合用橋接模式來解耦和封裝抽象和實現(xiàn)兩部分的變化。
如果要實現(xiàn)自己的錯誤碼,只需要寫自定義的錯誤碼值和派生std::error_category去重寫里面的錯誤信息相關(guān)的虛函數(shù)就行了。
eg:雅蘭亭庫自定義錯誤碼的實現(xiàn)
圖中的單例也在這里是一個全局的函數(shù),里面有一個static instance成員:custom category
雅蘭亭庫里面struct_pack和struct_json都實現(xiàn)了自己的錯誤碼,也是根據(jù)std::error_code的橋接模式去實現(xiàn)的。以struct_pack的error_code為例:
#include <system_error>namespace struct_pack {
enum class errc {ok = 0,no_buffer_space,invalid_argument,hash_conflict,
};namespace detail {class struct_pack_category : public std::error_category {public:virtual const char *name() const noexcept override {return "struct_pack::category";}virtual std::string message(int err_val) const override {switch (static_cast<errc>(err_val)) {case errc::ok:return "ok";case errc::no_buffer_space:return "no buffer space";case errc::invalid_argument:return "invalid argument";case errc::hash_conflict:return "hash conflict";default:return "(unrecognized error)";}}
};inline const std::error_category &category() {static struct_pack::detail::struct_pack_category instance;return instance;
}
} // namespace detail} // namespace struct_packinline std::error_code make_error_code(struct_pack::errc err) {return std::error_code(static_cast<int>(err),struct_pack::detail::category());
}
注意這里的make_error_code函數(shù)其實就是在抽象和實現(xiàn)中間建橋,從而讓std::error_code變成自定義的error_code,一個關(guān)鍵點是實現(xiàn)了struct_pack_category,而不是std::error_category,它是自定義的派生于std::error_category,所以輸出的信息也是自定義的錯誤信息。
參考
- C++ 橋接模式講解和代碼示例
- c++ 設(shè)計模式之橋接模式(Bridge)
- [Back to the basic] std::error_code與設(shè)計模式
- [Back to the basic] std::error_code與設(shè)計模式