谈到应用程序的层次,我们平时所说的层次有两种:逻辑的层次(layer)和部署的层次(tier)。这两种层次划分的目的是不同的,因此划分方式也有一些差异,能够为应用程序带来的好处也是不同的。
逻辑层次
逻辑层次(layer)划分的最重要的目的在于调整应用程序各部分之间的依赖关系。应用程序可以看作数据和业务规则的集合,这个集合通过用户界面与用户发生交互。如果不划分层次,或者只划分最简单的层次,系统的结构就会是这样:数据库处于系统的中心地位,在此之上建立用户界面,业务规则写在用户界面里。
这样做的问题在于:数据库作为应用程序的中心是不合适的,因为数据库只负责存储数据,而不能对数据做出解释(这是业务规则的任务),而业务规则分散在非中心的位置,零散的表达在用户界面中。一旦需求改变,业务规则必须随之改变,而业务规则是分散在各处的,我们就要四处寻找业务规则,进行修改。随着应用程序规模的扩大,这是一项非常艰难的任务。
为了解决复杂的依赖关系,我们创建了业务层。典型的逻辑分层结构就是:表示层->业务层->数据层。
建立业务层的方式可以非常简单:假如我们的应用程序要进行多项业务,我们分析这些业务的流程,找出这些流程中共同的部分,提取他们作为独立的过程,这就形成了最简单的业务层。这样,不同的界面之间就可以重用业务规则的代码,从一定程度上解决了依赖关系的不合理性。这是一种基于过程的方法。但是这样的方法有两个问题:
第一:他建立在对系统中的各种业务充分了解的基础之上,要正确的分析各个业务流程的细节,否则划分的流程和提取的共同过程必然有疏漏。但是在实际的开发过程中,我们往往一开始只了解业务的整体情况,对于其细节是逐渐了解的。并且对于一些隐含的关系,需要在开发的过程中才能认识到。有时候采用的一些具体的开发技术也会影响到某些业务的实现方式,进而对其流程带来影响。这样就为过程的分析带来很大的困难。
第二:基于过程的方法受到需求变化的冲击比较大。因为这样的分析方法,在实现的时候是基于业务的过程,而不是业务的目的。当系统中有业务需要变化的时候,会影响到其他具有共同过程的业务,业务之间的依赖是比较严重的,对需求变更的适应力差。
如果采用面向对象的方式,将业务进行抽象,概括实现这些业务需要使用的手段(例如需要更新数据表、向端口发送指令、保持业务进行的状态……),为所有的业务制定一个统一的框架,由这个框架决定业务执行的策略(如何限定业务的条件、分哪些步骤、失败后是重试还是放弃、何时向用户报告业务执行的进度……),框架不加区别的处理所有的业务。用户执行一个业务的时候,我们只要相应的创建一个业务对象,将这个对象置于框架内,监视执行的进度。这样,各个业务的实现就自然的隔离起来,依赖关系比较简单。同时应付需求变更也比较容易,独立的业务发生变更,只要修改一个类,如果多个业务的执行方式都要改变,则可以从框架上想办法。
如果为这个框架添加新的机制,使他不仅能够执行业务对象,还能够负责创建和销毁业务对象,就能够带来更强大的功能。业务框架就进而成为一个业务容器。当用户希望执行一个业务的时候,我们可以要求业务容器创建这个业务对象,业务容器可以交给我们一个业务对象的代理,隐藏真实的对象的位置。实际的执行过程可能是分布式的,容器为我们管理业务对象的事务性,保存业务执行的进度,必要的时候暂停业务,条件成熟后再重新开始,业务完成以后将对象销毁……这些我们都不必关心,由容器为我们做。
优化应用程序内部的依赖关系是划分逻辑层次的一个重要目的。层次间的调用机制可以采用逐层调用,也可以跨层调用,都是比较常见的方式。层次间的依赖的方向可以从上到下,也可以从下到上,都是合理的。需要小心的是,两个层次之间不可以产生互相的依赖,否则会破坏层次结构。比如有A和B两个层次,要么A依赖B,要么B依赖A,不可以互相依赖。如果必须有层次间互相调用,则必须采用某些方法转换其中一个方向上的依赖关系(可以采用的方式诸如delegete,interface,消息,事件等等)。多个层次之间也不应该出现循环的依赖关系。
部署层次
部署层次(tier)划分的目的在于增强应用程序部署的灵活性,更大限度的利用硬件环境资源。应用软件对环境有多方面的需要,比如数据存储的模块需要服务器有较强的稳定性、容错性、较高的IO效率,事务处理模块则需要运算迅速的处理器和较大的内存,而管理终端则需要有较强的表现能力。通常很难有这样的服务器满足多方面的需要,因此我们需要将应用程序分为多个层次,部署在不同的位置。
同时,在某些应用程序中需要访问一些敏感的数据,这些数据是不允许或者不可能集中到本地的,这都要求程序在部署方面增加灵活性。
从系统性能的角度说,应用程序在部署时划分合理的层次,可以增加系统的扩展性。当现有的硬件条件不满足需要的时候,可以通过改变部署方式,提高系统的性能。
比如使用一个服务器的时候可以满足100个并发业务,当并发业务达到150的时候,可以通过增加服务器满足这个要求。这就需要应用程序使用多层次的结构,缺少必要的层次是不可能进行这样的扩展的。
再如,我们需要在一个支持20个连接的数据库上建立一个支持100个客户端的系统,这时就必须建立独立于数据库的连接保持层次。
应用程序的部署层次有一些常用的模式,比如B/S结构,C/S结构,客户端/应用服务器/数据服务器结构,都是一些常用的层次划分模式。具体采用什么样的模式要看应用程序的需要,假如应用程序需要频繁的访问客户本地资源,要求较高的效率,或者要求具有强大的离线处理功能,则采用B/S结构就是不合适的,C/S结构是一个比较好的选择。如果需要在部署配置方面比较简单,又要面对不同的客户端环境,那么B/S结构就是很好的模式。如果需要建立服务器组来平衡负载,那么应用服务器则是必不可少的。
部署层次和逻辑层次有一定的关系,比如说一个程序从逻辑上不分层次,那么在部署的时候要分为多个层次就很难了。但是这两种层次并不是必须严格一致的,比如说,采用“客户端/应用服务器/数据服务器”的部署层次,并不一定意味着客户端对应着表示层,应用服务器端对应着业务层,数据服务器就一定是数据层。不能把对应关系绝对化,这两个角度划分的层次没有严格的对应关系,他们是为了达到不同的目的而划分的。
例如:在一些软件系统中,将业务逻辑以存储过程的形式直接写在数据库中。这样的系统中,业务层与数据服务器是相对应的。这样的业务层如果处理的好,一样具有清晰简洁的逻辑,维护起来也十分方便。但是这样显然会在部署上失去灵活性。
现实的问题
很多人在软件设计中常有这样的体会:在设计一个软件体系架构的时候,为了能够简化软件系统的逻辑复杂性,经常采用的手段是将一个大的系统分为若干层次。常用的方法是:分解一个庞大系统的各个功能,将其分别部署在多个部件中,每个部件只完成单一的功能,以减弱部件的复杂程度。部件之间采用一些弱耦合方式进行通信,比如消息、TCP连接等方式。
但是实际的开发和维护过程中,却发现这样并没有减轻系统的复杂程度。部件尽管已经从物理上分离,运行在不同的线程、进程、服务器上,但是部件之间在逻辑上却有着强烈的耦合关系,部件之间的接口有着时间、空间上的复杂交错关系,调用者需要依照复杂的逻辑次序,调用被调用者的接口,保存各次调用之间的状态,深入到对方的业务过程细节中。系统的结构没有因为层次而变得简洁,反而更加的复杂。一个业务的变更会在多个层次中造成影响,变更的代价更加巨大,没有起到层次模型应有的作用。
造成这个后果的原因在于,设计者混淆了逻辑层次和部署层次之间的区别。如果以减小系统的复杂程度为目的,首要的一点是从逻辑上分开层次,让各层次对业务的处理处于不同的抽象级别。为业务行为建立合理的抽象层次,而不是简单的让各个部件执行业务的某个阶段,也不是简单的让部件甲执行ABC业务,部件乙执行XYZ业务,这是逻辑层次的关键。做到了这一点,才能减小系统的复杂程度。错误的逻辑层次划分,只能使系统变得更加复杂。而盲目的划分部署层次,则是与初始的目的毫无关系的,也只能增加复杂性。
部署层次划分的作用并不在于简化系统逻辑结构,而是增加系统对环境的适应力。
逻辑层次的设计和部署层次的设计在软件开发中经常是交织在一起进行的,他们互相制约,互相影响,有共同的特征。但是这两种层次划分方式有着本质上的不同,存在着对立的方面,需要开发者在设计的时候进行协调,这也是需要注意的。
应用软件的层次划分
最新推荐文章于 2024-08-26 03:41:35 发布