三、架构的评价指标
1. 财务角度
在传统的财务核算机制中,系统或者产品的开发通常属于成本中心,对于IT企业来说,电脑设备,办公室等属于沉没成本,而IT人员的工资属于可变成本,是成本的核心部分,为了控制成本,就需要减少项目的开发时间。
因此架构一个重要的关注点在于控制开发成本和维护成本,通常讲维护成本是开发成本的3倍。降低开发成本核心,在于提高效率、提高适应需求变化的能力。
2. 技术角度
技术角度评估架构很难说。
四、架构的技术层面
(一)基础手段
提高开发效率和品质的基本手段是分解——即充分的分离系统中不同的关注点,好处不用说了,可以并发的工作,每个人面对的问题都简单而容易操作。而与分解对应的集成,只有提供了好的集成能力,分解才成为现实,而只有分解了,才能清晰的提供业务更多适应性。
分解和集成的手段分为编程语言和技术框架两个层面。所谓语言就是强框架,而框架就是弱语言。
A. 开发语言
现代面向对象的语言提供如下能力:抽象和派生能力,以及接口隔离能力。实际提供两种分解和集成能力:
(1)把逻辑分解在两个层次中,而通过继承的方式把两个部分集成在一起。
(2)把逻辑的外观和实现分解在两个地方,而通过接口实现的方式把两部分集成在一起。
另一种语言AspectJ或者C#语言2.0之后提供的特性:把流程逻辑,分解在不同的地方,而通过签名匹配,利用代码生成的方式来把几部分集成在一起。
B. 应用框架
然而语言提供的集成能力,毕竟底层,而且有限,扩展起来也格外小心。因而技术框架提供另外的集成能力就格外重要:
(1)对象关联关系的分解和集成,如Spring提供容器管理能力
依赖注入对于依赖关系是适合的,对于服务间,技术层次间都是适合的(因为无状态);但对于聚合(整体和部分)的关系——主要是领域模型(有状态的)——则不合适;
(2)模块间关联关系的分解和集成,如OSGi,ESB等
(3)流程逻辑的分解和集成,如Spring Web Flow以及jBPM。
(4)不同系统的类型分解和集成,如Spring利用动态代理提供的Exporter模式。
(5)模式的封装集成,设计应当是面向服务的设计,但是服务的暴露方式以及模式可以有很多种,比如API,Web Service,RMI,以及Command模式,Event模式等,框架应该利用动态代理等技术对于这些服务暴露方式,模式进行封装。
C. 分析设计
设计中涉及到的组合方式,包括类(接口)组合,继承组合以及产生组合三种。三种组合各有优缺点,设计时适应不同场合。这就涉及到现有面向对象的设计粒度:类(第一公民)和方法(二等公民)。
类(接口)组合实际上复用的是类一级粒度的设计,而继承组合本质上是一种有方向的组合,复用的方法一级粒度的设计,提供与或非的逻辑操作。而产生组合,例如AspectJ,也是在方法一级粒度的设计复用。
因为继承组合复用在方法一级的粒度上,因而其更适合存在嵌入式,最低粒度的差异性的设计中,借助于虚拟机的支持,无需额外工作。而类(接口)在类一级上,更适合在更高一级的逻辑复用上;其实不一定需要接口,普通的类也可以,但是在这一级粒度的差异性替换,采用接口优于类,因此称为类(接口)组合;接口是类(接口)组合的编码需要;对于接口一级,需要通过框架的集成和适配来提供差异性的设计。产生组合其实也是在方法一级,不过更关注于广泛的横切面,同时由于现有的语言对它的支持不同,Java需要额外的编译器,而.Net则是在内置编译器上支持。
更高一级的组合是组件组合。对于组件边界的设计,遵从两点:严格把关设计和代码优先。接口优先的设计通常导致成本太高,实践中会导致开发人员在项目的进度压力下把代码写在不合适的地方。
D. 开发方式
常见的开发方式可以归结为3类:开发式编程(Programmatic programming),声明式编程(Declarative programming)和产生式编程(Generative programming)。
开发式编程 | 声明式编程 | 产生式编程 | |
开发手段 | 编码。 如:Java, C# | 解析。 如:ANT(spring等的xml不一样,它们是静态描述型的,不那么verb) | 生成。 如:AOP(AspectJ),DSL(Drools) |
开发性质 | 聚合 | 声明 | 组合 |
代表事物 | 接口 | N/A | DSL 自然语言的表达能力很强大,虽然说有时具有二义性,但是在特定领域下是确定的,既然是讲DSL,那都是特定领域相关的,一定是明确的。 |
基础设施 | 解析器; 编辑器,如jbpm; 元模型; | 生成器; 正统的需要编辑器; 元模型 | |
开发方式 | 自上向下,声明式编程是解析概念,用统一的概念来理解,把不同差异性交由具体程序解析; 编辑器生成的是xml文件,将由框架程序解析; 声明式是粗粒度的(不能直接比较大小,定义的是无差异性的概念); | 自底向上,产生式编程用的思路是组合概念(用小粒度的概念组合生成大粒度的概念); 产生式生成程序代码,不做解析运行; 产生式是相对细粒度的; |
E. 小结
通常语言作为架构的基础,语言的设计带来的好处远远高于框架和模式,但其引入和更换也是有巨大风险的;而通过提供强大的框架能力,框架尽可能多的完成技术问题,并通过元数据,模式以及约定降低业务和框架的耦合。避免因为框架升级带来不必要的成本。
Meta Programming的最高层次是语言级别直接解决,比如,Smalltalk, Ruby, Python, 还有其他Reflection支持的非常好的语言。甚至STL等template技术,也可以算作语言级别。 Code Generation 是最低级别的Meta Programming解决方案,技术含量也最低。这个级别必须超越,才能够真正达到质变,完全跳出概念炒作的层次。
从技术手段上,提高开发效率的另外两个手段是代码生成和类库引用。但代码生成和类库引用,都只解决了逻辑的分解能力,没有提供集成能力,所以一般情况下需要提供框架集成,尤其代码生成需要在系统的最外层,避免集成带来的问题。
代码生成也没有那么坏,关键在于生成什么,如果是生成结构性的代码,由于往往不是最终的产物,就存在同步维护问题;同时这种代码是大都可以用template完成的。
但如果生成的是功能性代码,这类代码是最终执行代码,那么通常就把用于设计的代码看作是最终产物,最明显的例子是DSL。