zoukankan      html  css  js  c++  java
  • 代码整洁之道-第11章-系统-读书笔记

    第 11 章 系统

      要将注意力放到代码组织的更高层面,才能得到整洁的代码。

    11.1 如何建造一个城市

      城市在没有一个人管理时,也能正常运转,是因为它能演化出恰当的抽象等级和模块。
      本章将讨论如何在较高的抽象层级—系统层级—上保持整洁。

    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 并使用类方面的机制来无损地组合其他关注面时,这种事情才会发生。
      无论是设计系统或单独的模块,别忘了使用大概可工作的最简单方案。

  • 相关阅读:
    高斯消元学习
    HDU 4596 Yet another end of the world(解一阶不定方程)
    Codeforces Round #318 div2
    HDU 4463 Outlets(一条边固定的最小生成树)
    HDU 4458 Shoot the Airplane(计算几何 判断点是否在n边形内)
    HDU 4112 Break the Chocolate(简单的数学推导)
    HDU 4111 Alice and Bob (博弈)
    POJ 2481 Cows(线段树单点更新)
    HDU 4288 Coder(STL水过)
    zoj 2563 Long Dominoes
  • 原文地址:https://www.cnblogs.com/zhangmiao14/p/10700867.html
Copyright © 2011-2022 走看看