• 架构整洁之道摘录


    软件架构

    软件架构规则和其他变量完全⽆关。
    软件设计的终极⽬标是⽤最⼩的成本来满⾜构建和维护系统的需求。
    程序设计重要的是软件架构的灵活性⽽不是先实现功能。
    软件系统的第⼀价值体系是系统⾏为,第⼆价值体系是系统架构

    编程范式

    结构化编程

    利⽤if/else while等替代goto的使⽤,结构化编程对程序控制权的直接转移进⾏了限制和规范。

    ⾯向对象编程

    利⽤多态限制⽤户对函数指针的使⽤,⾯向对象编程对程序控制权的间接转移进⾏了限制和规范。

    函数式编程

    函数式编程⼀般不可以修改变量的值,因此函数式编程对于变量的赋值进⾏了限制和规范。
    ⽤顺序结构、分⽀结构和循环结构就可以构造出任何程序。
    架构设计时,对于功能的降解拆分仍然很重要。

    ⾯向对象编程

    ⾯向对象编程是⼀种对真实世界进⾏建模的⽅式。
    封装性对于⾯向对象编程语⾔实际是削弱了。例如c++需要将变量定义在头⽂件中,java,c#由
    于没头⽂件⽽分不清申明和定义。
    完美的封装性存在于c语⾔中,struct定义在其他头⽂件,使⽤时前置申明好再使⽤。

    继承性

    继承特性在c语⾔中通过数据结构的内部变量的顺序控制以及强制类型转换实现。但是⾯向对象
    的编程语⾔还是提供了更加易⽤的使⽤⽅式,⽐如多重继承的实现以及隐式的转换。

    多态性

    多态性使⽤c语⾔的函数指针也可以实现,但是对于易⽤和安全上,⾯向对象编程语⾔还是更加优秀。
    ⾯向对象编程通过多态实现依赖反转,这样可以完全控制系统中源码的依赖关系。使得架构师可以构建出插件式架构,每个组件实现独⽴部署。

    不变性

    函数式编程语⾔的变量是不可变的。这主要是为了避免造成多线程、死锁、并发等等的问题。

    可变性隔离

    将可变的组件和不可变的组件进⾏分离,不可变的组件使⽤纯函数的⽅式来执⾏任务,期间状态不可更改。不可变的组件将通过⼀个或者多个⾮函数式组件通信的⽅式来修改状态。
    状态的修改⼀般使⽤事务型内存来保护可变变量,避免同步或者竞争状态的发⽣。
    ⼀个架构设计良好的应⽤程序应该将状态修改的部分和不需要修改的部分隔离或者独⽴成单独的组件,然后使⽤合适的机制来保护可变量。应该将逻辑尽可能的放在不可变组件中,可变组件中的逻辑越少越好。

    SOLID原则

    SRP:单⼀职责原则,每个软件模块有且只有⼀个被改变的理由
    任何⼀个软件模块应该只对⼀类⾏为者负责
    反⾯案例:⼀个⼯资管理程序的Employee类中包含三个函数calculatePay、reportHours、save,但这三个函数不属于⼀类calculatePay是财务部⻔制定、reportHours有HR使⽤、save由DBA制定。因此解决⽅法是将不同的⾏为者进⾏切分。

    OCP原则

    开闭原则,软件系统想要更加容易被改变的话,就需要允许增加代码来实现模块功能⽽⾮
    修改现有的代码。⼀个好的计算机软件应该易于扩展,抗拒修改。模块单向依赖,限制每次修改的影响范围。实现⽅式是通过将系统划分为⼀系列的组件,将这些组件按照层次结构进⾏划分,使得低层次组建的修改不会影响⾼层次组件。

    LSP原则

    ⾥⽒替换原则,软件最好遵守同⼀个约定,这样组件之间可以相互替换。
    ⼀个类依赖于另⼀个类的⽗类,这样⼦类不同,依赖的类不需要修改。
    LSP原则不仅仅适⽤于继承类型,它适⽤于所有有统⼀的接⼝,并且期望可以相互替换的接⼝。

    ISP原则

    接⼝隔离原则,设计中避免不必要的依赖。类与类之间依赖只涉及必要的接⼝,⽆关的接⼝
    不要依赖。

    DIP

    依赖反转原则,⾼层策略性的代码不应该依赖底层实现的代码,反之底层实现的代码应该依赖⾼层策略性的代码对于软件系统中经常变动的模块应该提供⼀个稳定的抽象接⼝。
    代码中多使⽤抽象接⼝,尽量避免使⽤那些多变的具体实现类。不要在具体实现类派衍⽣类。不要覆盖包含具体实现的函数。应避免今年在代码中写任何与具体实现有关的名字,或者其他容易变动的名字。但具体实现调⽤的组件也不是不能出现,⼀般⼀个模块中会有⼀个调⽤具体实现的main组件。

    组件

    组建是指在软件部署时的最⼩单元。⽆论采⽤哪种部署形式,设计良好的组件应该保持可以单独部署的属性。

    重定位技术

    重定位技术出现是为了解决函数需要通过源代码的形加载使⽤的代码中,并且地址固定。
    通过修改程序输出⼆进制⽂件的格式,由⼀个智能加载器加载到任意的内存。程序员需要指加载到的内存位置可重定位的代码中添加了⼀些记号,加载器在加载时会对这些制定的位置进⾏⼀些修改,⼀般是递增。这样的话,程序员可以⽤加载器来修改库函数加载的位置,并且还可以同时加载多个。

    链接加载器

    将库中的函数存储起来,⼀段程序调⽤了库中函数会被定义为外部引⽤,并将库中函数的名字称为外部定义,将两者链接起来。这样可以让程序员实现将程序分为多个可以被分别编译的代码段。
    但是对于代码量较⼤的程序来说,这个过程⾮常缓慢,因此只能将加载和链接分离。链接过程放到链接器中进⾏。
    现代程序由于内存和运算速度的⼤幅提⾼,链接过程已经⼤幅减少时间。
    组件的形式在动态链接⽂件加载的应⽤场景中⾮常典型。⽬前插件式的架构已经被⼴泛使⽤。

    组件聚合

    REP 复⽤/发布等同原则

    软件复⽤的最⼩粒度等同于发布的最⼩粒度。组建中的类与模块是密切相关的。

    CCP 共同闭包原则

    将可能同时修改以及有相同修改⽬的的类放在⼀个组件中。

    CRP 共同复⽤原则

    不要强迫⽤户⽤他们不需要的东⻄

    ⾯向服务架构

    将系统拆分为⼀个个服务,服务之间进⾏隔离。服务需要⽀持单独编译和部署。

    耦合的谬论

    即使拆分为每个服务,但是进程之间的通信以及共享资源还是会将服务耦合在⼀起。所以如果想做到真正的耦合需要考虑如何避免这些问题。

    独⽴部署的谬论

    数据形式和⾏为形式的耦合,仍然会导致独⽴部署的困难。

    横跨型变更

    ⽐如⼀个功能变更影响到许多的服务,这样也⽆法避免逻辑耦合的服务⼀起变更。
    解决横跨型问题的⼀个⽅案是采⽤⾯向对象的组件式的模型,将逻辑放在基类中,特定的功能放在⼦类中。
    服务边界不能代表系统的架构边界,服务内部的组件边界才是。

    测试也是⼀种系统组件

    可以将测试代码视为系统最外圈的程序。始终向内依赖,但其他组件不依赖它。
    为了防⽌系统组件的修改引起⼤量的测试程序改变,需提⾼系统的可测试性,原则就是尽量少的依赖多变的东⻄。

    整洁的嵌⼊式架构

    软件不会随着时间推移⽽磨损,但是硬件会,因此硬件变更时软件可能也需要随之变更。因此需要通过好的架构设计来避免固件更迭带来的软件不适配问题,延⻓代码的有效使⽤⽣命周期。
    嵌⼊式系统设计⼀般采⽤分层的⽅法。HAL是硬件抽象层,这⼀层将会给业务提供软件接⼝,因此会对硬件进⾏抽象。HAL的上层业务不需要知道底层硬件具体的实现细节,这样测试也可以通过HAL接⼝灌⼊值对软件接⼝进⾏测试。
    硬件绑定很深的代码,例如硬件⼚商提供的读写寄存器、IO中断等的代码需要限制在固件层。和软件隔离之上层业务才能做到更好的移植性和可测试性。
    软件和固件之间还需要操作系统层来防⽌上层代码和操作系统产⽣依赖。
    整洁的嵌⼊式架构会引⼊操作系统抽象层,OSAL兼容以前的接⼝,并且可以让应采⽤定义好的公⽤结构。OSAL还可以为应⽤提供⽬标平台外的测试⽀撑点,是的测试也不依赖操作系统。

    ⾯向接⼝编程与可替代性

    ⾯向接⼝的交互式使得服务的可替代性提⾼。⽬前的普适规则是使⽤头⽂件充当接⼝的定义,但这样的话就需要保证头⽂件内容只包括函数声明以及函数使⽤到的结体和变量。数据库
    数据库终究只是在硬盘与内存之间相互传输数据的⼀种⼿段⽽已,它真的可以被认为只是⼀个⻓期存储数据的、系统架构应该对磁盘本身的存在完全不关⼼。

  • 相关阅读:
    CocosCreator3.8研究笔记(三)CocosCreator 项目结构说明及编辑器的简单使用
    形参与实参
    FL Studio水果2023版本更新下载汉化教程
    在CIFAR-10数据集上构建ResNet-18模型(pytorch版)
    超详细整合SSM框架--(Spring + Spring MVC + MyBatis)
    shigen的一些shell脚本分享
    ARM汇编
    最大值和最小值之差达标的子数组数量
    修改CentOS默认mail发件人名称
    倍福TwinCAT3 NCI在NC轴界面中的基本配置和测试
  • 原文地址:https://blog.csdn.net/weixin_34764432/article/details/134087404