体系结构设计建议

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/HermitSun/article/details/102289109

内容来自《Software Architecture in Practice》,尽量遵守原文,但很多部分采(jia)用(dai)意(si)译(huo)。

以下只是建议,而非强制要求;即使没有做到下列建议中的任何一点,也并不代表体系结构设计一定会出现致命失误。但是这些可以作为设计中的参考(或者说警戒线)。

过程建议

  1. 保持概念完整性和技术一致性。这意味着需要加强设计者之间的沟通,或者极端一点,整个系统由一个人进行设计。但在做到这一点的同时,要避免设计脱离实际,导致无法实现。

    这一点来自Brooks的《人月神话》。前一点很好理解,概念的不完整会导致沟通成本的急剧上升,严重的话甚至会无法集成,造成整个项目的失败。但后一点不太好理解,我个人觉得,作者想表达的是,过分追求设计和技术的一致,导致技术无法落地,最后也会失败;这一点往往出现在技术水平较低的团队里。

    比如,主系统决定采用Vue开发,然后就拍脑袋决定所有子系统都用Vue开发,但有些业务场景Vue是不太合适的(移动端当中重量级的应用场景,比如混合应用开发,Vue本身专注于视图,比较轻量,在这方面比较乏力,缺少完善的解决方案),如果硬要上必须进行改造,但技术积累不足,无法落地,就会导致项目的失败。同样的,如果为了刻意追求某个pattern,在不合适的场景下生搬硬套而不加以改造,也会导致失败。

    为什么我想到了马谡……

  2. 体系结构设计应该基于明确的质量属性需求。

    强调质量属性在体系结构设计中的重要作用。同时,我们也应该意识到,提高一个质量属性往往意味着降低另一个质量属性,需要进行权衡。Tradeoffs always occur.

  3. 体系结构应该通过视图(view)进行文档化。视图应当能够回应最重要的利益相关者(stakeholder)的关切,以获取他们对项目进度的支持。

    原文:The views should address the concerns of most important stakeholders in support of the project timeline.

    如果利益相关者没法感受到项目进度,就很难让他们全力支持。因为软件系统固有的复杂性,并且不同的利益相关者的关注点是不同的,整个系统很难通过一张图来刻画,就需要有多个视图来从各个方面展示系统,比如著名的4 + 1视图:过程视图、逻辑视图、开发视图、物理视图、用例。

    体系结构由结构组成,视图是结构的表现形式。结构就是结构(元素 + 关系,但本身是个抽象概念),视图是将结构实例化了。或者说,结构是体系结构在特定上下文(context)中的一个快照(snapshot),类似于胶卷;而视图是把结构可视化之后呈现出来,类似于冲洗之后的照片。

  4. 体系结构应该持续进行评估,以确认能满足质量需求。

    这里的“持续”指的不是一直进行,而是指在合适的时间进行,并且贯穿整个生命周期。

    因为设计可能会过时,并且每次变更都可能对整个架构产生影响,所以需要持续进行评估。此外,我觉得这一点还有螺旋模型的意思在里面:如果已经可以预见不能满足质量需求,就应当及时止损。

  5. 体系结构应该支持增量集成以尽早发现问题,避免后期的大爆炸集成(大爆炸集成一般不会成功)。一种实践方案是使用骨架系统(skeletal system)。骨架系统指的是已经搭建好的,能够完成内部交互并且已经实现了最小功能(minimal functionality)的系统。

    我感觉骨架系统就是我们平时所说的“项目脚手架”,比如vue-cli和create-react-app。注意,是最小功能,而非最少功能。最少功能一般是满足需求规格,能够正常交付的最少功能,类似于演化模型中的第一个迭代实现的功能。

    越早发现和解决问题,成本越低。

结构建议

  1. 体系结构的模块应该符合信息隐藏和关注点分离的原则。

    模块本身就应该符合这两条,不然叫什么模块呢?

  2. 尽量采用成熟的解决方案,除非需要解决的问题没有先例。

    一般来说,自己开发的方案,比起经过业界打磨的成熟方案,总是存在差距的,至少在优化上有所不足。除非,自己就是“业界”……大厂请自动忽略。

  3. 体系结构不应当依赖于某个商业产品或工具的特定版本。如果必须要依赖于特定版本,那么要把它单独分离出一个结构以应对变更。

    要依赖于抽象,而不是依赖于具体。

  4. 提供数据的模块和使用数据的模块应该分离。

    这样可以实现解耦。如果提供和使用数据的是同一个模块,一旦发生变更,两边都要修改;分开之后就只需要修改一个了。

  5. 模块(module)和组件(component)没有一一对应关系。比如,一个并行的系统,可能有多个组件同时运行,但这些组件都是由同一个模块构建的。

    模块是静态的、开发时的概念,组件是动态的、运行时的概念,从一开始就不一样,自然也没有必然的联系。如果举一个更极端的例子,在一个使用map-reduce pattern的系统中,只有一个模块(因为只有一个职责,就是完成这个计算任务),但在运行时会有很多个component分配到不同的计算节点上。

  6. 应该编写每个进程,这样即使是在运行时,也可以轻松更改它在特定处理器上的分配。

    原文:Every process should be written so that its assignment to a specific processor can be easily changed, perhaps even at runtime.

    我觉得,不显式分开编写也就没有进行分配的机会吧。

    An application is required to perform a variety of tasks of varying complexity on the information that it processes. A straightforward but inflexible approach to implementing an application is to perform this processing as a monolithic module. However, this approach is likely to reduce the opportunities for refactoring the code, optimizing it, or reusing it if parts of the same processing are required elsewhere within the application.

  7. 体系结构只应该具有少量的组件交互方式。也就是说,系统应该始终用同样的方式做同样的事。这样有助于提高可理解性,缩短开发时间,增强可靠性;同时提高了可修改性。

    再简单一点,大概就是尽量用同样的方式去实现同样的功能。这应该是之前提到的概念完整性的一个细化。如果同样的功能有不同的实现方式,很容易让人迷惑,使用提高了可理解性。相当于复用,缩短了开发时间。之前的同样的功能已经经过相对完善的测试,能够可靠地使用,增强了可靠性。至于提高了可修改性,这个马上说。

    同样的方式去实现同样的功能(和复用)并不代表直接复制粘贴,而是抽出可复用的核心部分,然后其他部分都依赖于这个部分。这是allocation patterns中的一种(platform pattern);所以从某种意义上,也可以提高可修改性。

  8. 体系结构应该包含一组特定(并且较小)的资源竞争区域,并且应该明确指定和维护其解决方案。举例来说,如果网络利用率是一个关注点,那么应该给每个开发组提供指导,以达成最小的网络通信量。

    原文:The architecture should contain a specific(and small) set of resource contention areas, the resolution of which is clearly specified and maintained.

猜你喜欢

转载自blog.csdn.net/HermitSun/article/details/102289109