根据页面的渲染过程来编写代码结构。像:init -> getData -> processData -> bindevent -> report -> xxx 。 方法之间线性跳转,你大概也能感受这样代码弊端。随着页面逻辑越来越复杂,这条” 过程线“ 也会越来越长,并且越来越绕。加之缺少规范约束,其他项目成员根据各自需要,在” 过程线“ 加插各自逻辑,最终这个页面的逻辑变得难以维护。
最为实际的一种思考,也比较符合普通人的思维方式,它考虑的更多是实际的实现,从上而下的步步求精。不过缺点也比较明显,因为每一步都依赖它的上一步计算结果,导致整个功能为一个整体,牵一发而动全身,另外也限制了复用的可能性。
概述
组件就是将一段UI样式和其对应的功能作为独立的整体去看待,无论这个整体放在哪里去使用,它都具有一样的功能和样式,从而实现复用,这种整体部分的思想就是组件化。不难看出,组件化设计就是为了增加复用性,灵活性,提高系统设计,从而提高开发效率。与模块化类似,组件化是页面层次上,按照功能纬度来进行分割,从而达到隔离复杂度的目的。
康威定律:软件源代码的组织结构要与开发团队的组织结构尽量保持一致
目的
功能性降解拆分,我们可以将一个大型问题拆分为多个高级函数,再将高级函数拆分为多个低级函数,如此无限递归下去,层层分解,且每一个函数也可以通过结构化编程来分解,最终可以拆解成更小、可证明的函数单元。
但是形式化证明并没有发生,因为程序和数学还是不同,程序没办法被证明,只能够被证伪。我们写的单元测试,或者由测试人员进行的测试只能证明在当前环境是足够正确的。
禁用goto无条件转移语句
通用组件:
具有业务通用性,普适与一般的业务场景,为了复用而被创建,可被沉淀下来被多处使用,可以被单独维护和扩展。目前市面上开源的组件都是通用组件。
业务组件:
包含完整的功能逻辑,具备控制层职责,为高级策略组件,是稳定抽象层,整个功能中最稳定的部分,负责高级策略的定义,整体轮廓的搭建,及其他策略类的创建与配置。智能组件
原子组件:
业务组件中直接依赖的低级策略组件,作为业务组件的一部分存在,通过组合功能完成功能,只负责展示相关的职责。木偶组件
组件聚合原则
该原则告诉我们应该将哪些类放在一个组件中。
三大原则:
复用/发布等同原则(REP)
为复用性而组合
软件复用的最小颗粒度等于其发布的最小颗粒度,该原则从发布角度来指导我们构建组件,它告诉我们一个组件是由一组有关联的类或者模块组成,它们之间至少有一个共同的主题或者大方向。
共同闭包原则(CCP)
为维护性而组合
单一职责原则在组件层次上的应用(将由于相同原因被修改,且会同时修改的东西放在一起)
共同复用原则(CRP)
为避免不必要的发布而切分
接口隔离原则在组件层次上的应用,建议我们不要依赖不需要的类,保持组件的最小可用性,不要依赖不需要的东西。
三个原则彼此之间存在竞争关系,而软件架构就是在这三个原则进行取舍。
在项目早期,我们会牺牲一部分通用性,而偏向可维护性和最小可用性的指导原则来进行开发,在项目逐渐迭代的过程中,决策重心会逐渐往可复用性方向倾斜。CCP和CRP原则其实也存在对抗关系,是最小可用原则和保持更多可选项观点间的对抗,我们要具体针对项目工期,公司资源,团队大小,项目紧急程度来进行权衡。
组件耦合原则
该原则告诉我们管理组件之间的关系
无环依赖原则
组件依赖关系图中不应该出现环
程序员总希望可以单打独斗,可大多数情况下我们需要与其他人共同协作开发维护软件,当其他人的修改,很可能会影响到自己的功能。一觉醒来,我的功能却不能用了!
解决方案:
每周构建:周一到周四大家在自己分支上开发自己的功能,周五进行统一构建
好处:每周都有四天可以让大家放手工作
坏处:将风险延后到星期五,可能会频繁导致周六日加班
消除循环依赖:项目划分为一些组件,被单独负责,当组件变更,更改版本号后通知出来。其他依赖项目选择在合适的时机升级验证。
好处:组件变更不会立刻影响到其他团队,减少团队间的直接依赖,使得集成工作以一种小型渐进的方式进行。
坏处:项目架构难度高,需要控制好组件之间的依赖结构,消除依赖环。
如何消除依赖环:
依赖倒置:反转组件间的依赖关系
创建新组件:将两个组件相互依赖的部分提取出来,创建一个新的组件
组件结构图不是自上而下被设计出来的,他是当前项目真实状态的地图,随着项目变更不断的演变,不是项目的设计图,没办法在项目初期就确定下来。它的一个重要目标是隔离变化,识别出稳定高价值组件与易变组件,将它们隔离起到保护作用。
面向过程的开发,上层调用下层,上层依赖于下层,当下层剧烈变动时上层也要跟着变动,这就会导致模块的复用性降低而且大大提高了开发的成本。
面向对象的开发很好的解决了这个问题,一般情况下抽象的变化概率很小,让用户程序依赖于抽象,实现的细节也依赖于抽象。即使实现细节不断变动,只要抽象不变,客户程序就不需要变化。这大大降低了客户程序与实现细节的耦合度。
依赖关系必须要指向更稳定的方向,确保设计中容易变更的模块,不会被难以修改的模块所依赖
稳定性:只需要很小成本就可以改变状态是不稳定的,需要成本很高,那它是稳定的
不稳定性指标:依赖组件数/(依赖组件数+被依赖组件数)
一个组件抽象化程度应该与其稳定性保持一致
设计出哪些组件应该稳定,哪些组件应该不稳定
稳定的组件很难被修改,为了让组件既稳定又可以被修改,我们一般应用开闭原则,让组件保持一定的抽象,可以被扩展。
抽象程度:总类/抽象类
传统方式
好处
坏处
组件间进行通信的桥梁,附载到总线上的组件都可以相互通信,所以使用简单,可以很方便将消息从一个组件发送到另外一个组件。虽然这种方式使组件间解耦,但是大量使用必定造成在维护阶段代码难以维护。事件总线是一种一对多,或者多对多的关系,类似于广播,也是观察者模式的一种应用,在实际应用中不建议应用于组件间一对一的通信方式。
好处
坏处
帮助中大型单页应用,更好地管理组件外部状态,适用于一个组件影响另外一个组件数据的情况。
好处
坏处
好处
坏处
示意图
总结
父子组件通信、最小颗粒度组件内部通信使用传统方式,直接进行访问。
直接兄弟组件间通信,通过父组件进行调用
非直接兄弟组件间通信,通过全局状态管理
跨页面级组件间通信,通过事件总线
将系统切分为组件,并且安排好组件间的依赖关系,及通信方式。并且在设计中尽可能长时间的保留更多的可选项。
针对不同的团队,采用不同的架构,提高团队开发效率,降低团队维护发布成本。架构在其中起到协调作用,在开发效率与维护成本之间做取舍。
软件架构的主要目标是支撑系统的全生命周期,设计良好的架构可以让系统更利于理解,易于修改,方便维护,并且能轻松部署。终极目标就是最大程序员生产力,最小系统运营成本。
设计良好的架构可以帮助程序员更容易理解系统的运行过程,间接的帮助对系统的开发和维护。
设计良好的架构可以降低修改对其他部分的影响,降低实际的维护成本。
设计良好的架构可以使软件保持更好的灵活性,通常保持更多的可选项目,可以使得项目变更时降低成本。
我们的终极目标就是以最小的人力成本来构建和维护软件系统
软件系统应该从两个纬度来衡量它的价值,行为纬度和架构纬度。
行为价值:我们将产品文档翻译成具体的代码,使得程序可以按照文档描述运行起来,并且出现bug时候要进行调试修复。这是最直观的价值,且大部分人认为这是他们全部的工作。
架构价值:软件系统必须保持一定的灵活性,使其更容易被修改,这体现了软件上软的价值,而在机器上不容易被改变的部分,我们称为硬件。
哪个纬度更重要呢?通常人们可能会认为行为价值更为重要,一个软件系统如果不能正常的运行起来,那他的价值应该为0。但是对于一个仅仅在当前能够运行起来,但是却无法被修改的系统来说是无法持续产生价值的。相对应的,对于一个暂时无法运行,但是很容易被修改的系统而言,让它能够正常工作,应该是一件简单的事情。
行为价值是紧急但不重要的
架构价值是重要但不紧急
一个类被改变的原因只有一个,一个类只有一个职责可以提高类的可读性和可维护性,并且降低变更风险。如何划分职责,我们还要从需求和业务出发,识别出同一类型的职责。
对扩展开放,对修改关闭,是进行软件架构设计的指导原则,其主要目的是让系统便于扩展,同时限制每次修改影响的范围,实现方式是通过将系统划分为一系列的组件,并且组织依赖关系,使得高阶组件不会因为低阶组件的修改而收到影响。
任何基类出现的地方,一定可以被子类所替换,是指导接口与具体实现关系的设计原则。该原则一般被应用在软件架构层面,一旦违反了该原则,架构设计中就不得不增加大量复杂的应对机制
避免使用不需要的接口,类间依赖关系建立在最小的接口上,所以也叫做最小接口原则。ISP是单一职责原则在接口层面的体现,避免他的实现类破坏单一职责原则,提高其内聚性。ISP要求我们将接口设计的足够小,足够灵活,但是也不是越小越好,接口越小灵活度越高,但是系统复杂度也会越高,所以接口不是越小越好必须要有一个度。
上层模块不依赖底层模块依赖抽象,抽象不依赖细节,细节依赖抽象,针对接口编程。
在面向过程的开发中,依赖关系总是自上而下,上层模块依赖和调用下层模块,当下层模块剧烈变更时,上层模块总是会跟着改动,变化会从下向上传递。
在面向对象的开发中为了解决这个问题,遵循依赖倒置原则,构建稳定的抽象层,当细节变更了,也不容易引起抽象层的变更,这样客户端更稳定。
它要求对其他对象有最少的了解,所以又叫做“最小知道原则”,它主要的意义在于降低类之间的耦合,使系统模块更容易独立开来,只要类之间关系低耦合,才可以增强类的复用,降低类变更的分险。
怎么做到最小知道?1:只和直接的朋友(成员变量,方法的输入输出参数)交流,2:减少对朋友的了解。
迪米特法则目的把类编程一个肥宅,减少与外接的交流,限制类的活动范围。
设计模式中,门面模式和中介模式是实现迪米特法则的常见手段。