软件项目实训及课程设计指导——如何合理地创建对象实例以降低程序类之间关系的耦合度
1、对象创建是面向对象OOP编程技术中不可缺少的一件事情
创建类的对象实例是在所有的面向对象OOP编程开发中软件应用系统的开发人员所必须要面对的问题,软件应用系统中的业务活动是由各个对象实例之间的相互交互而构成的。但频繁地创建对象实例不仅会降低软件应用系统的整体运行的性能,也增加了不必要的程序类之间的耦合关系。
如下示图中的Employee类与其中的TimeCard类之间就存在有“一对多”形式的关联关系,因为在Employee类的对象实例中耦合有TimeCard类的对象实例。
因此,如何正确和合理地创建出类的对象实例?什么时候应该创建类的对象实例?如何保证所创建出的类的对象实例能够适时地被销毁?
其实上面的核心问题也就是软件应用系统的开发人员如何能够更高效地创建、并且松藕合、达到程序模块的可扩展性?继续采用如下的对象实例的创建形式吗?
OracleDAOoneOracleDAO=newOracleDAO();
2、合理地进行对象的创建以降低程序类之间关系的耦合度
两个程序类之间如果存在或者出现有包括控制关系、调用关系、数据传递关系等情形时,这两个程序类之间也就产生了耦合关系。而其中的调用关系是通过对目标对象实例中的对外的功能服务方法实现的(public方法),因此软件应用系统的设计人员有必要合理地进行对象实例的创建以降低程序类之间关系的耦合度。
如下示图中的Employee类与其中的Calculater类之间就存在有“依赖”关系,因为Calculater类以对象实例的方式参与到Employee类的功能方法calcSalary(Calculaterstrate)的参数中,也就是Employee类的功能方法calcSalary在执行时依赖于Calculater类的对象实例——Employee类与Calculater类之间就存在有“依赖”关系。
3、完善GRASP创建者设计模式的不足
通用职责分配软件模式(GRASP)中的创建者设计模式只是指导软件应用系统的设计人员决定创建对象的职责具体是由哪个目标程序类来承担,但并没有明确地告诉开发人员应该采用什么形式和技术手段完成创建对象的具体工作。
作者在本文及后续相关的文章中试图指导读者在软件应用系统的项目开发中如何合理地创建对象实例以降低程序类之间关系的耦合度,进一步提高对软件应用系统中程序类之间关系的设计能力。
4、面向对象编程语言中的对象实例的传统创建方式——利用new语句创建对象实例
(1)应用 new语句创建对象实例
在支持面向对象OOP编程技术的各种语言中(如Java语言)都提供有采用new语句并在程序类中提供公有(public)的构造方法实现对象实例创建的语法支持。比如,在Java程序设计语言的程序代码中要创建出代表用户信息的实体类UserInfoPO的对象实例时,可以采用下面的语句:
UserInfoPOoneUserInfoPO=newUserInfoPO();
使用new操作符创建对象实例一般需要三个基本的过程:首先为对象实例分配一定的内存空间,然后再调用对象所在类的构造方法,最后返回所创建出的对象实例的引用。
而且在Java程序设计语言中的对象只有实例化后,JVM虚拟机系统才真正地创建出它并为它分配出合适的内存空间;如果未实例化对象就直接使用它,则该对象将为空对象并抛出NullPointerException类型的异常。
在如下示图所示的程序示例中,由于对StudentInfo类的对象只是定义了(StudentInfooneStudent=null;),但并没有对它进行对象实例化(oneStudent=newStudentInfo();)就直接使用StudentInfo类的对象oneStudent,从而产生出NullPointerException空指针类型的异常错误(参见如下示图中的控制台中的异常信息)。
当然,如果一个对象已经进行了对象实例化,但又采用了oneStudent=null;这样的语句进行赋值,该对象实例已经被删除后如果再继续使用该对象,也同样会产生出NullPointerException空指针类型的异常错误,读者可以参见如下示图中的程序代码示例(注意其中黑体标识的语句)以及在控制台中显示的异常信息。
(2)了解对象的生命周期
Java程序设计语言中的对象生存期主要分为:创建、使用和销毁(删除)三个阶段。当对象失除其作用域时,JVM虚拟机系统自动在后台清除这些对象——如在某方法中创建了一个对象实例,当该方法返回时,也就无法再引用该对象实例。
当然,软件应用系统的开发人员也可强行提前清除某一对象,这只需要将它置为null或者调用该对象中的close()方法。比如,对于物理数据库系统的数据库连接对象、文件IO对象等在使用完毕后,应该及时地释放该对象所占用的系统资源、并清除该对象。
5、常规的应用new语句创建对象实例方式所存在的问题
常规采用new操作符语句进行对象实例创建的方式存在一定的问题,首先需要在对象创建的语句中直接指定目标类的名称。一旦目标类名称发生变化,就需要修改该new语句中的目标类名称;其次在不同的方法中需要该对象实例时,都需要利用new语句对该对象进行创建。不仅产生了大量重复的对象实例创建的语句,而且也将对象实例创建的职责分散到不同的功能方法中。一旦对象实例的创建过程和要求发生变化时,就需要修改分散到不同的功能方法中的对象实例的创建语句,增加了代码维护的工作量。
另外,有些程序类的对象实例是不应该重复地创建的——比如数据库连接Connection对象等;其次,某些程序类只允许产生出单例的对象实例。对于对象实例创建时的这些基本的要求,采用普通的new操作符语句是做不到的。
因此,如何能够更高效地创建对象实例、并且松耦合以达到程序模块的可扩展性和可维护性?
6、利用工厂设计模式分离对象实例的创建逻辑和对象实例的使用逻辑
为了能够降低程序类之间关系的耦合度,软件应用系统的开发人员有必要合理和有效地创建对象——也就是需要分离对象实例的创建逻辑和对象的使用逻辑。对象的使用方只需要了解对象对外的功能方法的接口,而不需要自行负责对该对象的创建工作。封装对象的创建过程和逻辑、并将对象的创建职责由某个特定的功能类(如工厂类)承担——这是对面向对象OOP设计方法中的“单一职责设计原则”的具体应用。
一旦能够分离对象实例的创建逻辑和对象实例的使用逻辑,也就能够降低由于对象实例的创建而产生的程序类之间关系的耦合度。当被创建的目标对象所在的程序类的代码发生变化时,不需要修改分散到不同功能方法中的对象创建的new操作符语句,而只需要集中修改承担对象实例创建职责的工厂类中的对象实例的创建方法。
下图所示的黑体部分所标识的代码,表示在示例项目银行账户信息管理系统中,利用数据库连接对象的工厂类ConnectDBFactory中的newConnectDBBean方法创建出对应的对象实例的程序代码。
如下程序为数据库连接对象的工厂类ConnectDBFactory类的完整程序代码示例,在ConnectDBFactory工厂类中应用了观察者设计模式。其中的工厂方法newConnectDBBean接收需要创建对象的类名称参数,并返回目标对象所在的接口类型的对象实例。
package