基于JAVA SMART系统-系统框架设计与开发

引言

随着计算机技术的发展及计算机的日益普及,基于Web的在线考试与无纸化办公已成为大势所趋。特别是在大兴数字化校园建立的今天,基于WEB在线考试系统也已经成为学校信息化建设中不可缺少的一部分,它的优势不但体现在人力上也体现在物力上,基于WEB的在线考试系统的自动评阅、计分、成绩存档功能将有效地避免资源的浪费,有利于环保,减少人员投入,大大的提高了效率。

相比传统的考试方式,基于WEB的在线考试系统的主要好处是一方面可以动态地管理各种考试信息,只要准备好足够大的题库,就可以按照要求自动生成各种试卷;另一方面,考试时间灵活,可以在规定的时间段内的任意时间参加考试;另外计算机化的考试的最大特点是阅卷快,系统可以在考试结束时当场给出客观题考试成绩,计算机阅卷给了考生最大的公平感。

本文中研究了基于Web的在线考试系统―系统框架设计与开发设计与实现,包括系统需求分析和系统框架功能设计与实现。

国内外研究现状

网络考试系统极大地提高了教学的灵活性,现在在许多领域已经有了广泛的应用。在国外最有影响的案例就是ETS(美国教育考试中心)举办的GRE(美国研究生入学考试)的计算机化考试,它使考试由原来的每年只能有两次参加考试的机会变成了每个工作日都可以参加的考试,大大提高了工作的效率。

在我国,经过这么多年在IT业的发展及经验的积累,虽然数字化教育已经蓬勃地发展起来,但是目前学校与社会上的各种考试大都采用传统的考试方式,在此方式下,组织一次考试至少要经过五个步骤,即人工出题、考生考试、人工阅卷、成绩评估和试卷分析。然而在这个过程中人工手动出题和试题的选择是最为复杂的,显然,随着考试类型的不断增加及考试要求的不断提高,教师的工作量将会越来越大,并且其工作将是一件十分烦琐和非常容易出错的事情,可以说传统的考试方式已经不能适应现代考试的需要。因此在国内也出现了比较适合自己本国特色的在线考试系统如:杰佛通用在线考试系统,新为在线考试系统等。

本课题研究的意义

本系统主要用来缓解传统考试所需要的繁琐工作流程,组织试题、印刷试卷、组织考试、监考防作弊、收卷判分、统计结果等,通过本系统,可以将以上考试的全部流程,完全借助计算机系统来实现,从而减轻教师的工作负担及提高工作效率,与此同时也提高了考试的质量,使考试更趋于公证、客观、科学性进一步的激发了学生的学习兴趣。

系统需求分析

SMART系统框架在实现上要求采用在J2EE领域中比较流行的SSH框架组合方式,并结合系统自身的一些特点来实现适合于本系统框架设计,该系统的一些非功能性的需求如下:

可扩展性要求

一般来说,软件的可扩展性决定着其适应变更能力的大小。事后我们总是可以很容易地评价某个程序是可扩展的还是不可扩展的,但是要想使这个系统在今后真正派上用场,我们必须事先就对它有个判断。一个系统一般都是要在不断升级的过程中去结合用户的需求来完善自身功能,因此该系统要求采用清晰的接口把对象的实现与它的交互分离开来,特定的实现就可以独立于应用的其他部分,各个实现也可以在将来很方便地修改、升级甚至彻底替换。这时的应用不再是一个单独的东西,而是一个系统中半独立的组件。多个开发者可以在相互不破坏彼此成果或者甚至在不了解全局场景的情况下协同开发。组件提供了特定的功能,允许独立地测试,也可以重用于多个应用中。如果组件之间工作分配及接口是很清晰的,这样的软件就很容易扩展。

可维护性要求

系统在开发完成以后,后期的维护也是一个项目中重要的一环,包括为了满足用户的使用体验对代码所作的修改;对上线后长时间运行过程中所出现的BUG所作的修改等等这些需求,都是要求一个系统是具有可维护性的特性的。

可伸缩性要求

可伸缩性和性能是紧密相关的,但是它们并不完全相同。可伸缩性可以广义地定义为应用在请求数目增长时维护性能的能力。在SMART系统中要求达到一个地市州级地区教育系统内的全体学生提供在线考试服务,并发数应在10000人以上。在本系统架构的中是采用分层的技术,这将可以很好的满足可扩展性与可维护性的要求,但是各层之间的通信又反过来制约了伸缩性。因此,就要求我们在架构设计的时候使用有成效的实践经验来连接这些层次。

可靠性要求

可靠的软件在所有时刻都会按照预期的那样执行。与可伸缩性类似,一个可靠的系统取决于对其底层组件的可预测性。从一个用户的角度来看,可靠性是对整个系统的判断,包括硬件、软件和网络元素。如果一个单独的组件发生故障了,并且用户无法访问应用或者应用工作不正确了,那么整个系统就是不可靠的。在SMART系统中的需求中,可靠性是首当其冲的,特别体现在考试的过程中。如果在考试的过程中系统出问题了,或者在考试后的阅卷中系统算错分了,这都将是灾难性的错误,从而导致严重的后果。因此,保证SMART系统的可靠性的至关重要的。

  1. 系统开发环境及实现关键技术
    1. 硬件环境

CPU:  AMD Sempron(tm) 2500+

内存:1 G

硬盘:80 G

    1. 软件环境

操作系统:Microsoft Windows Server 2003

显示设置:SAMSUNG SyncMaster 795MB

应用软件:SQL Server 2000 + Eclipse3.1.2 + JDK1.5 + Apache Tomcat5.5.15

系统实现的关键技术

AJAX技术

AJAX全称为“Asynchronous JavaScript and XML”(异步JavaScript和XML),该技术并不是一种新的技术,实际上是多种技术的综合,包括JavaScript、XHML和CSS、DOM、XMLXSL和XMLHttpRequest。其中使用XHML和CSS作为标准化的呈现,使用XMLHttpRequest作为异步数据的读取,使用JavaScript绑定和处理所有数据。

在AJAX提出之前,业界对于上述技术都只是单独的使用,没有综合使用,也是由于之前的技术需求所决定的。与传统的Web应用不同,AJAX采用异步交互过程。AJAX在用户与服务器之间引入一个中间媒介,从而消除了网络交互过程中的处理-等待-处理-等待缺点。用户的浏览器在执行任务时即装载了AJAX引擎。AJAX引擎用JavaScript语言编写,通常藏在一个隐藏的框架中。它负责编译用户界面及与服务器之间的交互。AJAX引擎允许用与应用软件之间的交互过程异步进行,独立于用户与网络服务器间的交流。现在,可以用JavaScript调用AJAX引擎来代替产生一个HTTP的用户动作,内存中的数据编辑、页面导航、数据校验这些不需要重新载入整个页面的需求可以交给AJAX来执行。

图1 Ajax的web应用模型

      1. 单例模式

单例模式作为设计模式中的创建模式类型中的一种,从其名字可以看出该模式确保了一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。这类称为单例类。单例模式具有以下的特点:

  1. 单例类只可有一个实例。
  2. 单例类必须自己创建自己这惟一的实例。
  3. 单例类必须给所有其他对象提供这一实例。

在Java中单例模式一般有三种实现其分别为:饿汉式单例类;懒汉式单例类;登记式单例类。

  1. 饿汉式单例类:

饿汉式单例类是在Java 语言里实现得最为简便的单例类,下面所示的类图描述了一个饿汉式单例类的典型实现。

图2 饿汉式单例类类图

从类图中可以看出,在这个类被加载时,静态变量m_instance 会被初始化,此时类的私有构造子会被调用。这时候,单例类的惟一实例就被创建出来了。Java 语言中单例类的一个最重要的特点是类的构造子是私有的,从而避免外界利用构造子直接创建出任意多的实例。值得指出的是,由于构造子是私有的,因此,此类不能被继承。

  1. 懒汉式单例类:

与饿汉式单例类相同之处是,类的构造子是私有的。与饿汉式单例类不同的是,懒汉式单例类在第一次被引用时将自己实例化。如果加载器是静态的,那么在懒汉式单例类被加载时不会将自己实例化。如图3所示,类图中给出了一个典型的懒汉式单例类实现。

图3懒汉式单例类类图

  1. 登记式单例类:

登记式单例类是GoF为了克服饿汉式单例类及懒汉式单例类均不可继承的缺点而设计的。在本例中把他们的例子翻译为Java 语言,并将它自己实例化的方式从懒汉式改为饿汉式。只是它的子类实例化的方式只能是懒汉式的,这是无法改变的。如图4所示是登记式单例类的一个例子,图中的关系线表明,此类已将自己实例化。

图 4登记式单例类

      1. IoC模式

IoC (Inversion of Control)模式又称DI(依赖注入),从GoF设计模式中,我们已经习惯一种思维编程方式:Interface Driven Design 接口驱动,接口驱动有很多好处,可以提供不同灵活的子类实现,增加代码稳定和健壮性等等,但是接口一定是需要实现的,也就是如下语句迟早要执行:

  AInterface a = new AInterfaceImp();

AInterfaceImp是接口AInterface的一个子类,IoC模式可以延缓接口的实现,根据需要实现,打个比喻:接口如同空的模型套,在必要时,需要向模型套注射石膏,这样才能成为一个模型实体,因此,将人为控制接口的实现成为“注射”。

其实IoC模式也是解决调用者和被调用者之间的一种关系,上述AInterface实现语句表明当前是在调用被调用者AInterfaceImp,由于被调用者名称写入了调用者的代码中,这产生了一个接口实现的原罪:彼此联系,调用者和被调用者有紧密联系,在UML中是用依赖 Dependency 表示。

但是这种依赖在分离关注的思维下是不可忍耐的,必须切割,实现调用者和被调用者解耦,新的IoC模式 Dependency Injection 模式由此产生了, Dependency Injection模式是依赖注射的意思,也就是将依赖先剥离,然后在适当时候再注射进入。

      1. JAVA反射机制

“反射”是 Java 程序开发语言的特征之一,它允许运行中的 Java 程序对自身进行检查,或者说“自审”,并能直接操作程序的内部属性。Java 的反射机制是使其具有动态特性的非常关键的一种机制,这个机制允许程序在运行时透过Reflection APIs取得任何一个已知名称的class的内部信息,包括其modifiers(诸如public, static 等等)、superclass(例如Object)、实现之interfaces(例如Cloneable),也包括fields和methods的所有信息,并可于运行时改变fields内容或唤起methods。

      1. JSP自定义标签

要在JSP中实现自定义标签,那么要求必须实现一个相应的标签处理类及写该标签的TLD文件,在JSP中一个自定义的action也就是对tag处理类,是基于一个bean类型的类,该类里面伴随的getter/setter方法对应于tag中的属性。另外这个tag  handler类还必须要实现JSP规范中与tag相关的三个接口中的一个。这三个接口如下所示:

  1. Tag
  2. IterationTag
  3. BodyTag

IterationTag继承了Tag,BodyTag继承了IterationTag在API中提供的两个类使开发自定义的标签变得容易,它们分别为TagSupport、BodyTagSupport。TagSupport实现了Tag与IterationTag的接口,而BodyTagSupport除了实现了那两个接口外还实现了BodyTag 接口。图5展现了整个标签处理过程:

图5标签处理的生命周期

  1. 系统总体框架设计
    1. 总体结构说明

  

图6系统总体框架示意图

      1. 表现层

如图1所示,在整个SMART系统的总体框架中表现层是结合J2EE领域的开源框架Struts来实现的,Struts能充分满足应用开发的需求,简单易用,该框架是基于MVC模式的来构建的,该模式将表达层分解为自包含的和可重用的几个部分,当用户通过浏览器发起HTTP请求时,该框架将利用其ActionForm将请求页面的非对象化的数据转化为对象,交由其对应Action来处理。

开发人员利用该框架进行开发时,不用再自己实现全套MVC模式,节省了大量的开发时间。

      1. 业务层

如图1所示,在表现层与业务层之间利用一个公共代理类来完成交互,该代理类采用单例模式设计开发,在整个框架中起到了如下几点作用:

  1. 减少耦合性
  2. 避免生成大量的临时对象

在该代理类,实现一个对相应业务逻辑的处理方法,该方法的参数为一个封装好相应的页面数据对象、要调用的业务类的名称及该业务类中相应的处理方法名的类。

在该层中利用了Spring框架中的IoC模式(英文全名为Inversion of Control即反转模式),该模式类似于著名的好莱坞原则:“Don't call us,we'll call you”,后被Martin Fowler改名为 Dependency Injection 依赖注射,也就是将类之间的关系通过第三方进行注射,不需要类自己去解决调用关系,实现了调用者和被调用者之间的解耦分离。IoC的引入并没有消除接口与实现类之间的联系,它的实质在于只是将这种联系转移了。在Spring的IoC实现中这关系被转移到相应的XML配置文件中,由Spring框架来提供对这种关系的依赖注入。其原理如图8所示:

图8 IoC模式在Spring中的依赖注入

      1. 持久层

在SMART系统的整体框架中的持久层,是采用基于现有持久层框架的实现模式,在这种模式中,将最为繁琐的基于JDBC的OR映射工作,交由第三方组件(本框架中采用开源的Hibernate)来完成,这样就会在对数据访问对象进行编码时,大大的简化了一些繁琐而又复杂的编码工作,只需要利用Hibernate提供的API,对持久化对象进行操作。在该持久层框架提供了优秀的性能优化机制,如内置的数据库连接池支持,PreparedStatement缓存、数据缓存等。这些优化机制的综合使用大大提升了系统的性能。

在SMART系统的持久层中,对一些常用的添加、删除、更新数据库操作进行了抽象封装。并在Hibernate中配置相应的数据库连接池实现。

图9基于现有持久层框架的实现模式

    1. 总体结构设计与建模
      1. 公共代理机制的设计

在大部分采用B/S结构的web应用中,用户与系统的交互都是要涉及到相应的交互数据、业务逻辑,因此在本系统的框架设计中考虑到将这些交互中涉及到的因素全部封装到一个Carrier类中,再通过一个单例类来实现表现层与业务层的交互,这样用户操作时不用每次都去new一个临时的对象,也实现各功能模块中子程序之间的解耦。

      1. 系统接口设计

Java程序设计语言提供了两种机制,可以用来定义一个允许多个实现的类型:接口和抽象类。两种机制之间最明显的区别是,抽象类允许包含某些方法的实现,但是接口是不允许的。一个更为重要的不同之处在于,为了实现一个由抽象类定义的类型,它必须成为抽象类的一个子类。任何一个类,只要它定义了所有要求的方法,并且遵守通用约定,那么它就允许实现一个接口,不管这个类位于类层次的哪个地方。因为Java只允许单继承,所以,抽象类作为类型定义受到了极大的限制。接口使得我们可以构造出非层次结构的类型框架。例如:假设有一个接口代表一个singer(歌唱家),另一个接口代表一个songwriter(作曲家):

public interface Singer{

   AudioClip Sing(Song s);

}

public interface SongWriter{

   Song compose(boolean hit);

}

在现实生活中,有些歌唱家本身也是作曲家。因为是使用接口而不抽象类来定义这些类型,所以对于一个类而言,它同时实现Singer和Songwriter是完全允许的。实际上还可以定义第三个接口,它同时扩展了Singer和Songwriter,并且加入一些适合于这种组合的新方法:

   public interface SingerSongwriter extends Singer,Songwriter{

       AudioClip strum();

       Void actSensitive();

}

如果要满足这样的一种灵活性,抽象类是不可能完成的。

虽然接口不允许包含方法的实现,但是,使用接口定义类型并不妨碍你为程序员提供实现上的帮助,在本系统的接口设计与实现中借鉴了Java API中的将接口类与抽象类的优点结合起来,将期望导出的每一个重要接口,都提供一个抽象类的骨架实现(skeletal implementation)类。按照惯有编码命名习惯,将该骨架实现类命名为AbstractInterface

在该系统中采用这种方式的来设计的接口有Business(业务)接口,DAO(数据访问对象)接口。

      1. 自定义标签的设计

在对系统的实现中,由于在表现层使用的是基于MVC的Struts框架,该框架中为了在表现层的JSP页面中不混合大量的Java代码,及保持JSP页面的程序的容易读性而提供了相应的Struts自己的一套标签,但是考虑到本系统的实际应用的功能实现。且这些功能实现又是Struts标签没法满足要求的,因此在该系统框架中实现了自己的一套标签,主要有<smart:submit></smart:submit>,<smart:select></smart:select>。

  1. <smart:submit>标签:

设计意图:

在一般的基于B/S结构的web应用系统中,在页面上经常是会涉及到添加、编辑、删除几种常用的功能,在早期的一些开发编码中,相当一部分人是将这几种功能分为多个页面来实现,例如:添加用一个add.jsp页面,编辑用一个edit.jsp页面,删除用一个delete.jsp页面。而这些的页面的代码80%以上都基本上是一样的,只不过是上显示的按钮及在点击相应的提交给后台处理方法不一样。为了达到在一个页面上面实现添加、编辑、删除功能,并且要保持页面代码的清晰可读性,因此,在本系统框架中,封装了自定义的提交标签。

表1 <smart:submit>标签属性列表

属性名称

属性描述

备注

Id

该标签元素的id 。

Name

该标签元素的名称。

Property

当点击该标签所显示的内容后所提交的参数名称

Message

该标签在页面上所显示的值,等同于html标签组里面的input标签的value属性。

displayControl

是否要对该标签的内容是否在页面上显示出来作控制。

只有两种值

true/false

Image

该标签在页面显示的内容的背景图片。

onClick

当点击该标签所展现的内容后所触发的事件。

  1. <smart:select>标签:

设计意图:

在一般的基于B/S结构的web应用系统中,在页面上经常要用到下拉框,且在JSP页面加载出来时就有一组相应的值,为了在加载该页面时动态的、带条件的取其标签相应的键/值对,因此,在本系统框架中,封装了自定义的下拉菜单。

表2 <smart:select>标签属性列表

属性名称

属性描述

备注

name

该标签元素的名称。

property

该标签元素属性名称,与Struts中的html标签的property元素是一样的含义,对应于FormBean里面的一个域。

content

该标签属性为标签取值的业务代码。

multiple

该属性同html标签组中的select标签的multiple属性。

size

该属性同html标签组中的select标签的size属性。

style

该属性同html标签组中的select标签的style属性。

relationValue

该属性为在加载该标签相应的值时的条件。

      1. 基于Struts表现层设计

大部分基于B/S结构的web应用系统中,在页面上经常会出现一个以上的功能按钮,而这些功能按钮基本上都是对应于后台的一个操作实现,由于在本系统中的表现层选用较为成熟Struts框架,该框架中最为核心的部分要属控制器控制转发相应的HttpRequest,其中的LookupDispatchAction类是允许你指定一个具有多个方法的类,每一个方法的调用都基于配置文件中指定的一个特殊请求参数值,利用该参数值反向查询资源绑定,并将它与类中的一个方法进行匹配。从这些功能可以看出Struts是满足对系统页面上多个功能按钮与实现的绑定。

因此,在对本系统进行架构设计的时候,考虑建立一个抽象的BaseAction类,该类继承LookupDispatchAction,实现LookupDispatchAction类中的getKeyMethodMap方法,在方法中返回本系统中请求参数值与资源文件中参数值的键/值对。实现一些对于所有的Action都是有可能用到的公共方法,包括从session中得到用户的信息;对页面上按钮是否显示的控制;检查用户权限;对公共业务逻辑接口的调用等等。在涉及到系统的具体开发实现的时候,要求所有开发人员在写自己的Action的时候统一继承BaseAction。

      1. 基于Hibernate持久层设计

本系统的持久层是基于开源的Hibernate来实现的,在了解到相关的Hibernate特性后,在本系统的框架中,从如下几个方面对其进行了进一步的封装。

  1. 对产生Session实例进行封装

设计意图:

Session是Hibernate持久化操作的基础。注意这里Session的含义,它与传统意义上的Web层的HttpSession并没有什么关系。Hibernate Session之与Hibernate,相当于JDBC Connection与JDBC。

Session作为贯穿Hibernate的持久化管理器核心,提供了众多持久化方法,如save、update、delete,find等。通过这些方法即可透明地完成对象的增删改查(CRUD)。但是值得注意的是,Hibernate Session的设计是非线程安全的,也就是说,一个Session实例同时只可由一个线程使用,同一个Session实例的多线程并发调用将导致难以预知的错误。

因此在本框架中对通过SessionFactory所产生的Session。进行了线程安全性的处理,在实现的HibernateSessionFactory类新建一个Java中的ThreadLocal类,将每次产生Session放入该类中,这样就达到了线程安全性的效果了。

当产生Session而创建SessionFactory实例时,也要注意对SessionFactory重用的问题,因为SessionFactory中保存了对应当前数据库配置的所有映射关系,同时也负责维护当前的二级缓存和Statement Pool。由此可见,SessionFactory的创建过程必然非常复杂、代价高昂,而这也意味,在系统设计中要充分考虑到SessionFactory的重用策略。由于SessionFactory采取了线程安全的设计,可由多个线程并发调用,大多数情况下,一个应用中针对一个数据库共享一个SessionFactory实例即可。

  1. 对通常的数据库操作进行封装

设计意图:

   在系统的设计架构中考虑到代码的重用性,因此对一些通用的数据库的操作都将其封装到一个公共类中,这样就减少了系统开发人员的代码编写工作量,也避免了同功能代码的重复编写。

  1. 系统总体框架具体实现
    1. 各层具体实现
      1. 表现层

在本系统中的表现层实现了一个抽象的BaseAction类,该类继承LookupDispatchAction,实现LookupDispatchAction类中的getKeyMethodMap方法,在方法中返回本系统中请求参数值与资源文件中参数值的键/值对。实现一些对于所有的Action都是有可能用到的公共方法。部分代码如下所示:

public abstract class BaseAction extends LookupDispatchAction {

//实现父类的方法

        protected Map getKeyMethodMap() {

                 Map map = new HashMap();

                 map.put("button.save", "save");

                 map.put("button.cancel", "cancel");

            …………………..

                 return map;

        }

    //统一的业务逻辑调用方法

        protected Object call(Carrier vo) {

        PublicProxy proxy = PublicProxy.getInstance();

        Object obj = null;

        try {

            obj = proxy.process(vo);

        } catch (ApplicationException ex) {

            throw ex;

        }

        return obj;

}

//显示页面按钮

        protected void showButton(String buttonName) {

        Map btnMap = getButtonMap();

        btnMap.put(buttonName, "true");

    }

    //隐藏页面按钮

    protected void hideButton(String buttonName) {

        Map btnMap = getButtonMap();

        btnMap.put(buttonName, "false");

    }

    …………………………………

}

      1. 业务层

在本系统中的业务层实现了一个单例的PublicProxy类,系统中所有的表现层与业务层的交互都要通过这个类来实现。该类结合了Spring框架的相应的API对业务接口与其实现的对应关系的xml文件进行了解析,详见下面的类中的process方法中对xml文件的读取。

public class PublicProxy {

    private static PublicProxy instance = null;

    private static Object lock = new Object();

    private PublicProxy() {

    }

    //返回唯一的实例

    public static PublicProxy getInstance() {

        if (instance == null) {

            synchronized (lock) {

                if (instance == null) {

                    instance = new PublicProxy();

                }

            }

        }

        return instance;

}

//相应的业务处理公共接口

    public Object process(Carrier aop) {

        Business business = null;

        ApplicationContext context

= new ClassPathXmlApplicationContext("beans/*.xml");

        business = (Business) context.getBean(aop.getBusiness());

        return business.process(aop);

    }

}

      1. 持久层

本系统的持久层是基于开源的Hibernate来实现的,结合Hibernate提供的API提供相应实现的部分代码如下所示:

public class HibernateSessionFactory {

    …………………

    private static final ThreadLocal thread = new ThreadLocal();

    //打开一个新的session

    public static Session openSession(boolean useCurrent) {

        Session session = null;

        if (useCurrent && thread.get() != null) {

            session = (Session) thread.get();

        }

        if (session == null) {

            try {

                if (factory == null) {

                    factory = new Configuration().configure(

                            CONFIG_FILE_LOCATION).

                              buildSessionFactory();

                }

                session = factory.openSession();

                 //将session放入ThreadLocal中实现线程安全性设计

                thread.set(session);

            } catch (HibernateException ex) {

                ex.printStackTrace();

            }

        }

        return session;

    }

    ………………

}

结论

通过本次课题的研究,在结合J2EE领域比较优秀成熟的框架的基础上,对系统进行了分层架构,完成了对基于Struts表现层扩展与开发,加入适合本框架的一些特性。在表现层与业务层之间的衔接上,设计并实现了通过一个公共的代理类来交互,在基于Hibernate的持久层的设计与开发中,实现了对一些常用功能操作的封装。从总体上来说,实现了一个具有实用价值的框架,利用该框架来进行开发的SMART系统具有可扩展性、可维护性等优秀软件所要达到的特点。

通过对本次课题的研究,使我在对软件开发有了一个新的认识,感觉到软件的设计与建模其实是一种艺术。在对这种艺术的研究过程中,我对Java编程有了更深入的理解,对J2EE领域的各技术的实现有了自己的看法与理解。

专注毕业设计指导,私信可领取开题报告。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值