设计模式专栏目录
创建型设计模式-单例模式/工厂模式/抽象工厂
行为型设计模式:模板设计模式/观察者设计模式/策略设计模式
结构型设计模式:装饰器模式
C#反射机制实现开闭原则的简单工厂模式
目录
- 设计模式专栏目录
- 设计模式分类
- 设计模式的设计原则
- 装饰器模式
设计模式分类
设计模式可以分为三种类型:创建型设计模式、结构型设计模式和行为型设计模式。
创建型设计模式:这些模式涉及到对象的创建机制,包括简单工厂模式、工厂方法模式、抽象工厂模式、单例模式、建造者模式和原型模式。
结构型设计模式:这些模式涉及到类和对象的组合,包括适配器模式、桥接模式、组合模式、装饰器模式、外观模式、享元模式和代理模式。
行为型设计模式:这些模式涉及到对象之间的通信和交互,包括责任链模式、命令模式、解释器模式、迭代器模式、中介者模式、备忘录模式、观察者模式、状态模式、策略模式、模板方法模式和访问者模式。
本文是对结构型设计模式中的装饰器、组合设计模式的一个总结。每个设计模式的定义都比较晦涩,可以直接看代码理解。
设计模式的设计原则
依赖倒置:高层模块不应该依赖低层模块,两者都应该依赖抽象; 抽象不应该依赖具体实现,具体实现应该依赖于抽象; (记住依赖抽象就好了)。
开放封闭:一个类应该对扩展(组合和继承)开放,对修改关闭;
面向接口:不将变量类型声明为某个特定的具体类,而是声明为某个接口;
客户程序无需获知对象的具体类型,只需要知道对象所具有的接口;
减少系统中各部分的依赖关系,从而实现“高内聚、松耦合”的类型设计方案;(记住只暴露接口,只调用接口)。
封装变化点:将稳定点和变化点分离,扩展修改变化点;让稳定点和变化点的实现层次分离;
单一职责:一个类应该仅有一个引起它变化的原因; (就是变化点不要太多)。
里氏替换:子类型必须能够替换掉它的父类型;主要出现在子类覆盖父类实现,原来使用父类型的程序可能出现错误;覆盖了父类方法却没有实现父类方法的职责;( 就是子类可以覆盖父类的方法,但是得保证父类必要的功能)。
接口隔离:不应该强迫客户依赖于它们不用的方法;
一般用于处理一个类拥有比较多的接口,而这些接口涉及到很多职责;
客户端不应该依赖它不需要的接口。
一个类对另一个类的依赖应该建立在最小的接口上。
组合优于继承:继承耦合度高,组合耦合度低;
装饰器模式
动态的给对象添加一些额外的责任,就增加功能来说,装饰比生成子类更为灵活。
用一个菜品计算成本(包括食物和各种调料)的例子说明这个设计模式:
在餐馆需要给食物计算成本,比如面条和加的各种调料:
#include <iostream>
#include <string>
using namespace std;
// 食品类
class Food {
protected:
string des;
double price;
public:
virtual double cost() = 0;
string getDes() {
return des;
}
void setDes(string des) {
this->des = des;
}
double getPrice() {
return price;
}
void setPrice(double price) {
this->price = price;
}
};
// 面条类
class Noodles : public Food {
public:
double cost() override {
return getPrice();
}
};
// 中式面条类
class ChineseNoodles : public Noodles {
public:
ChineseNoodles() {
setDes("中式面条");
setPrice(25.00);
}
};
// 装饰器类
class Decorator : public Food {
protected:
Food* desFood;
public:
Decorator(Food* desFood) {
this->desFood = desFood;
}
double cost() override {
cout << desFood->getDes() << "价格:" << desFood->getPrice() << " 配料如下:"
<< getDes() << " 价格:" << getPrice() << " 总价" << (getPrice() + desFood->cost()) << endl;
return getPrice() + desFood->cost();
}
};
// 孜然类
class Cumin : public Decorator {
public:
Cumin(Food* desFood) : Decorator(desFood) {
setDes("孜然");
setPrice(2.00);
}
};
// 胡椒类
class Peper : public Decorator {
public:
Peper(Food* desFood) : Decorator(desFood) {
setDes("胡椒");
setPrice(3.00);
}
};
int main() {
// 先定义一个被装饰者,返回对象要为最顶层的对象,这样被装饰者才能接受
Food* noodles = new ChineseNoodles();
// 定义一个装饰者对象
Food* cumin = new Cumin(noodles);
// 输出为:中式面条价格:25配料如下:孜然价格:2总价27
cout << "-----------面条+孜然------------------------" << endl;
cumin->cost();
cout << "-----------面条+胡椒------------------------" << endl;
Food* peper = new Peper(noodles);
peper->cost();
cout << "-----------面条+胡椒+孜然------------------------" << endl;
peper = new Peper(cumin);
cout << "面条+胡椒+孜然价格:" <<peper->cost();
delete cumin;
delete noodles;
delete peper;
return 0;
}
“面条+胡椒+孜然”的例子日志打印比较乱,是由于装饰器类cost打印有嵌套,所以日志打印比较乱。
结构:
- 被装饰者抽象接口(Food):提供基础功能方法,和装饰方法接口。
- 具体的被装饰者(Noodles):继承抽象类,实现方法接口。
- 装饰者公共类(Decorator):实现统一的装饰方法。
- 具体的修饰者类:定制化装饰者属性。
使用场景:在软件开发过程中,有时想用一些现存的组件(已经定义好的对象)。这些组件可能只是完成一些核心功能。但在不改变其架构的情况下,可以动态地扩展其功能。所以这些都可以采用装饰模式来实现。
特点:
装饰者设计模式是通过组合+继承的形式实现的。
装饰类和被装饰类可以独立发展,不会相互耦合。
装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。