第 11 章 系统
要将注意力放到代码组织的更高层面,才能获得整洁的代码。java
11.1 如何建造一个城市
城市在没有一我的管理时,也能正常运转,是由于它能演化出恰当的抽象等级和模块。
本章将讨论如何在较高的抽象层级—系统层级—上保持整洁。web
11.2 将系统的构造与使用分开
首先,构造与使用是很是不同的过程。
软件系统应将启始过程和启始过程以后的运行时逻辑分离开,在启始过程当中构建应用对象,也会存在相互缠结的依赖关系。
每一个应用程序都该留意启始过程。将关注的方面分离开,是软件技艺中最古老也最重要的设计技巧。
延迟化初始化/赋值(获取对象时,若是对象为空则建立对象,反之则直接返回对象)的好处是在真正用到对象以前,无需操心这种架空构造,启始时间也会更短,并且还能保证永远不会返回 null 值。坏处是获得了对象及其构造器所需一切的硬编码,不分解这些依赖关系就没法编译,即使在运行时永不使用这种类型的对象;在测试中,因为构造逻辑与运行过程相混杂,就必须测试全部的执行路径,在全局中也没法确保初始化的值是正确的。有了这些权责,说明方法作了不止一件事,这样就略微违反了单一权责原则。
仅出现一次的延迟初始化不算是严重问题。不过,在应用程序中每每有许多种相似的状况出现。因而,全局设置策略(若是有的话)在应用程序中四散分布,缺少模块组织性,一般也会有许多重复代码。
若是咱们勤于打造有着良好格式而且强固的系统,就不应让这类就手小技巧破坏模块组织性。对象构造的启始和设置过程也不例外。应当将这个过程从正常的运行时逻辑中分离出来,确保拥有解决主要依赖问题的全局性一向策略。编程
11.2.1 分解 main
将构造与使用分开的方法之一始将所有构造过程搬迁到 main 或被称之为 main 的模块中,设计系统的其余部分时,假设全部对象都已正确构造和设置。
控制流程很容易理解。 main 函数建立系统所需的对象,再传递给应用程序,应用程序只管使用。注意看横贯 main 与应用程序之间隔离的依赖箭头的方向。它们都从 main 函数向外走。这表示应用程序对 main 或者构造过程一无所知。它只是简单地期望一切已齐备。
设计模式
11.2.2 工厂
有时应用程序也要负责什么时候建立对象。
可使用抽象工厂模式让应用自行控制什么时候建立对象,但构造的细节却隔离于应用程序代码以外。
缓存
11.2.3 依赖注入
有一种强大的机制能够实现分离构造与使用,那就是依赖注入(Dependency Injection DI),控制反转(Inversion of Control,IoC)在依赖管理中的一种应用手段。控制反转将第二权责从对象中拿出来,转移到另外一个专一于此的对象中,从而遵循了单一权责原则。在依赖管理情景中,对象不该负责实体化对自身的依赖。反之,它应当将这份权责移交给其余“有权利”的机制,从而实现控制的反转。由于初始设置是一种全局问题,这种受权机制一般要么是 main 例程,要么是有特定目的的容器。
调用对象并不控制真正返回对象的类别(固然前提是它实现了恰当的接口),但调用对象仍然主动分解了依赖。
真正的依赖注入还要更进一步。类并不直接分解其依赖,而是彻底被动的。它提供可用于注入依赖的赋值器方法或构造器参数(或两者皆有)。在构造过程当中, DI 容器(构造器容器)实体化须要的对象(一般按需建立),并使用构造器参数或赋值器方法将依赖链接到一块儿。至于哪一个依赖对象真正获得使用,是经过配置文件或在一个有特殊目的的构造模块中编程决定。
但延后初始化的好处是什么呢?首先,多数 DI 容器在须要对象以前并不构造对象。其次,许多这类容器提供调用工厂或构造代理的机制,而这种机制可为延迟赋值或相似的优化处理所用。安全
11.3 扩容
与物理系统相比软件系统比较独特。它们的架构均可以递增式地增加,只要咱们持续将关注面恰当地切分。
没有恰当的切分关注面,业务逻辑与容器紧密耦合,隔离单元测试很困难,也会致使冗余类型的出现。
横贯式关注面
持久化之类关注面倾向于横贯某个领域的自然对象边界。会想用一样的策略来持久化全部对象(例如,命名约定采用一致的语义)。
原则上,能够从模块、封装的角度推理持久化策略。但在实践上,却不得不将实现了持久化策略的代码铺展到许多对象中。用术语“横贯式关注面”来形容这类状况。一样,持久化框架和领域逻辑,孤立地看也能够是模块化的。问题在于横贯这些领域的情形。
实际上,EJB(Enterprise Java Bean,JavaEE 中面向服务的体系架构的解决方案)架构处理持久化、安全和事务的方法要早于面向方面编程(aspect-oriented propramming,AOP),而 AOP 是一种恢复横贯式关注面模块化的普适手段。
在 AOP 中,被称为方面(aspect)的模块构造指明了系统中哪些点的行为会以某种一致的方式被修改,从而支持某种特定的场景。这种说明是用某种简洁的声明或编程机制来实现的。
以持久化为例,能够声明哪些对象和属性(或其模式)应当被持久化,而后将持久化任务委托给持久化框架。行为的修改由 AOP 框架以无损方式在目标代码中进行。架构
11.4 Java 代理
Java 代理适用于简单的状况,例如在单独的对象或类中包装方法调用。然而,JDK 提供的动态代理仅能与接口协同工做。对于代理类,你得使用字节码操做库,好比 CGLIB、ASM 或 Javassist。
代码量和复杂度是代理的两大弱点,建立整洁代码变得很难!另外,代理也没有提供在系统范围内指定执行点的机制,而那正是真正的 AOP (面向方面编程)解决方案所必须的。 框架
11.5 纯 Java AOP 框架
幸运的是,编程工具能自动处理大多数代理模版代码。在数个 Java 框架中,代理都是内嵌的,如 Spring AOP 和 JBoss AOP 等,从而可以从纯 Java 代码实现面向方面编程。在 Spring 中,你将业务逻辑编码成旧式 Java AOP 对象。POJO (Plain Ordinary Java Object,简单的 Java 对象,实际就是普通 JavaBean。)自扫门前雪,并不依赖于企业框架(或其余域)。所以,它在概念上更简单、更易于测试驱动。相对简单性也较易于保证正确地实现相应的用户故事,并为将来的用户故事维护和改进代码。
使用描述性配置文件或 API ,你把须要的应用程序构架组合起来,包括持久化、事务、安全、缓存、恢复等横贯性问题。在许多状况下,你实际上只是指定 Sprint 或 Jboss 类库,框架以对用户透明的方式处理使用 Java 代理或字节代码库的机制。这些声明驱动了依赖注入(DI)容器,DI 容器再实体化主要对象,并按需将对象链接起来。模块化
11.6 AspectJ 的方面
经过方面来实现关注面切分的功能最全的工具是 AspectJ 语言,一种提供 “一流的” 将方面做为模块构造处理支持的 Java 扩展。在 80% ~ 90% 用到方面特性的状况下,Spring AOP 和 JBoss AOP 提供的纯 Java 实现手段足够使用。然而,AspectJ 的弱势在于,须要采用几种新工具,学习新语言构造和使用方式。函数
11.7 测试驱动系统架构
先作大设计能够理解为一开始就设计好一切实现的方式,先作大设计(Big Design Up Front,BDUF)在必定程序上会阻碍改进,由于心理上会抵制丢弃既成之事,也由于架构上的方案选择影响到后续的设计思路。
固然,这不是说要毫无准备地进入一个项目。对于总的覆盖范围、目标、项目进度和最终系统的整体架构,咱们会有所预期。不过,咱们必须有能力随机应变。
最佳的系统架构由模块化的关注面领域组成,,每一个关注面均用纯 Java (或其余语言)对象实现。不一样的领域之间用最不具备侵害性的方面或类方面工具整合起来,这种架构能测试驱动,就像代码同样。
11.8 优化决策
模块化和关注面切分红就了分散化管理和决策。
延迟决策至最后一刻也是好手段。它让那个咱们可以基于最有可能的信息作出选择。提早决策是一种预备只是不足的决策。若是决策太早,就会绝少太多客户反馈、关于项目的思考和实施经验。
拥有模块化关注面的 POJO 系统提供的敏捷能力,容许咱们基于最新的知识作出优化的、时机恰好的决策。决策的复杂性也下降了。
11.9 明智使用添加了可论证价值的标准
有了标准,就更易复用想法和组件、雇用拥有相关经验的人才、封装好点子,以及将组件链接起来。不过,创立标准的过程有时却漫长到行业等不及的程度,有些标准没能与它要服务的采用者的真实需求相结合。
11.10 系统须要领域特定语言
DSL(领域特定语言)是一种单独的小型脚本语言或以标准语言写就的 API ,领域专家能够用它编写读起来像是组织严谨的散文通常的代码。
优秀的 DSL 填平了领域概念和实现领域概念的代码之间的“壕沟”,若是你用与领域专家使用同一种语言来实现领域逻辑,就会下降不正确地将领域翻译为实现的风险。
DSL 在有效使用时能提高代码惯用法和设计模式之上的抽象层次。它容许开发者在恰当的抽象层级上直指代码的初衷。
领域特定语言容许全部抽象层级和应用程序中的全部领域,从高级策略到底层细节,使用 POJO 来表达。
11.11 总结
系统也应该时整洁的。侵害性架构会湮灭领域逻辑,冲击敏捷能力。当领域逻辑受到困扰,质量也就堪忧,由于缺陷更易隐藏,用户故事更难实现。当敏捷能力受到损害时,生产力也会下降,TDD(Test-Driven Development 测试驱动开发,是敏捷开发中的一项核心实践和技术,也是一种设计方法伦)的好处遗失殆尽。 在全部的抽象层级上,意图都应该清晰可辨。只有在编写 POJO 并使用类方面的机制来无损地组合其余关注面时,这种事情才会发生。 不管是设计系统或单独的模块,别忘了使用大概可工做的最简单方案。