随着各式各样培训机构的兴起,越来越多的新手程序员开始这样想:赶紧学Spring Boot + Vue前后端分离,学明白了就不愁工作啦。一味的追求编码“招式”,而忽略了“内功”的修炼。什么是内功呢?数据结构,算法,设计模式,重构,软件工程。“招式”可以通过网上各式各样的视频快速学会,“内功”则需要长时间的积累。**空泛的学习设计模式是没有用的,一定要结合到具体的“实战招式”才能灵活运用。**这个系列将会结合源码系列,从一而终的讲述你最常用的设计模式,是一个比较长但又值得的过程。
本文参考了刘伟老师的设计模式博客(推荐):https://me.csdn.net/LoveLion
设计模式
了解设计模式,要先从模式讲起。 模式是在特定环境下解决某类重复问题的一套有效方案。以此类推,设计模式就是在解决开发过程中某类问题的较优模板。所以说,多学一种设计模式就意味着你能多处理一类型的问题。
设计模式根据用途主要分为三种类型:
1.创建型模式:描述如何创建对象
2.结构型模式:如何实现类和对象的组合
3.行为型模式:类和对象怎样交互以及如何分配职责
学习设计模式的过程应该遵循这样一个步骤:这个设计模式的要解决一个什么场景?它是如何解决的?它的好处在哪里?
面向对象设计原则
在一个项目的开发中,如何同时提高系统的可维护性与可复用性是需要解决的核心问题之一。面向对象设计原则就是为了解决这个问题而诞生,它是从各种优良的设计模式中总结出来的指导性原则。带着指导性原则去审计你的代码,然后对不符合原则的点进行不断的更正,才能设计出一个优秀的系统。
单一职责原则
一个类或者一个接口只负责单一的功能。
听起来很简单但又超级难运用!我们在日常开发中,总是在不经意间就将类的功能多元化了:最典型的JDBC,又得连接数据库,又得执行sql语句,有时候还顺带着业务代码.最常见的实现方式就是分成DAO,Mapper,Service,然后Service中处理业务逻辑,Mapper中处理具体的sql,DAO交互的对象。
这不就是最常见的MVC嘛,所以说设计模式就在我们身边。
开闭原则
应该在不修改原有代码的情况下进行扩展。
最典型的例子:
public void test(String type){
if(type.equals("circle")){
Circle c = new Circle();
c.display();
} else if(type.equals("rectangle")){
Rectangle c = new Rectangle();
c.display();
}
}
如果增加了其他形状,就只好再加一个if判断,这很明显违背了开闭原则。那么应该怎么做呢?
将其抽象成一个形状抽象类,每次调用时直接传入该抽象类的具体实现即可调用(会调用到具体的方法)。
public void test(Shape shape){
shape.display();
}
抽象化是开闭原则的关键,进行扩展时只需要增加新的实现类而无需修改已有代码。
里氏代换原则
所有引用基类(父类)的地方必须能透明地使用其子类的对象。
通俗点说,在程序中应该尽量采用基类类型来进行对象的定义,在运行期再确定其子类类型。它是实现开闭原则的具体手段之一。
依赖倒转原则
抽象不应该依赖于细节,细节应当依赖于抽象。
也就是常说的面向接口编程,使用接口和抽象类进行变量类型声明,而不需要使用具体类来做这些事情。
是不是有点懵?感觉跟里氏代换分不清?别急,看下面一个例子:
回到上面形状的例子,最开始的类图:
我们已经分析过了,增加新的类型时都必须修改Executor的执行逻辑,系统扩展性较差。将其抽象出一个Shape抽象类,具体数据存放在config.xml配置文件中,符合依赖倒转原则。在程序执行时,会根据配置文件生成一个Shape引用的具体子类对象,符合里氏代换原则。在进行扩展时,原有代码不需要进行修改,符合开闭原则。
所以说,在大多数情况下,这三个设计原则会同时出现。开闭原则是目标,里氏代换原则是基础,依赖倒转原则是手段,它们相辅相成,只是分析问题时所站的角度不同。
接口隔离原则
使用多个专门的接口,而不使用单一的总接口。也就是说接口应当尽量细化,同时接口中的方法应该尽量的少,防止实现一些冗余方法。
迪米特原则
一个实体应该尽可能少的与其他实体发生相互作用。
具体的运用:
1.尽量创建松耦合的类,必要时可以通过引入中间类来实现解耦合(耦合度越低越有利于复用);
2.降低类内部成员变量与成员方法的访问权限;
3.只要有可能,就设计成不可变类。
在具体设计模式的过程中,应该将这些原则代入到设计模式中,看看应用了哪些原则,也就更能领会设计模式的优良之处了。
扩展:系统为什么要分层?
-
代码和系统的可维护性更高。系统分层之后,每个层次都有自己的定位,每个层次内部的组件都有自己的分工,系统就会变得很清晰,维护起来非常明确; -
方便开发团队分工和开发效率的提升;举个例子,mybatis 这么大的一个源码框架不可 能是一个人开发的,他需要一个团队,团队之间肯定有分工,既然有了层次的划分,分 工也会变得容易,开发人员可以专注于某一层的某一个模块的实现,专注力提升了,开 发效率自然也会提升; -
提高系统的伸缩性和性能。系统分层之后,我们只要把层次之间的调用接口明确了,那 我们就可以从逻辑上的分层变成物理上的分层。当系统并发量吞吐量上来了,怎么办? 为了提高系统伸缩性和性能,我们可以把不同的层部署在不同服务器集群上,不同的组 件放在不同的机器上,用多台机器去抗压力,这就提高了系统的性能。压力大的时候扩 展节点加机器,压力小的时候,压缩节点减机器,系统的伸缩性就是这么来的。