写点什么

架构师训练营 1 期第 2 周:框架设计 - 总结

用户头像
灵霄
关注
发布于: 2020 年 09 月 26 日

如何进行优雅的程序设计,使软件的架构更加的富有弹性,更加灵活,易于扩展和易于维护,是软件架构师的一个重要工作职责。本文分析了编程语言的实质,梳理软件编程方法的演变过程,总结面向对象编程的特征,目的,指导原则,以及如何遵循这些原则来进行框架设计。

1. 编程语言的实质及演变过程

1.1 编程的本质



  • 编程的目的是:用计算机来解决现实世界的问题。

  • 编程的过程即:在计算机所能理解的“模型”(解空间)和现实世界(问题空间)之间, 建立一种联系。

  • 问题领域(问题空间):包含与系统所要解决的问题相关的实物和概念的空间。

  • 编程语言是一种“抽象”的机制,其编程方法的演进如下机器代码和汇编语言:对基础机器指令进行抽象非结构化的高级语言:对计算处理逻辑的抽象结构化的程序设计:开始对问题领域进行一定程度的抽象。比如开关函数面向对象程序设计:直接表达问题空间内的元素。比如电灯对象及其开关方法

1.2 编程的核心要素和编程语言的发展脉络

  • 编程的核心要素包括:人,计算机,客观业务领域

  • 编程方法的演进方向也是围绕这三个要素发展的,从面相机器的思维方式,到面向人的思维方式,最后到面向业务领域问题的编程,也就是我们现在使用的面向对象的编程。展现了事物发展的必然规律,面向对象编程更符合我们对问题的理解,也更符合我们对编程的认知。

  • 面向业务领域也可以进行细分,业务领域对象可以是实体对象,在大数据时代,业务领域对象也可以是数据,我们需要面向数据进行编程,这时一些新的编程语言或特性会出现,使面向对象编程的关注点关注到了数据里,如函数式编程,反应式编程等等。


2. 面向对象编程的特征,目的,指导原则

面向对象编程的定义(第一个成功的面向对象的语言 Smalltalk 描述):

  • 万物皆为对象

  • 程序是对象的集合,它们通过发送消息来告知彼此所要做的

  • 每个对象都有自己的由其他对象所构成的存储

  • 每个对象都拥有其类型

  • 某一特定类型的所有对象都可以接收同样的消息


面向对象编程的三要素(关键特征)

  • 封装性 - 隐藏实现

  • 继承性 - 接口的重用

  • 多态性 - 对象互换的魔法


面向对象设计的目的和原则

  • 目的:实现系统的强内聚、松耦合的特性,从而实现系统的易扩展,更强壮,可移植,更简单的特性

  • 原则:主要包括一些设计原则,贯彻设计原则的设计模式,以及基于这些设计原则、设计模式构建出来的一些框架、工具。

  • 设计原则,设计模式

  • 基于设计原则开发一个框架

  • 基于设计模式对代码进行重构

  • 框架,工具

  • 框架是用来实现某一类应用的结构性的程序,是对某一类架构方案可复用的设计与实现,程序必须要依附在框架上面进行进一步的开发和实现,框架决定了程序的整体结构和主体流程,程序由框架调用完成程序的整体结构

  • 作为架构师,能够理解框架背后的规律和框架设计的方法,要开发出自己的框架,或对现有框架进行自己的改进,基于框架的特点,使框架更加符合你要开发的系统的应用场景

  • 框架调用代码,代码调用工具,架构师用框架(如 Tomcat,Spring)保证架构落地,保证整体的程序运行流程和整体的结构,用工具(如 Log4j)来提高开发效率,可复用的一些操作可通过提供一些工具来复用

3. 设计原则与框架设计

软件设计敏捷和软件过程敏捷

  • 对于一个软件开发而言,要想实现敏捷的软件开发,并不只在于敏捷的软件过程保证,更重要的是设计本身是敏捷的(灵活强大)。当我们有需求变更的时候,当我们要快速迭代的时候,这些设计能够快速的支撑我们进行变更和迭代;而不是耦合在一起,僵硬的编程一团,脆弱的,不可移植的,难以使用的,让我们无从下手,再好的软件过程保证,也难以实现一个敏捷的软件开发。


设计的好坏如何评断?软件设计如何应对不断的需求变更(上下文环境)?

  • 软件设计的最终目的,是使软件达到“强内聚、松耦合”,从而使软件:

  • 易扩展 - 易于增加新的功能

  • 更强壮 - 不容易被粗心的程序员破坏

  • 可移植 - 能够在多样的环境下运行

  • 更简单 - 容易理解、容易维护

  • 与之相反,一个“不好的”软件,会发出如下特征:

  • 僵硬 - 不易改变

  • 脆弱 - 只想改 A,结果 B 被意外破坏

  • 不可移植 - 不能适应环境的变化

  • 导致误用的陷阱 - 做错误的事比做正确的事更容易,引诱程序员破坏原有的设计

  • 晦涩 - 代码难以理解

  • 过度设计、copy-paste 代码

3.1 设计原则 1:开闭原则(OCP)

开闭原则(OCP,Open/Closed Principle):对扩展功能开放,对修改代码封闭,不修改代码实现功能的扩展,靠的是抽象,对抽象进行编程

  • 方法一:继承或实现接口

  • 方法二:使用策略模式:不直接依赖目标对象,而是依赖策略接口,目标对象实现策略接口,但是是目标对象脆弱了

  • 方法三:适配器模式:目标对象通过适配器实现策略接口,变化的需求通过不同的适配器来实现

  • 方法四:观察者模式:优化策略接口,将策略接口改成 Listener 接口,可通过内部类的方式实现适配器进行事件监听

3.2 设计原则 2:依赖倒置原则(DIP)

依赖倒置原则(DIP,Dependency Inversion Principle):高层模块不能依赖低层模块,而是大家都依赖于抽象,抽象不能依赖实现,而是实现依赖抽象。倒置开发顺序和职责,先开发接口,接口属于高层模块,低层模块实现接口。高层模块根据自己使用场景进行接口设计。软件层次化,高层决定低层,高层被重用。


  • 框架设计的核心,框架不依赖于代码,但是却可以调用代码,高层(框架)定义一组接口,我们(代码程序)实现接口。

  • 好莱坞原则:Don’t call me,I’ll call you.倒转了层次依赖关系。

3.3 设计原则 3:里氏替换原则(LSP)

里氏替换原则(LSP,Liskov Substitution principle):用来解决继承合理性的问题,如果在编写程序中,子类可以替换父类,则继承是合理的,针对父类编写的程序可以用子类替换。在应用场景中看子类能否替换父类,替换后程序能否正常运行,能正常运行就是合理的继承,不能正常运行就是不合理的继承。同时也可以判断抽象接口使用是否合理。


3.4 设计原则 4:单一职责原则(SPR)

单一职责原则(SPR,Single Responsibility Principle):内聚性原则,一个类只有一个引起它变化的原因,如果原因很多,说明类是不单一的,导致类的职责很多,设计复杂,难以维护。


3.5 设计原则 5:接口隔离原则(ISP)

接口隔离原则(ISP,Interface Segregation Principle):职责不好分清时,如何优化?不应该强迫客户程序依赖它们不需要的方法

  • 通过拆分接口进行隔离,从而保证应用程序不依赖它们不需要的方法

  • 隔离方法:通过适配器隔离通过多继承隔离

4. 框架设计案例 - 反应式编程Flower

一般 web 服务器设计:


反应式编程 Flower 设计

  • 通过网络异步 IO 编程多路复用改善系统性能:用有限线程(容器线程)处理多个并发请求,交给 Flower 运行环境处理,Flower 负责创建工作线程(通过 Akka Actor 实现),处理服务消息,线程不会被阻塞,所以可以用来处理其他消息。


项目 github 链接:https://github.com/zhihuili/flower


注:本文大量参考 piercebn 学员的总结,原文链接[https://xie.infoq.cn/article/ce00a4d6fbf91eef42bebbafd]


发布于: 2020 年 09 月 26 日阅读数: 48
用户头像

灵霄

关注

还未添加个人签名 2019.02.13 加入

还未添加个人简介

评论

发布
暂无评论
架构师训练营 1 期第 2 周:框架设计 - 总结