在编写程序的过程中开发者都会考虑软件的扩展性,代码有低耦合高内聚的设计,刚刚入行的程序员大部分编写程序的时候主要是为了完成功能,能实现具体功能即可,但是这种方式写出来的代码很少具备扩展性,如果需求变更就会带来很多的代码改动,代码往往都会改的面目全非。如何写出易扩展并且代码结构很清晰也便于阅读?这个问题我相信是困扰很多程序员的问题之一,解决这个问题就有良好设计原则,在面向对象编程语言中有很多软件工程界实践范例,例如 23 种设计模式的应用,Java 这门编程语言就是天生支持面向对象的,而某些面向过程的编程语言可能对设计模式的应用就显得没有那么得心应手。

世界上有很多的优秀工程师开发过很多知名软件,这些大型软件中都有设计模式的应用,例如 IBM Eclipse 这类软件,项目工程非常庞大并且参入开发这个项目的开发人员人数众多,如何保证某个参入开发人员的技术水平过关?如何保证不同开发者开发模块能互相工作?如何保证整个项目工程代码质量?像 Eclipse 这种面向开发者的软件,还要考虑一些开发者自定义实现功能的问题?Eclipse 的插件系统如何设计?如何在不修改 Eclipse 主体代码的情况下能支持更多自定义功能?

针对这些问题计算机界的大佬都已经遇到过,并且部分大佬还提出一些软件工程的实践,Kent BeckErich Gamma 设计模式的先驱和测试驱动开发的提出者,也是知名软件 VSCode 和 Eclipse 、Java 测试框架 JUnit 项目共同的作者,他们曾提出很多关于软件工程的人员实践建议和出版很多软件工程的书籍,如果有对这方面想深入读者可以阅读者两位大佬的出版一些书籍和一些公开的演讲视频,整理一些演讲视频:


Abstract 和 Interface

如果能用一句话来概括如何提高代码的复用率的话,我回答是 Abstract 和 Interface ,设计模式的精髓也是抽象和接口。对应 Java 这种面向对象语言来说更是如此,一切万物皆对象,对象的三大特征就是: 封装 、继承、多态 ,而其中的多态使得对象的行为表现出不同形态。抽象类和抽象接口是一个对象类的多态前提,软件工程中一些规范:开闭原则 、依赖倒置原则 、 里氏替换原则 ,都是以抽象为基础提出软件开发原则,由此也产生 23 种设计模式的应用。

现在有一个需求为使用程序画一颗心,这颗心可能有很多种表现方式有不同形状和不同颜色,如上图,如果要在程序中画出这些不同种类,就会用到抽象。像这种需求在软件工程中有很多,如果你是开发应用框架更是如此,像 Spring 这种企业级开发框架,他们设计者就要考虑很多为了别人编写代码的时候需求,能在不修改 Spring 代码的前提下实现功能扩展,多扩展开放对修改关闭这就是开闭原则。

在开发 Java Web 应用的时候会用到 Tomcat 种类的 Servlet 容器,而 Servlet 则是 JakartaEE 中定义一种标准,开发者去实现 HttpServlet 的抽象就可以完成一个 HTTP Service 开发工作,而监听 HTTP 请求处理的逻辑则交给 Tomcat 这类容器去做,这些核心的代码逻辑已经被抽象成可以复用的代码逻辑,每个开发 Web 应用程序的程序员都会用到,这就使得 Tomcat 可以被更多开发者复用,而业务逻辑则是开发者要关心和实现的。

那么如何实现类似于 Spring 和 Tomcat 这种框架呢?答案很简单把公共使用的代码逻辑抽象出来,剥离出来成为可以复用的代码,而具体可变的代码逻辑则定义一种模板的形式,例如 Servlet 容器中的 HttpServlet 的抽象。实现这就会要用到依赖倒置原则,通过提前定义一层抽象,而具体实现者必须遵守这些抽象层定义规范,高层次的模块不依赖于低层次的模块,它们会共同依赖抽象层,抽象层不具备具体的实现而由低层次的模块去实现。

有了抽象还是不够的,怎么限制别人对自己的代码的访问权限?像 Java 中就提供一些访问权限修饰关键字 Private 、Protected 、 Public 这些来帮助开发者来修饰某个对象和某个对象行为,这些是远远不够的,程序在运行过程中是多变的,也就是多态的形式出现,这时如何去做限制?最后还是要用到抽象,多个抽象的组合,使用抽象的接口去隔离,把一类行为抽象成接口,这多个这样的抽象接口又可以组成另外一个抽象,这就是单一责任性和抽象的隔离性。

最理想的程序设计状态是像插件一样可以可插拔的,面向对象编程语言就提供这些很好的原生支持,多态非常简单,子类可以实现父类或者接口的抽象,对应编程开发者来说是面向的抽象层进行编程,运行期间会注入不同的子类,程序就会表现出不同的形态,这就是多态。


最后修改:2023 年 07 月 05 日
如果觉得我的文章对你有用,请随意赞赏 🌹 谢谢 !