结构型模式
结构型模式关注于将类和对象组合成较大的结构。继承的概念被用来组合接口和定义组合对象获得新功能的方式。
适配器模式
适配器的作用为:将一个类的接口转换成客户希望的另外一个接口,适配器使得原本由于接口不兼容而不能一起工作的那些类可以一起工作
适配器主要应用于希望复用一些现存的类,但是接口又与复用环境要求不一致的情况。其分为两种:类适配器和对象适配器模式:
类适配器使用
继承
来适配接口对象适配器使用
组合
来适配接口
例如:
class Voltage{
// 中国的通用电压是 220V
size_t voltage = 220;
public:
size_t output(){
return this->voltage;
}
};
bool PhoneCharging(size_t voltage){
// 手机充电需要的电压一般为 5V
static size_t phone_voltage = 5;
if( voltage == phone_voltage) return true;
return false;
}
class ClassAdapter: Voltage{
public:
size_t adapter(){
size_t voltage = Voltage::output()/44; // 对通用电压进行转换
return voltage;
}
};
class ObjectAdapter{
Voltage vol;
public:
size_t adapter(){
return vol.output()/44;
}
};
// 使用
ClassAdapter classAdapter;
ObjectAdapter objectAdapter;
cout<<boolalpha
<<"使用类适配器能否给手机充电?"<<PhoneCharging(classAdapter.adapter())
<<endl
<<"使用对象适配器能否给手机充电?"<<PhoneCharging(objectAdapter.adapter());
一般而言,我们更加推荐使用 |
桥接模式
桥街模式准确的定义为:将抽象部分与它的实现分离,使它们都可以独立地变化
。
通俗地来说,就是类继承之间只进行接口的继承,而具体实现则放到另外一个类中:
@startuml
- class Person\{
eat()
}
- class Child\{
implChild
eat()
}
Person <|.. Child Child <.. implChild
@enduml
由于实现的方式有多种,桥接模式将实现与接口进行分离,那么接口和实现就可以实现独自变化,进而降低了系统之间的耦合性。这种 impl 手法在 C 中还可以用来改进编译速度,提升程序效率。footnote:[Effective C 第三版 条款31:将文件间的编译依赖依存关系降到最低]
过滤器模式
装饰器模式
装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。
这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。
装饰器动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。其用法类似与组合,但是相比组合,导出的接口更少。
例如:[1]
class Pancake{
public:
virtual const string description() = 0;
virtual int cost() = 0;
};
class MeatPancake : public Pancake{
const string name = "肉煎饼";
public:
const string description() override{
return name;
}
int cost() override{
return 1;
}
};
class PancakeDecorator: public Pancake{
Pancake* decorated;
public:
explicit PancakeDecorator(Pancake* cake): decorated(cake){}
const string description() override{
return decorated->description();
}
int cost() override{
return decorated->cost();
}
virtual ~PancakeDecorator() = 0{
}
};
class Egg: public PancakeDecorator{
public:
explicit Egg(Pancake* cake): PancakeDecorator(cake){}
const string description() override{
return PancakeDecorator::description() + "加蛋";
}
int cost() override{
return PancakeDecorator::cost() + 1.5;
}
};
class Potato: public PancakeDecorator{
public:
explicit Potato(Pancake* cake): PancakeDecorator(cake){}
const string description() override{
return PancakeDecorator::description() + "加土豆";
}
int cost() override{
return PancakeDecorator::cost() + 2;
}
};
//
Pancake *cake = new MeatPancake;
cake = new Egg(cake);
cake = new Potato(cake);
cout << cake->description() << endl
<< cake->cost();
你可能会发现,实际上这是一种“横向类型转换”,为了保证代码的正确性,所有被访问的接口必须是虚有的,因此通过装饰器的基类来限定可访问的接口,并做为子类做一些初始化工作。
外观模式
外观模式:为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
class SubSystemOne {
public:
void MethodOne() {
cout << "MethodOne" << endl;
}
};
class SubSystemTwo {
public:
void MethodTwo() {
cout << "SubSystemTwo" << endl;
}
};
class Facade {
SubSystemOne one;
SubSystemTwo two;
public:
void MethodA() {
one.MethodOne();
}
void MethodTwo() {
two.MethodTwo();
}
};
// 使用
Facade facade;
facade.MethodA();
facade.MethodTwo();
享元模式
享元模式用于创建大量重复的对象,比如围棋棋子,通过区分其常用状态(内部状态)和不常用状态(外部状态)将其进行划分。
typedef pair<size_t, size_t> Coords;
enum class Color {
black, white
};
class Go {
Color color;
vector<Coords> coords;
public:
Go(Color color): color(color){}
void setCoords(Coords coord) {
coords.push_back(coord);
}
};
class GoFactory {
unordered_map<Color, Go> gos;
public:
Go getGo(Color color) {
if (gos.find(color) == gos.end()) {
Go go(color);
gos.insert(pair<Color, Go>(color, go));
}
return gos.at(color);
}
};
// 使用
GoFactory factory;
auto white = factory.getGo(Color::white);
white.setCoords(Coords(10,20));
white.setCoords(Coords(10,25));
white.setCoords(Coords(10, 30));
auto black = factory.getGo(Color::black);
black.setCoords(Coords(5,15));
black.setCoords(Coords(5,25));
black.setCoords(Coords(5,35));