软件项目实训及课程设计指导——如何实现面向对象的系统架构设计
1、什么是面向对象的软件应用系统的架构设计
从软件应用系统的架构设计师的角度来看,所谓的软件应用系统的系统架构就是一套构建软件应用系统的整体结构的各种设计准则。通过这套设计准则,架构设计师可以把一个复杂的软件应用系统划分为一些相对独立的子系统,并在对各种繁杂的技术应用要求和功能实现中获得最优雅、简洁和合理的解决方案。
2、在软件应用系统的架构设计中要综合应用OOP、AOP和SOA相关的设计思想
(1)面向对象(OOP)和面向方面(AOP)的设计思想
由于与面向对象(OOP)和面向方面(AOP)开发有关的各种设计思想和具体的实现技术都是解决软件应用系统的内部结构设计方面的问题,并且面向方面(AOP)的设计思想弥补了面向对象(OOP)设计思想在实际软件应用系统开发应用中所存在的相关缺陷——因为面向对象(OOP)的编程技术不能实现软件应用系统中的核心关注点与横切关注点的相互分离,而面向方面(AOP)的编程思想正是为了解决这个问题而提出的。因此,在软件应用系统的设计、开发实现中应该要综合应用面向对象(OOP)的编程技术和面向方面(AOP)的编程技术。
在如下示图中的程序代码中夹杂有大量的日志功能实现的程序代码,导致日志功能实现的程序代码水平地散布在所有对象的层次中----在控制层、业务层和数据访问层都需要这样的日志功能实现,并且而与它所散布到的对象的核心功能毫无关系。而对于其他类型的代码,如安全性、异常处理和透明的持续性也是如此,这种散布在各处的无关的代码被称为横切(cross-cutting)代码。在用面向对象(OOP)设计方法中,它将会导致大量程序代码的重复,而不利于各个功能模块的重用。
![93f04123c22ee77fe80679667d405b4f.png](https://i-blog.csdnimg.cn/blog_migrate/5bc6d78938733c6f32ca2d073ab59e85.jpeg)
(2)面向服务(SOA)的设计思想
但面向服务(SOA)的设计思想和具体实现的技术更多的是解决软件应用系统的外部结构设计方面的问题——也就是如何让软件应用系统之间能够更好地整合和互联以实现更高效率的重用、并能够快速地满足企业多变的业务需求。
如下示图为基于面向服务(SOA)的设计思想的轻量级Web Service组件技术的系统架构示图,各个Web服务组件通过SOAP协议进行消息的通讯。
![f488fdb25aaf1c2cf93ad96e43518a2d.png](https://i-blog.csdnimg.cn/blog_migrate/6650d3ba512c3f98d077f350992d6f38.jpeg)
Web Service组件技术从本质上讲是放置于Web站点上的可重用组件,WebService便是基于网络的、分布式的模块化组件,它执行特定的任务,遵守具体的技术规范,这些规范使得Web Service能与其他兼容的组件进行互操作;Web Services的概念实际上就是对诸如RMI、COM和CORBA等现有面向服务的技术的扩展;Webservice平台是一套标准,它定义了应用程序如何在Web上实现互操作性,主要是用于解决在不同的系统平台下的应用的协同——如J2EE平台和VS.Net平台之间的信息共享访问。
3、明确软件应用系统的架构设计的设计目标
软件系统的架构设计师不仅要考虑软件系统的整体结构方面的设计工作以及软件系统所应该具有的功能,还要关注整个软件系统的可用性、可重用性和可扩展性以及可靠性、安全性等相关方面的技术实现问题,以期望能够达到高内聚低藕合的系统架构设计目标。
4、区分面向过程的设计方法和面向对象的设计方法之间的差别
(1)面向过程的设计方法
决定软件应用系统的体系架构所常用的设计方法主要为面向过程的设计方法和面向对象的设计方法,而面向过程的设计方法由于是建立在三种能够构成结构化程序的逻辑构造元素方面——顺序、选择和重复;并且面向过程的设计方法采用的是"自顶向下、逐步精化"的经典瀑布式设计方法,而由于瀑布式的设计方法是"自顶而下"顺序进行的,这就要求系统的架构设计者在系统设计之初就需要对软件系统中所应该要解决的各个方面的问题(也就是系统的需求)有全面的、周密的了解。
这样的设计方法在软件系统的功能比较复杂和需求多变的企业级的应用系统设计和开发实现中,将会存在有一定的难度。
(2)面向对象的设计方法
而目前比较流行的面向对象的系统架构设计方法通过定义出系统中的各种可能类型的对象,并对每种类型的对象进行细化以找出彼此之间的关系。
在面向对象的设计方法中,设计者可以充分地利用对象所具有的"抽象、封装、继承和多态"等特性,同时面向对象的系统架构设计方法是一种"自下而上"的设计方法,它所带来的优点是能够形成一种螺旋上升的软件开发方式,这对于已有的系统设计只需要进行局部地修改或者调整就可以满足软件应用系统的变化要求。
5、为什么要应用面向对象设计方法进行软件应用系统的架构设计——面向对象的架构设计能够适应不断变化的软件系统需求
应用面向对象的软件应用系统的架构设计方法能够保证软件应用系统的体系架构的设计结果具有一定的可重用性、可扩展性。这对于一个系统功能需要不断扩充的软件应用系统项目(如商业进销存系统、客户关系管理系统等),或者需要系列化的产品软件的软件应用系统架构设计(如财务产品软件、税务产品软件等)等类型的软件应用系统,更应该要采用面向对象的软件应用系统的架构设计方法进行系统设计和程序模块的功能开发。
6、遵守面向对象的软件应用系统架构设计的相关基本原则
(1)纵向分层隔离原则
软件应用系统经过合理地纵向分层和隔离,从而使得软件应用系统中的每一层都能够为其所对应的上一层提供功能服务而成为服务的提供者,同时也作为下层的客户端而获得所需要的其它层所提供的功能服务。因此,利用纵向分层架构模式来组织软件应用系统时能够构造出一个层次化的软件应用系统的纵向结构。
J2EE技术规范为开发复杂的、分布式企业级的软件应用系统定义了一整套系统体系结构和技术规范,它不仅提供了一套完整的基于标准化模块的功能服务组件(如JSP、Servlet和EJB等组件),而且也提供了对企业应用系统的标准纵向分层设计方案。
下图所示为体现某个系统的分层架构的UML技术规范的包图,从该示图中所展现的UML包图中可以了解到,该软件应用系统分别是由表示层、控制层、业务层、数据服务层和持久层等构成的,各个层之间只存在单向依赖关系——从而较好地实现了各个层的封装和彼此间的隔离。
![9035c748dc89dd22d6c8bab512c394ea.png](https://i-blog.csdnimg.cn/blog_migrate/f5e1962fa5a88c18afdb3ef40742921e.jpeg)
软件应用系统的设计人员如果在系统架构设计中遵守了纵向分层隔离的原则,将能够屏蔽各个层中的功能组件的具体技术实现细节、同时也能够达到某个层次的模块使用者只管使用所需要的目标层中的组件功能服务,而并不需要去了解该目标层中的各个组件的具体结构以及它们的具体技术实现的细节;另外,在软件应用系统的需求发生变化时,只需要改变某些基础层中的功能服务组件,但不会影响到其上层中的服务使用者组件的技术实现和程序代码。
本系列文章中所给出的示例项目——银行账户信息管理系统项目在纵向分层隔离方面采用四层次的系统架构设计,下图所示中的UML包图为体现本软件应用系统项目的系统架构设计结果的分层包图。
![63ba49e375d08670319b1cedc1a83e3b.png](https://i-blog.csdnimg.cn/blog_migrate/79e2dfb8cd589b8e06b47204007e8758.jpeg)
(2)依赖倒置原则
依赖倒置原则的本质就是要求将软件应用系统的架构设计中的各个层之间的关系要建立在依赖抽象接口的基础上,同时要求上层模块不应该直接依赖于下层的模块,它们两者都共同依赖于一个抽象;抽象元素不能依赖于具体元素,而具体元素则必须依赖于抽象元素。下图所示为某个软件应用系统中实现XML数据处理和XML文件中的数据解析之间的依赖关系图示。
![023fec29902d015e0c0b59d5a348f2c8.png](https://i-blog.csdnimg.cn/blog_migrate/31e9021c60a8ba24bc4942cadee1b240.jpeg)
因为在传统的面向过程的设计方法中倾向于使高层次的模块直接依赖于低层次的模块、抽象层程序依赖于具体实现层次的功能组件程序。而面向对象设计思想中所倡导的依赖倒置原则就是要把这种错误的依赖关系倒转过来(Dependence Inversion)。
下图中的箭头代表了面向过程的程序设计方法中的函数间的调用关系,也是函数间的依赖关系。如图所示,main()函数依赖于其各个子函数,而这些子函数又依赖于更小的子函数,而在实际的程序中,越小的函数处理的往往是很细节的功能实现,这些具体的功能实现的要求又常常会发生变化——当软件应用系统的需求发生变化时,软件应用系统的设计和开发实现人员经常要来回改动这些细节的实现。
![f7bca4c3309f2969497292e16d41f6de.png](https://i-blog.csdnimg.cn/blog_migrate/c2cf5baddc0a3168525f4c2a5e74b92f.jpeg)
随着软件应用系统功能的不断复杂化,系统中的各个层与层之间、层中的各个模块与模块之间的依赖关系会逐渐加强,软件应用系统的整体方面的可扩展性逐渐减弱——因为在面向过程的程序设计方法中的程序"核心逻辑"严重地依赖于外围程序的功能实现细节,程序中本来应该是比较稳定的"核心逻辑",也因为依赖于易变化的外围的功能实现细节部分,而变得不稳定起来。此时,如果软件应用系统中有一个细节上的小小改动(比如系统的需求发生变化而导致程序的修改),也有可能在依赖关系上引发一系列的"连锁"变动。
而如果在进行软件应用系统的架构设计时,设计人员遵守了依赖倒置原则将可以减轻或者避免这样的设计缺陷的状况出现。当然,为了能够满足依赖倒置原则的基本要求,软件应用系统的设计人员也需要以接口作为软件应用系统中的各个层之间的"粘联剂"。
(3)接口的定义和接口的具体实现相互分离原则
软件应用系统经过合理地分层隔离后,如何设计并决定出层中的各个组件之间的关系、分配各个组件各自的职责?
将软件应用系统中的各个功能模块的接口定义和对这些接口的具体实现相互分离,是解决这些问题的软件应用系统设计和实现的指导原则。
该设计原则的基本指导思想:为了能够消解两个软件模块间的相互依赖关系,应该在两个软件模块之间定义出一个抽象的接口,上层软件模块调用这个抽象接口中定义的方法,而下层软件模块具体地实现该接口中定义的各个方法。因为接口能够体现出对问题的抽象,同时由于抽象一般是相对稳定的或者是相对变化不频繁的,而具体实现则是易变的——随着应用的需求变化而相应地进行更新和完善相关的程序功能实现。
下图所示为某个系统持久层中实现用户信息数据库表功能操作的DAO组件接口UserManageDAOInterface和该DAO接口的功能实现类UserManageDAOJDBCImple之间关系的UML类图。
![8964955d590adc2260bc34910b4cf4bc.png](https://i-blog.csdnimg.cn/blog_migrate/68a70dfedefbbf9a54ef542673cc62c0.jpeg)
而一旦接口的定义和接口的具体实现如果相互分离,就可以基于同一个接口根据不同的应用需求不同要求提供对应的功能实现类,从而保证了软件应用系统能够适应不同的应用需求,同时也保证了上层的功能服务的调用保持不变。如下UML类图所示为计算机和不同外部设备之间的关系图,其中计算机类只依赖关联于USBDevice接口,而不依赖关联于具体的USB接口的实现设备,从而保证了计算机的USB设备的可扩展和可变化。
![61d2d0ebe11ebfd58ccd3bdad07fdd2b.png](https://i-blog.csdnimg.cn/blog_migrate/4ccb9b4ac1c2e007b5874cd5d5cbcd0a.jpeg)
7、在面向对象架构设计中合理地应用各种架构设计模式
对于"架构设计中的架构模式"等有关内容,作者已经在"J2EE项目实训——UML及设计模式"一书中的第7章"架构设计中的架构模式"作了比较详细的介绍。在此不再重复地描述,读者可以参考相关的章节内容的说明、并自行学习和理解。
![b7ed319743ed0a69ede2826c8f9e7a6e.png](https://i-blog.csdnimg.cn/blog_migrate/d0214fe573321aba0e29118926ec4a85.jpeg)
但作者在此需要补充说明一点,GOF设计模式中的桥(Bridge)、门面(Façade)、和中介(Mediator)等代码实现的模式,其实它们不仅可以作为代码实现的模式,也可以作为系统架构模式而出现——因为它们都能够实现"分层隔离"的效果。下图所示为应用中介模式实现将系统中的表示层组件与应用系统中的数据处理功能组件相互隔离的UML类图——通过中介对象把一系列的其他对象的交互关系加以封装,从而降低这些对象之间的耦合关系。
使对象之间的"多对多"关系变成两个"一对多"的关系,从而可以降低软件应用系统中的类之间关系的复杂性,相应地也就提高了软件应用系统的可扩展性。
![e530cbe1914f7c30c558c61a01da8b92.png](https://i-blog.csdnimg.cn/blog_migrate/699cce622187d3ae8e20ead325d4c8bb.jpeg)