前言
本文先从理论知识的角度介绍一下设计模式和设计原则。
设计模式:是指在软件开发中,经过验证的,用于解决在特定环境下,重复出现的,特定问题的解决方案。
设计原则:是指在软件开发中,想要写出一手好代码,就得在设计原则的框架下书写代码。
提示:以下是本篇文章正文内容
一、设计模式
强力推荐:《设计模式-可复用的面向对象的基础》
根据书中描述,总共有23种设计模式,它们大概可以分为三类:创建型设计模式,结构型设计模式,行为型设计模式。
创建型设计模式:工厂模式,抽象工厂模式,单例模式,建造者模式,原型模式。
结构型设计模式:适配器模式,桥接模式,过滤器模式,组合模式,装饰器模式,外观模式,享元模式,代理模式。
行为型设计模式:责任链模式,命令模式,解释器模式,迭代器模式,中介者模式,备忘录 模式,观察者模式,状态模式,空对象模式,策略模式,模板模式,访问者模式。
是的,多,太多设计模式了。
二、设计原则
设计原则包括:依赖倒置,开放封闭,单一职责,里式替换,接口隔离,迪米特法则。
1.依赖倒置
高层模块不应该依赖于底层模块,两者都应该依赖于抽象。抽象不应该依赖细节,细节应该依赖抽象。
举两个例子:
例子1
对于汽车自动驾驶系统,每个汽车厂商或者汽车品牌如果都使用了同一套自动驾驶系统的话,如果本田汽车提出了新的需求,那么这套自动驾驶系统除了要开发适应本田汽车的新需求外,还得兼容其他厂商的汽车。如果有新的品牌的汽车需要使用该自动驾驶系统,还得在汽车自动驾驶系统中添加新的品牌并做一些兼容性工作,这会使得自动驾驶系统变得越来越复杂,越来越难以维护。如果在自动驾驶系统和厂商中间穿插一层自动驾驶标准,汽车 自动驾驶系统和各个品牌的汽车都依赖于这层标准 ,那么无论是新增需求还是修改需求,还是自动驾驶系统的迭代更新,都可以避免了牵一发而动全身的窘境。
例子2
上代码
class books
{
public:
void show_cplusplus()
{
std::cout << "I'm cplusplus book";
}
void show_java()
{
std::cout << "I'm java book";
}
}
void main()
{
books b = new books();
b->show_cplusplus();
b->show_java();
}
上述代码定义了一个书库的类,如果我现在需要在书库里增加一个python的书,那么我需要在books类中新增一个show_python()的方法,也需要在高层(main函数)调用中增加调用。如果这样肆意扩展的话,books的类很快就会变得臃肿不堪。怎么修改它来满足依赖倒置原则呢?
上第二段代码
class book()
{
protected:
virtual void show();
}
class cplusplus:public book
{
void show()
{
std::cout << "I'm cplusplus book";
}
}
class java:public book
{
void show()
{
std::cout << "I'm java book";
}
}
class books
{
void show(book* b)
{
b.show();
}
}
void main()
{
books b = new books();
b->show(new java());
}
上面定义了一个基类book。把相关的书籍定义成一个子类并继承book。如果需要新加书籍,那么只需要新建一个类并继承book,并在高层(main函数)中把指针传进b->show中即可。上面代码并不是一个很完美的代码,而仅仅是解释了高层不应该依赖于底层实现,二者都应该依赖于抽象(book基类)。
2.开放封闭
一个类应该对扩展开放,对修改关闭。
上面第二个程序也可以说明这一点,对于book基类的扩展实现book的类对于扩展的开放;如果需要新增一个类或者修改一个类的show方法,并不需要改基类的代码,做到对修改关闭。
在C++中,扩展一般指的是下列两种情况:
- 组合基类指针。
class Base()
{
}
class Subject()
{
private:
Base* base;
}
- 继承基类并对基类中的虚函数的覆盖。
class Base()
{
protected:
virtual void fun1(){}
virtual void fun2(){}
}
class Subject: public Base
{
protected:
virtual void fun1(){}
virtual void fun2(){}
void fun3();
}
3.单一职责
一个类应该仅有一个引起它变化的原因。
4.里式替换
一个引用基类的地方必须能用其子类的对象完全替换或者代替其基类对象,而不会影响到整体的执行原则。
5.接口隔离
不应该强迫高层应用去依赖于它们不用的方法。
如果一个类拥有过多的接口(不管是高层应用需不需要用到),那么这些接口都会使得这个类的职责变得复杂。
6.迪米特法则
尽量减少类与类之间的关系,也就是降低类之间的耦合。
三、一些小tips
- 在编写代码时,应该首先符合设计原则,然后迭代出设计模式,一个符合设计原则的代码,后面如果需要迭代出设计模式是很方便的。
- 解耦合不是说在代码中消灭耦合,而是将耦合程度降低。还有另一个概念叫做锁粒度,它不是为了消灭锁,而是将锁的颗粒降到最低。
- 怎么学设计模式:首先需要找到稳定点和变化点,然后把变化点隔离起来。然后先满足设计原则,再迭代出设计模式。
- 编写代码的思维模式不过就是抽象思维和分治思维,抽象思维也就是应用设计模式来为特定的问题找到解决办法,而分治思维则就是把问题拆分。
- 比较重要的设计原则是:开闭原则,单一原则,里式替换,接口隔离,它们不是互相独立的,它们都是有交集的,而且可以互相佐证的。
- 觉得文章对你有帮助,那就点个赞呗