一、设计模式是什么?
1、计模式是指在软件开发中,经过验证的,用于解决在特定的环境下,重复出现的,特定问题的解决方案。
2、解决问题的固定套路 。
(注意慎用设计模式)
二、设计模式怎么来的?
1、满足设计原则后,慢慢迭代出来的。
三、设计模式解决了什么问题?
①:前提:既有变化点又有稳定点。
②:少量的改变就能适应需求的变化。
比喻:整洁的房间,好动的猫。怎么保证房间的整洁?把猫关进一个笼子。
四、设计模式基础
1、面向对象思想
封装:隐藏实现细节,实现模块化
继承:无需修改原有类的情况下,实现对功能的扩展。
多态:
①:静态多态--->函数重载
②:动态多态,继承中的虚函数重写。
早绑定:在没有使用虚函数的时候发生。
晚绑定:使用虚函数的时候发生。
下面解释过程:
class Base{
public:
virtual void func1(){}
virtual void func2(){}
int a;
};
class Subject:public Base{
public:
virtual void func2(){}
virtual void func3(){}
int b;
};
由这2张图看出晚绑定就是Base*p = new Subject;时,Base会转换为Subject,p就变成了Subject类型。但是早绑定就不会转换,p还是Base类型。
2、设计原则
2.1、依赖倒置
- 核心思想:高层模块不应该依赖低层模块,二者都应该依赖抽象。抽象不应该依赖细节,细节应该依赖抽象。
- 实现方式:通过接口或抽象类定义依赖关系,具体实现类依赖接口或抽象类。
class IAnimal {
public:
virtual void speak() = 0;
};
class Dog : public IAnimal {
public:
void speak() override {
std::cout << "Woof!" << std::endl;
}
};
class Cat : public IAnimal {
public:
void speak() override {
std::cout << "Meow!" << std::endl;
}
};
void makeAnimalSpeak(IAnimal* animal) {
animal->speak();
}
2.2、开发封闭
对扩展开发,对修改关闭。
扩展方式 ①继承 ②多态组合
class Shape {
public:
virtual void draw() = 0;
};
class Circle : public Shape {
public:
void draw() override {
std::cout << "Drawing Circle" << std::endl;
}
};
class Square : public Shape {
public:
void draw() override {
std::cout << "Drawing Square" << std::endl;
}
};
2.3、面向接口
- 核心思想:通过接口定义行为,具体实现类实现接口。
- 优点:提高代码的灵活性和可维护性。
class ILogger {
public:
virtual void log(const std::string& message) = 0;
};
class FileLogger : public ILogger {
public:
void log(const std::string& message) override {
// Log to file
}
};
class ConsoleLogger : public ILogger {
public:
void log(const std::string& message) override {
// Log to console
}
};
2.4、封装变化点
- 核心思想:将系统中可能变化的部分封装起来,减少对系统其他部分的影响。
- 实现方式:使用
protected或private关键字限制访问权限。
class PaymentProcessor {
protected:
void validatePayment() {
// Validation logic
}
public:
void processPayment() {
validatePayment();
// Process payment
}
};
2.5、单一职责原则
- 核心思想:一个类只负责一个职责,只有一个引起它变化的原因。
class Report {
public:
void generateReport() {
// Generate report
}
};
class ReportPrinter {
public:
void printReport(const Report& report) {
// Print report
}
};
2.6、里氏替换
- 核心思想:子类可以替换父类,并且不会影响程序的正确性。子类继承父类的一些行为时,父类的行为要保存到子类去。
class Bird {
public:
virtual void fly() {
std::cout << "Flying" << std::endl;
}
};
class Sparrow : public Bird {
public:
void fly() override {
std::cout << "Sparrow flying" << std::endl;
}
};
2.7、接口隔离
- 核心思想:客户端不应该依赖它不需要的接口。
- 实现方式:将大接口拆分为小接口。
- 类封装,权限限定词来实现。private是只有自己可以用,public是用户、自己和子类都可以用。protected是给自己和子类使用。
- 类与类依赖 接口(依赖注入)
2.8、组合优于继承
- 核心思想:优先使用组合而不是继承来实现代码复用。
class Engine {
public:
void start() {
std::cout << "Engine started" << std::endl;
}
};
class Car {
private:
Engine engine;
public:
void start() {
engine.start();
}
};
2.9、最小知道原则
- 核心思想:一个类应该只与直接相关的类交互,减少类之间的耦合。用户知道越少越好。
class Zooshow{
public:
Zooshow(){}
public:
void show(){
if(show0()){
Play();
}
show1();
}
private://用户不需要知道他们怎么运行,所以可以直接封装起来
bool show0(){}
void Play(){}
void show1(){}
};
五、设计模式如何学?
- 明确目的:在现有的设计模式基础上,扩展代码。
- 做功能抽象时,如何选择设计模式。
- 通过看代码知道是哪个设计模式。
- 符合的设计原则要清楚。
- 如何扩展代码。
- 有哪些设计应用场景。
六、观察者模式
定义:定义对象间的一种一对多(变化)的依赖关系,以便当一个对象Subject的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。
1、解决的问题
-
稳定点:一对多的依赖关系,“一”变化“多”跟着变化
-
变化点:①:“多”增加、②“多”减少
为了让稳定点变的更稳定,需要将他们抽象。比如:
class control{
public:
//调用ZooShow类。
//如果ZooShow类 有多个对象使用
//就需要抽象
vector<Zooshow*>v;//如果不这样写
```
//一变化则多也跟着变化,就需要一个函数来调用他们共同的函数了
for(){
v.show();
}
//就可能是写个函数封装起来
void fun(){//每次增加对象都要填一个新的z,就不稳定了
Zooshow* z1= new Zooshow;
Zooshow* z2 = new Zooshow;
}
};
稳定点用到了面向接口原则。变量类型抽象成了接口,而不是类。
七、策略模式
定义:定义一系列算法,把它们封装起来,并且使它们可互相替换,该模式使得算法可独立于使用它的客户程序而变化。
解决了什么问题:
①:稳定点:客户程序与算法的调用关系
②:变化点:算法变化(新加算法和后期算法改变)
class VAC_GuoQing : public ProStategy {
public:
virtual double CalcPro(const Context &ctx){}
};
class VAC_GuoQing2 : public VAC_GuoQing {
public:
virtual double CalcPro(const Context &ctx){}
};
class VAC_Shengdan : public ProStategy {
public:
virtual double CalcPro(const Context &ctx){}
};
// 设计原则:接口隔离原则
// 组合、继承
// 组合基类指针
// 两种方法:1. 采用具体接口选择算法 2. 依赖注入
class Promotion {
public:
Promotion(ProStategy *sss = nullptr) : s(sss){}
~Promotion(){}
void Choose(ProStategy *sss) {
// 条件选择
if (sss != nullptr) {
s = sss;
}
}
double CalcPromotion(const Context &ctx){
if (s != nullptr) {
return s->CalcPro(ctx);
}
return 0.0L;
}
private:
ProStategy *s;
};