• 【学习笔记】《架构整洁之道》(1)


    零、前言

    《架构整洁之道》的内容大体分为3个部分:

    1. 编程范式(结构化编程、面向对象编程和函数式编程)
    2. 设计原则(主要是SOLID)
    3. 软件架构(其中讲了很多有关软件架构高屋建瓴的内容)

    第一部分学习笔记,包含上述1,2两部分。

    无论是微观世界的代码,还是宏观层面的架构,无论是三种编程范式还是微服务架构,它们都在解决一个问题——分离控制和逻辑。

    所谓控制就是对程序流转的业务逻辑无关的代码或系统的控制(如多线程、异步、服务发现、部署、弹性伸缩等),所谓逻辑则是指实实在在的业务逻辑,是解决用户问题的逻辑。

    控制和逻辑构成了整体的软件复杂度,有效地分离控制和逻辑会让你的系统得到最大的简化。

    所谓架构就是“用最小的人力成本来满足构建和维护系统需求”的设计行为。

    软件开发一个核心特点是:“要想跑的快,先要跑的稳。”,在一个糟糕的架构上开发新的功能,只会越做超臃肿,越做越慢,越做越糟糕。

    对于每个软件系统,我们可以通过行为架构两个维护来体现它的实际价值。行为指的是软件的业务逻辑所带来的价值。而架构是与具体用户需求无关的,程序员往往忽略它的存在。

    在艾森豪威尔矩阵中,行为这个价值维度,是紧急的,但是并不总是特别重要。而架构的价值,是重要的,但是并不总是特别紧急。业务部分人研发人员常犯的错误就是,忽略架构价值的存在,导致新功能开发效率低下,系统运行、维护的成本急剧上升。平衡系统架构的重要性和功能的紧急程序这件事,是软件开发人员自己的职责。软件系统的可维护性需要由你来保护,这是你角色的一部分,也是你职责中不可缺少的一部分。

    软件架构师这一职责本身就应更关注系统的整体结构,而不是具体的功能和系统行为的实现。

    一、编程范式

    三个编程范式都是在1958-1968这十年间被提出来的,后续再也没有新的编程范式出现过。

    这本书讲架构,但却在第一章介绍了三种编程范式,这是因为这3种编程范式与软件架构的三大关注点不谋而合:

    多态是我们跨越架构边界的手段,函数式编程是我们规范和限制数据存放位置与访问权限的手段,结构化编程则是各模块的算法实现基础。

    而软件架构的三大关注点分别是:功能性(结构化编程)、组件独立性(面向对象)以及数据管理(函数式编程)

    面向对象编程到底是什么?业界在这个问题上存在着很多不同的说法和意见。然而对一个软件架构师来说,其含义应该是非常明确的:面向对象编程就是以多态为手段,来对源代码中的依赖关系进行控制的能力(依赖反转),这种能力让软件架构师可以构建出某种插件式架构,让高层策略性组件(业务架构代码)与底层实现性组件分离(如数据库存储,UI展示层,都不涉及到业务逻辑,算是一种辅助系统,可以随意替换),底层组件可以被编译成插件,实现独立于高层组件的开发和部署。

    这样做的意义在于,如果我们实现存储,则不需要import一个mysql包,将辅助组件的代码引入到业务逻辑当中。而是通过定义一个接口,让辅助组件实现其接口,实现存储功能。业务逻辑与辅助组件无关,则可以提高系统灵活扩展性,可以将mysql随意替换成其它任何存储。这就是软件架构师应该做的事情。

    二、设计原则

    SOLIC原则的主要作用就是告诉我们,如何将数据和函数组织成为类,以及如何将这些类链接起来成为程序。

    请注意,这里虽然用到了“类”这个词,但是并不是意味着我们将要讨论的这些设计原则仅仅适用于面向对象编程。这里的类仅仅代表了一种数据和函数的分组,每个软件系统都会有自己的分类系统,不管它们各自是不是将其称为“类”,事实上都是SOLID原则的适用域。

    SOLID原则讲的是如何拼装一个好的模块,而之后第三部分则是如何将拼装好的模块组建成运作良好的系统。这两部分都很重要。

    1。SRP 单一职责原则

    “任何一个软件模块都应该只对某一类行为者负责。”

    一个模块只应该有一个修改原因,不应该存在一个模块,因为多个不同部分的修改方,导致对其修改相互影响。

    2。OCP 开闭原则

    “设计良好的计算机软件应该易于扩展,同时抗拒修改。”

    也就是对扩展“开放”,对修改“关闭”。

    我们要通过修改给系统增加新的功能,而不是通过修改旧的代码。OCP是我们进行系统架构设计的主导原则,其主要目标是让系统易于扩展,同时限制其每次修改所影响的范围。

    实现方式是通过将系统划分为一系列组件,并且将这些组件间的依赖关系按层次结构进行组织,使得高阶组件(处理业务逻辑的核心组件)不会因为低阶组件(如存储等辅助模块,在分层的结构中,低阶组件是相对不重要的部分)被修改而受影响。

    3。LSP 里式替换原则

    用户依赖一种接口,并且实现该接口的类之前具有可替换性。

    如存储模块,定义了write(), read()接口,只要某个类实现了该接口,不管是mysql,还是redis,则他们都可以用作存储,并且相互替换。

    4。ISP 接口隔离原则

    “任何层次的软件设计,如果依赖了它并不需要的东西,就会带来意料之外的麻烦。”

    从源代码层次来讲,这样的依赖关系会导致不必要的重新编译和重新部署,对更高层次的软件架构设计来说,问题也是类似的。

    如系统S依赖框架F,框架F绑定了数据库D,如果D是包含F中不需要的功能,也必然是S中不需要的,而我们对D中功能的修改将会导致F需要重新部署,后者又会导致S的重新部署。更糟糕的是,D中一个无关功能的错误也可能导致F和S运行出错。

    5。DIP 依赖反转原则

    依赖反转原则告诉我们:如果想要设计一个灵活的系统,在源代码层次的依赖关系中就应该多引用抽象类型,而非具体实现。

    子原则1:要有稳定的抽象层。我们每次修改抽象接口的时候,一定也会去修改对应的具体实现。但反过来,当我们修改具体实现时,却很少需要去修改相应的抽象接口。所以我们可以认为接口比实现更加稳定。

    • 应在代码中多使用抽象接口,尽量避免使用那些多变的具体实现类。这条守则适用于所有编程语言,无论静态类型语言还是动态类型语言。同时,对象的创建过程也应该受到严格限制,对此,我们通常会选择用抽象工厂这个设计模式。

    架构师要做的与工程师不同的是,他只需关心定义好接口,而不需要关系接口的具体实现类。

  • 相关阅读:
    disabled
    C# 实例化顺序
    session问题
    Node js文件系统
    Node js WEB模块
    Node js GET POST请求
    Node js路由
    Node js函数
    Node js模块系统
    Node js 安装+回调函数+事件
  • 原文地址:https://www.cnblogs.com/gm-201705/p/12897890.html
Copyright © 2020-2023  润新知