在软件系统设计中如何降低软件系统中程序类之间耦合关系(上篇)

软件项目实训及课程设计指导——如何降低软件系统中程序类之间耦合关系(上篇)

1、分离软件应用系统中各个模块类的接口定义和对应接口的具体功能实现

面向对象程序类设计的五大原则中的”开放-封闭原则” ( OCP,Open-Close Principle)倡导分离接口的定义和接口的具体功能实现的设计原则。这在软件应用系统的需求是多变或者软件应用系统的功能实现存在多样化的软件应用系统的设计中,更应该遵守该设计的原则。

通过分离软件应用系统中各个模块类的接口定义和接口的具体功能实现,能够减少由于功能实现方式的变化或者多样化所带来的对目标程序类的被动改变;或者即使功能实现发生了变化,由于上层的使用者(在此为服务的请求者)是与功能的抽象接口相互关联,而没有与功能的具体实现程序类直接关联,并不会造成被动的变化修改。

如下示例图是说明本设计原则的UML类图示例,其中的客户端(也就是软件应用系统中的业务处理层相关功能类中的方法)是通过数据访问接口操作访问目标数据库系统中的数据,而不是直接应用指定的目标数据库系统对应的数据访问实现类来操作访问目标数据库系统中的数据。

这样的设计结果,可以使得软件应用系统的设计和开发人员可以在不修改软件应用系统中原有的功能代码的前提下(不必改动源代码或者二进制代码——二进制的可执行程序、动态链接库DLL或者Java中的JAR包文件等),而实现对软件应用系统中的软件功能进行扩展。

下图所示为银行账户信息管理系统在开发实现的开发规范要求的文档中,要求本应用系统的开发人员严格遵守将应用系统中的各个功能类分为接口定义和接口的具体功能实现两部分。比如,对于应用系统中的用户信息的数据库表中的数据访问功能的要求是由UserManageDAOInterface接口内定义的各个功能方法体现,而具体的数据访问功能的实现代码则是放在UserManageDAOJDBCImple实现类中(它为UserManageDAOInterface接口的具体实现)。请见下图中黑体所标识的代码或者文件名称。

2、利用接口类型的对象作为相关层之间或者两个程序类之间的连接点

(1)深入理解依赖倒置设计原则

1)上层模块不应该直接依赖于下层的模块,它们共同依赖于一个抽象(父类不能依赖子类,它们都要依赖抽象类);

2)抽象不能依赖于具体,具体应该依赖于抽象——因为抽象层次包含的是软件应用系统中通用的业务逻辑,一般是相对稳定的或者是相对变化不频繁的。而具体功能模块一般都包含有与具体功能实现有关的算法和逻辑等内容,一般是易变的——技术更新、应用环境改变、功能需求发生变化等各种可变因素。

此外,依赖抽象是实现软件应用系统中代码扩展和运行期内绑定(动态多态)的技术实现基础——只要是该抽象类的子类(在实际编程实现时如果将此处的”抽象”设计为编程语言中的接口,则它就是接口的实现类;而如果将此处的”抽象”设计为某种程序语言中的抽象类,它就为基于抽象类的子类),都可以被类的使用者使用。

(2)如何合理和正确地应用依赖倒置设计原则

“依赖倒置原则”的本质是要求软件应用系统的设计和开发实现人员将软件应用系统中程序类之间的关系建立在”抽象接口”的基础上——也就是要求软件应用系统的设计人员为了能够消解软件应用系统中的两个模块之间的依赖关系,应该在两个模块之间定义出一个”抽象接口”、并利用此接口对象作为层之间(比如业务处理层和数据访问层)或者两个程序类之间的连接点;上层模块调用在”抽象接口”中定义的方法(该方法代表具体的功能定义),而下层模块则实现该接口中定义的各个功能方法(该方法代表具体的功能实现)。

下图中的示例UML类图说明了”依赖倒置原则”的设计原理,应用”抽象接口”连接服务的请求者和服务功能的实现者,并根据应用的具体需要可以提供多个不同形式的功能实现,从而适应软件应用系统的可变性。

因为软件应用系统中的需求的改变,不仅可能是由于功能实现的多样化造成的,也可能是由于程序类之间的依赖关系发生了改变——如软件应用系统的业务逻辑的变化或者对业务流程的调整等因素而造成的被动变化。

(3)遵守依赖倒置设计原则的应用示例

基于此设计原则,也要求软件应用系统的设计和开发实现人员在设计程序类中的成员方法的参数(形参和返回参数)时应该尽可能选择为接口类型的对象、而不要选择接口的具体实现类的类型对象。

比如下面的代码示例中的getUserInfo方法的返回值的对象类型可以改用List接口类型而不要返回ArrayList类型——因为List接口有多种不同形式的实现类,当getUserInfo方法扩展为返回List接口的其它实现类时,不会影响到调用getUserInfo方法的上层程序代码;同样对setUserInfo方法的参数也应该由ArrayList类型改变为用List接口类型的参数。

因此,如下代码示例中的setUserInfo和getUserInfo方法的返回参数应该尽可能设计为返回接口类型的对象。

3、在软件应用系统的功能实现中尽可能针对”抽象”编程而不针对具体子类编程

因此,在软件应用系统的功能实现中尽可能针对”抽象”编程而不针对具体子类编程的设计要求的核心思想也就是要求程序开发人员在具体编程时尽可能声明抽象类型(如Java编程语言中的接口类型interface或者抽象基类abstract)的对象——这包括在方法的参数或者程序类的成员属性声明中,这样就可以充分地应用面向对象OOP技术中的动态多态机制达到灵活地对不同的子类型的对象访问。

下面所示的程序代码示例体现这种编程要求——对其中的各个对象类型都尽可能声明为抽象类型,并请注意其中黑体标识的代码。

interface SomeInterface{			//接口中的方法定义在此省略}class SomeInterfaceImple implements SomeInterface{			//对接口中的方法具体实现代码在此省略}class OtherClientApp{			private SomeInterface oneObj;			public void someMethod(SomeInterface twoObj){          //方法内的功能实现代码在此省略			}}

因为面向对象程序类设计五大原则中的”依赖倒置原则”倡导将程序类之间的关系建立在抽象接口的基础上,因此上述所示的程序代码也同样遵守了”依赖倒置原则”。但如下示例代码是不符合”依赖倒置”的设计原则,因为OtherClientApp是直接依赖于SomeInterface接口的实现类SomeInterfaceImple而不是依赖于抽象的接口SomeInterface。

class OtherClientApp{		private SomeInterfaceImple oneObj;		public void someMethod(SomeInterface twoObj){    		 //方法内的功能实现代码在此省略		}}

声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2020年10月15日
下一篇 2020年10月15日

相关推荐