赞
踩
在C++中,桥接模式通常涉及以下几个角色:
以下是一些可能适合使用桥接模式的具体场景:
总之,桥接模式适用于需要将抽象部分和实现部分分离的场景,以实现灵活性、可扩展性和解耦的设计。它可以帮助处理多个维度上的变化,并在运行时动态地切换抽象和实现的关系。
eg:手机品牌和软件是两个概念,不同的软件可以在不同的手机上,不同的手机可以有相同的软件,两者都具有很大的变动性。
如果我们单独以手机品牌或手机软件为基类来进行继承扩展的话,无疑会使类的数目剧增并且耦合性很高,(如果更改品牌或增加软件都会增加很多的变动)两种方式的结构如下:
以将两者抽象出来两个基类分别是PhoneBrand和PhoneSoft,那么在品牌类中聚合一个软件对象的基类将解决软件和手机扩展混乱的问题,这样两者的扩展就相对灵活,剪短了两者的必要联系,结构图如下:
// 实现类接口 class Implementor { public: virtual void operationImpl() = 0; }; // 具体实现类 A class ConcreteImplementorA : public Implementor { public: void operationImpl() override { // 具体实现 A // ... } }; // 具体实现类 B class ConcreteImplementorB : public Implementor { public: void operationImpl() override { // 具体实现 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(); // 调用实现类接口 // ... } }; // 具体类 B class ConcreteAbstractionB : public Abstraction { public: ConcreteAbstractionB(Implementor* impl) : Abstraction(impl) {} void operation() override { // 具体类 B 的操作 // ... implementor->operationImpl(); // 调用实现类接口 // ... } }; int main() { // 创建具体实现类对象 Implementor* implA = new ConcreteImplementorA(); Implementor* implB = new ConcreteImplementorB(); // 创建具体类对象,并传入具体实现类对象 Abstraction* abstractionA = new ConcreteAbstractionA(implA); Abstraction* abstractionB = new ConcreteAbstractionB(implB); // 调用具体类的操作 abstractionA->operation(); abstractionB->operation(); delete abstractionA; delete abstractionB; delete implA; delete implB; return 0; }
std::error_code 类图
标准库提供了创建std::error_code的方法,参数传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的实现:
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的设计实际上是桥接模式,它把抽象和实现分离了,对于错误码来说,抽象代表的是错误码的值,实现代表的是具体的错误信息,默认情况下std::errc表示错误码的值,而std::error_category表示的是错误码对应的具体错误信息。
正是因为错误码的值会在不同的领域里含义不同,所以才需要对它做抽象,而具体的错误信息也是变化的,不同领域里错误信息也不同,所以这里非常适合用桥接模式来解耦和封装抽象和实现两部分的变化。
如果要实现自己的错误码,只需要写自定义的错误码值和派生std::error_category去重写里面的错误信息相关的虚函数就行了。
图中的单例也在这里是一个全局的函数,里面有一个static instance成员:custom category
雅兰亭库里面struct_pack和struct_json都实现了自己的错误码,也是根据std::error_code的桥接模式去实现的。以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_pack inline 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函数其实就是在抽象和实现中间建桥,从而让std::error_code变成自定义的error_code,一个关键点是实现了struct_pack_category,而不是std::error_category,它是自定义的派生于std::error_category,所以输出的信息也是自定义的错误信息。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。