2005年08月31日

单位变化的时间算法
 这是你怎样计算两个日期的时间变化:
1. 制作两个日期的拷贝。Close()方法能制作拷贝。
2. 使用日期拷贝,将所有的小于时间单位变化的部分设置成它的最小单位。例如,如果计算天数,那么将小时,分钟,秒和毫秒设置成0。这种情况中,使用clear()方法将时间值设置称他们各自的最小值。
3. 取出较早的日期,将你要计算的单位加1,重复直到两个日期相等。你加1的次数就是答案。可以使用before()和after()方法,他们返回boolean值,来判断是否一个日期在另一个日期之前或之后。
下面的类的方法用来计算天数和月数。

import java.util.*;

public class ElapsedTime {

   public int getDays(GregorianCalendar g1, GregorianCalendar g2) {
      int elapsed = 0;
      GregorianCalendar gc1, gc2;

      if (g2.after(g1)) {
         gc2 = (GregorianCalendar) g2.clone();
         gc1 = (GregorianCalendar) g1.clone();
      }
      else   {
         gc2 = (GregorianCalendar) g1.clone();
         gc1 = (GregorianCalendar) g2.clone();
      }

      gc1.clear(Calendar.MILLISECOND);
      gc1.clear(Calendar.SECOND);
      gc1.clear(Calendar.MINUTE);
      gc1.clear(Calendar.HOUR_OF_DAY);

      gc2.clear(Calendar.MILLISECOND);
      gc2.clear(Calendar.SECOND);
      gc2.clear(Calendar.MINUTE);
      gc2.clear(Calendar.HOUR_OF_DAY);

      while ( gc1.before(gc2) ) {
         gc1.add(Calendar.DATE, 1);
         elapsed++;
      }
      return elapsed;
   }

   public int getMonths(GregorianCalendar g1, GregorianCalendar g2) {
      int elapsed = 0;
      GregorianCalendar gc1, gc2;

      if (g2.after(g1)) {
         gc2 = (GregorianCalendar) g2.clone();
         gc1 = (GregorianCalendar) g1.clone();
      }
      else   {
         gc2 = (GregorianCalendar) g1.clone();
         gc1 = (GregorianCalendar) g2.clone();
      }

      gc1.clear(Calendar.MILLISECOND);
      gc1.clear(Calendar.SECOND);
      gc1.clear(Calendar.MINUTE);
      gc1.clear(Calendar.HOUR_OF_DAY);
      gc1.clear(Calendar.DATE);

      gc2.clear(Calendar.MILLISECOND);
      gc2.clear(Calendar.SECOND);
      gc2.clear(Calendar.MINUTE);
      gc2.clear(Calendar.HOUR_OF_DAY);
      gc2.clear(Calendar.DATE);

      while ( gc1.before(gc2) ) {
         gc1.add(Calendar.MONTH, 1);
         elapsed++;
      }
      return elapsed;
   }
}

你可以在上面的类中补充另外的方法来处理小时和分钟。同样,计算时间段的算法能更高效一些,尤其是时间相隔很长。可是,作为介绍目的,这个算法有短小和简单的优势。
下面的例子使用ElapsedTime类来计算两个日期之间的天使,而后是月数:

import java.util.*;

public class Example {
   public static void main(String[] args) {
      GregorianCalendar gc1 = new GregorianCalendar(2001, Calendar.DECEMBER, 30);
      GregorianCalendar gc2 = new GregorianCalendar(2002, Calendar.FEBRUARY, 1);

      ElapsedTime et = new ElapsedTime();
      int days = et.getDays(gc1, gc2);
      int months = et.getMonths(gc1, gc2);

      System.out.println("Days = " + days);
      System.out.println("Months = " + months);
   }
}

当计算时,上面的程序可能有用,例如,最近的航班。它显示下面的输出:

Days = 33
Months = 2

(OK,关于航班的计算有些夸张;这个天数算法很适合像图书馆借书这样的应用,你看到了她怎样工作)
告诫
在进行时间工作时要谨慎:你看到的时间段的例子,你精确仔细的考虑非常重要。本文介绍了两种通常计算时间段的想法,但是人们能想到的时间段的计算方法仅仅受到人类想象力的限制。
所以,当写一个Java程序的时候,确信你的精确度能让使用和以来这些程序的人满意。同样,彻底的测试程序对处理时间的程序非重重要。
总结
本文是在我的前一篇文章 Java时间计算介绍怎样使用GregorianCalendar 和 DateFormat类处理时间问题的基础上的。你已经看到了两种方法来思考时间段问题和两种相应的途径使用Java来处理时间问题。这里提供的信息,很基础,提供给你一个在Java中处理时间问题的有力工具。

关于作者
 Robert Nielsen是SCJP。他拥有硕士学位,专攻计算机教育,并且在计算机领域执教多年。他也在各样的杂志上发表过很多计算机相关的文章。
关于译者
Cocia Lin(cocia@163.com)是程序员。它拥有学士学位,现在专攻Java相关技术,刚刚开始在计算机领域折腾。

概述
如果你知道怎样在java中使用日期,那么使用时间和它才不多一样简单。这篇文章告诉你怎样把他们的差别联系起来。Robert Nielsen还告诉你怎样使用java来计算抵达航班和制造过程的时间。
作者:Robert Nielsen
翻译:Cocia Lin

 

这篇文章是在我发表过的<计算Java时间>(译者:已经翻译完成)的基础上的。在这里,我列出那篇文章几个你应该熟悉得关键点。如果这几点你不太清楚,我建议你读一下<计算Java时间>,了解一下。
1. Java计算时间依靠1970年1月1日开始的毫秒数.                                                                                                                                                              
2. Date类的构造函数Date()返回代表当前创建的时刻的对象。Date的方法getTime()返回一个long值在数值上等于1970年1月1日之前或之后的时刻。
3. DateFormat类用来转换Date到String,反之亦然。静态方法getDateInstance()返回DateFormat的缺省格式;getDateInstance(DateFormat.FIELD)返回指定的DateFormat对象格式。Format(Date d)方法返回String表示日期,例如"January 1,2002."反过来,parse(String s)方法返回以参数字符串表示的Date对象。
4. format()方法返回的字符串格式根据不同地区的时间设置而有所不同。
5. GregorianCalendear类有两个重要的构造函数:GregorianCalerdar(),返回代表当前创建时间的对象;GregorianCalendar(int year,int month,int date)返回代表任意日期的对象。GregorianCalendar类的getTime()方法返回日期对象。Add(int field,int amount)方法通过加或减时间单位,象天数,月数或年数来计算日期。
GregorianCalendar和 时间
 两个GregorianCalendar的构造函数可以用来处理时间。前者创建一个表示日期,小时和分钟的对象:

GregorianCalendar(int year, int month, int date, int hour, int minute)

第二个创建一个表示一个日期,小时,分钟和秒:

GregorianCalendar(int year, int month, int date, int hour, int minute, int second)

首先,我应该提醒一下,每一个构造函数需要时间信息中的日期信息(年,月,日)。如果你想说2:30 p.m.,你必须指出日期。
同样,每一个GregorianCalendar构造函数创建一个在时间上使用毫秒计算的对象。所以,如果你的构造函数只提供年,月,日参数,那小时,分钟,秒和毫秒的值将被置0.
DateFormat和时间
你可以使用静态方法getDateTimeInstance(int dateStyle,int timeStyle)来建立DateFormat对象来显示时间和日期。这个方法表明你想要的日期和时间格式。如果你喜欢使用缺省格式,可以使用getDateTimeInstance()来代替它。
你可以使用静态方法getTimeInstance(int timeStyle)创建DateFormat对象来显示正确的时间。
下面的程序示范了getDateTimeInstance()和getTimeInstance()怎样工作:

import java.util.*;
import java.text.*;

public class Apollo {
   public static void main(String[] args) {
      GregorianCalendar liftOffApollo11 = new GregorianCalendar(1969, Calendar.JULY, 16, 9, 32);
      Date d = liftOffApollo11.getTime();
      DateFormat df1 = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM);
      DateFormat df2 = DateFormat.getTimeInstance(DateFormat.SHORT);
      String s1 = df1.format(d);
      String s2 = df2.format(d);
      System.out.println(s1);
      System.out.println(s2);
   }
}  
       
在我的电脑上,上面的程序显示如下:

Jul 16, 1969 9:32:00 AM
9:32 AM
(输出根据你所在得地区有所不同)

计算时间间隔
     你可能有时需要计算过去的时间;例如,给你开始和结束时间,你想知道制造流程的持续时间。一个出租公司按小时或天数出租东西,计算时间对他们也很有用。同样的,在金融界,经常需要计算重要的支付时间。
将问题复杂化,人类至少是用两种方法计算时间。你可以说一天已经结束当24小时过去了,或者日历从今天翻到明天。我们将讨论我们想到的这两种情况。
时间段,情况 1:严格时间单位
在这种情况中,只有24小时过去,这天才过去,60分钟过去,这个小时才过去,60秒过去,这个分钟才过去,以此类推。在这个方法中,23小时的时间将被认为是0天。
使用这种方法计算时间段,你从计算过去的毫秒开始。为了做到这一点,首先转换每个日期为从1970年1月1日起得毫秒数。你可以从第二个毫秒值中减去第一个毫秒值。这里有一个简单的计算:

import java.util.*;

public class ElapsedMillis {
   public static void main(String[] args) {
      GregorianCalendar gc1 = new GregorianCalendar(1995, 11, 1, 3, 2, 1);
      GregorianCalendar gc2 = new GregorianCalendar(1995, 11, 1, 3, 2, 2);
      // the above two dates are one second apart
      Date d1 = gc1.getTime();
      Date d2 = gc2.getTime();
      long l1 = d1.getTime();
      long l2 = d2.getTime();
      long difference = l2 – l1;
      System.out.println("Elapsed milliseconds: " + difference);
   }
}    

上面的程序打印如下:

Elapsed milliseconds: 1000

这个程序也带来一点混淆。GregorianCalendar类的getTime()返回一个Date对象,Date类的getTime()方法返回从1970年1月1日到这个时间的long类型的毫秒数值。虽然他们的方法名字相同,返回值却不一样!
下面的程序片断用简单的整数除法转换毫秒到秒:

long milliseconds = 1999;
long seconds = 1999 / 1000;

这种方法舍去小数部分转换毫秒到秒,所以1,999毫秒等于1秒,2,000毫秒等于2秒。
计算更大的单位-例如天数,小时和分钟-给定一个时间数值,可以使用下面的过程:
1. 计算最大的单位,减去这个数值的秒数
2. 计算第二大单位,减去这个数值的秒数
3. 重复操作直到只剩下秒
例如,如果你的时间的10,000秒,你想知道这个数值相应的是多少小时,多少分钟,多少秒,你从最大的单位开始:小时。10,000除以3600(一个小时的秒数)得到小时数。使用整数除法,答案是2小时(整数除法中小数舍去)计算剩下的秒数,10,000-(3,600 x 2) = 2,800秒。所以你有2小时和2,800秒。
将2,800秒转换成分钟,2,800除以60。使用整数除法,答案是46。2,800 – (60 x 46) = 40秒。最后答案是2小时,46分,40秒。
下面的Java程序使用上面的计算方法:

import java.util.*;

public class Elapsed1 {
   public void calcHMS(int timeInSeconds) {
      int hours, minutes, seconds;
      hours = timeInSeconds / 3600;
      timeInSeconds = timeInSeconds – (hours * 3600);
      minutes = timeInSeconds / 60;
      timeInSeconds = timeInSeconds – (minutes * 60);
      seconds = timeInSeconds;
      System.out.println(hours + " hour(s) " + minutes + " minute(s) " + seconds + " second(s)");
   }

   public static void main(String[] args) {
      Elapsed1 elap = new Elapsed1();
      elap.calcHMS(10000);
   }


输出结果如下:

2 hour(s) 46 minute(s) 40 second(s)

上面的程序甚至在时间少于一个小时也可以正确的计算小时数。例如,你用上面的程序计算1,000秒,输出入下:
0 hour(s) 16 minute(s) 40 second(s)
举一个现实世界的例子,下面的程序计算阿波罗11飞到月球使用得时间:

import java.util.*;

public class LunarLanding {

   public long getElapsedSeconds(GregorianCalendar gc1, GregorianCalendar gc2) {
      Date d1 = gc1.getTime();
      Date d2 = gc2.getTime();
      long l1 = d1.getTime();
      long l2 = d2.getTime();
      long difference = Math.abs(l2 – l1);
      return difference / 1000;
   }

   public void calcHM(long timeInSeconds) {
      long hours, minutes, seconds;
      hours = timeInSeconds / 3600;
      timeInSeconds = timeInSeconds – (hours * 3600);
      minutes = timeInSeconds / 60;
      System.out.println(hours + " hour(s) " + minutes + " minute(s)" );
   }

   public static void main(String[] args) {
      GregorianCalendar lunarLanding = new GregorianCalendar(1969, Calendar.JULY, 20, 16, 17);
      GregorianCalendar lunarDeparture = new GregorianCalendar(1969, Calendar.JULY, 21, 13, 54);
      GregorianCalendar startEVA = new GregorianCalendar(1969, Calendar.JULY, 20, 22, 56);
      GregorianCalendar endEVA = new GregorianCalendar(1969, Calendar.JULY, 21, 1, 9);

      LunarLanding apollo = new LunarLanding();

      long eva = apollo.getElapsedSeconds(startEVA, endEVA);
      System.out.print("EVA duration = ");
      apollo.calcHM(eva);

      long lunarStay = apollo.getElapsedSeconds(lunarLanding, lunarDeparture);
      System.out.print("Lunar stay = ");
      apollo.calcHM(lunarStay);
   }
}         

上面程序输出如下:

EVA duration = 2 hour(s) 13 minute(s)
Lunar stay = 21 hour(s) 37 minute(s)

目前为止,我们计算的基础公式是这样的:1分钟=60秒,1小时=60分,1天=24小时。
"1个月=?天,1年=?天"怎么办?
月份的天数有28,29,30,31;一年可以是365或366天。因此,当你试图计算严格单位的月份和年时,问题就产生了。例如,如果你使用月份的平均天数(近似30.4375),并且计算下面的时间间隔:

* July 1, 2:00 a.m. to July 31, 10:00 p.m.
* February 1, 2:00 a.m. to February 29, 10:00 p.m.

第一个计算结果是1个月;第二个结果是0个月!
所以,在计算严格单位时间的月份和年份是要想好。
时间段,情况 2:时间单位变化
时间单位的变化相当的简单:如果你要统计天数,你可以简单的统计日期变化次数。例如,如果某事15日开始,17日结束,经过2天。(日期先是便到16,再到17)同样的,一个步骤下午3:25开始,4:10 p.m结束,历时1个小时,因为小时数值变了一次(从3到4)。
图书馆经常使用这种习惯计算时间。例如,如果你从图书馆接一本书,我不能占有这本书最少24小时,会认为图书馆这样才给你算一天。而是,我的账号上记录我借书的日期。日期以变成下一天,我就已经结这本书一天了,即使总计不足24小时。
当使用单位的变化来计算时间段,通常感觉计算的时间没有多于一个时间单位。例如,如果9:00 p.m.我借了一本图书馆的书,第二天中午还回去,我能算出我借了这本书一天了。可是,有一种感觉在问:"1天和几个小时呢?"这本说总计借出15个小时,答案是一天还差9个小时呢?因此,这篇文章里,我将以一个时间单位变化计算时间。

2005年05月30日

session的概念及实现

一直觉得自己对servlet的基础好欠缺。javaeye看到的一个帖子,弥补对session的理解
HTTP协议(http://www.w3.org/Protocols/)是"一次性单向"协议。
服务端不能主动连接客户端,只能被动等待并答复客户端请求。客户端连接服务端,发出一个HTTP Request,服务端处理请求,并且返回一个HTTP Response给客户端,本次HTTP Request-Response Cycle结束。
我们看到,HTTP协议本身并不能支持服务端保存客户端的状态信息。于是,Web Server中引入了session的概念,用来保存客户端的状态信息。
这里用一个形象的比喻来解释session的工作方式。假设Web Server是一个商场的存包处,HTTP Request是一个顾客,第一次来到存包处,管理员把顾客的物品存放在某一个柜子里面(这个柜子就相当于Session),然后把一个号码牌交给这个顾客,作为取包凭证(这个号码牌就是Session ID)。顾客(HTTP Request)下一次来的时候,就要把号码牌(Session ID)交给存包处(Web Server)的管理员。管理员根据号码牌(Session ID)找到相应的柜子(Session),根据顾客(HTTP Request)的请求,Web Server可以取出、更换、添加柜子(Session)中的物品,Web Server也可以让顾客(HTTP Request)的号码牌和号码牌对应的柜子(Session)失效。顾客(HTTP Request)的忘性很大,管理员在顾客回去的时候(HTTP Response)都要重新提醒顾客记住自己的号码牌(Session ID)。这样,顾客(HTTP Request)下次来的时候,就又带着号码牌回来了。
我们可以看到,Session ID实际上是在客户端和服务端之间通过HTTP Request和HTTP Response传来传去的。

我们看到,号码牌(Session ID)必须包含在HTTP Request里面。关于HTTP Request的具体格式,请参见HTTP协议(http://www.w3.org/Protocols/)。这里只做一个简单的介绍。
在Java Web Server(即Servlet/JSP Server)中,Session ID用jsessionid表示(请参见Servlet规范)。
HTTP Request一般由3部分组成:
(1)Request Line
这一行由HTTP Method(如GET或POST)、URL、和HTTP版本号组成。
例如,GET http://www.w3.org/pub/WWW/TheProject.html HTTP/1.1
GET http://www.google.com/search?q=Tomcat HTTP/1.1
POST http://www.google.com/search HTTP/1.1
GET http://www.somsite.com/menu.do;jsessionid=1001 HTTP/1.1

(2)Request Headers
这部分定义了一些重要的头部信息,如,浏览器的种类,语言,类型。Request Headers中还可以包括Cookie的定义。例如:
User-Agent: Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0)
Accept-Language: en-us
Cookie: jsessionid=1001

(3)Message Body
如果HTTP Method是GET,那么Message Body为空。
如果HTTP Method是POST,说明这个HTTP Request是submit一个HTML Form的结果,
那么Message Body为HTML Form里面定义的Input属性。例如,
user=guest
password=guest
jsessionid=1001
主意,如果把HTML Form元素的Method属性改为GET。那么,Message Body为空,所有的Input属性都会加在URL的后面。你在浏览器的URL地址栏中会看到这些属性,类似于
http://www.somesite/login.do?user=guest&password=guest&jsessionid=1001

从理论上来说,这3个部分(Request URL,Cookie Header, Message Body)都可以用来存放Session ID。由于Message Body方法必须需要一个包含Session ID的HTML Form,所以这种方法不通用。
一般用来实现Session的方法有两种:
(1)URL重写。
Web Server在返回Response的时候,检查页面中所有的URL,包括所有的连接,和HTML Form的Action属性,在这些URL后面加上";jsessionid=XXX"。
下一次,用户访问这个页面中的URL。jsessionid就会传回到Web Server。
(2)Cookie。
如果客户端支持Cookie,Web Server在返回Response的时候,在Response的Header部分,加入一个"set-cookie: jsessionid=XXXX"header属性,把jsessionid放在Cookie里传到客户端。
客户端会把Cookie存放在本地文件里,下一次访问Web Server的时候,再把Cookie的信息放到HTTP Request的"Cookie"header属性里面,这样jsessionid就随着HTTP Request返回给Web Server。

我们来看Tomcat5的源代码如何支持jsessionid。
org.apache.coyote.tomcat5.CoyoteResponse类的toEncoded()方法支持URL重写。
String toEncoded(String url, String sessionId) {

StringBuffer sb = new StringBuffer(path);
if( sb.length() > 0 ) { // jsessionid can’t be first.
sb.append(";jsessionid=");
sb.append(sessionId);
}
sb.append(anchor);
sb.append(query);
return (sb.toString());
}

我们来看org.apache.coyote.tomcat5.CoyoteRequest的两个方法configureSessionCookie()
doGetSession()用Cookie支持jsessionid.

/**
* Configures the given JSESSIONID cookie.
*
* @param cookie The JSESSIONID cookie to be configured
*/
protected void configureSessionCookie(Cookie cookie) {

}

HttpSession doGetSession(boolean create){

// Creating a new session cookie based on that session
if ((session != null) && (getContext() != null)
&& getContext().getCookies()) {
Cookie cookie = new Cookie(Globals.SESSION_COOKIE_NAME,
session.getId());
configureSessionCookie(cookie);
((HttpServletResponse) response).addCookie(cookie);
}

}

Session的典型应用是存放用户的Login信息,如用户名,密码,权限角色等信息,应用程序(如Email服务、网上银行等系统)根据这些信息进行身份验证和权限验证

 java面试题集

tnnd,发现很多公司的面试题都重复,包括花期软件等等,搜集一下,希望能派上用场,以后要面试的java程序员可以看看

JSP方面
Servlet方面
Jdbc、Jdo方面
Xml方面
EJB方面
J2EE,MVC方面
CORBA方面
LINUX方面


**

Jsp方面

1、jsp有哪些内置对象?作用分别是什么?

答:JSP共有以下9种基本内置组件(可与ASP的6种内部组件相对应):

 request 用户端请求,此请求会包含来自GET/POST请求的参数

response 网页传回用户端的回应

pageContext 网页的属性是在这里管理

session 与请求有关的会话期

application servlet 正在执行的内容

out 用来传送回应的输出

config servlet的构架部件

page JSP网页本身

exception 针对错误网页,未捕捉的例外

2、jsp有哪些动作?作用分别是什么?

答:JSP共有以下6种基本动作

jsp:include:在页面被请求的时候引入一个文件。

jsp:useBean:寻找或者实例化一个JavaBean。

jsp:setProperty:设置JavaBean的属性。

jsp:getProperty:输出某个JavaBean的属性。

jsp:forward:把请求转到一个新的页面。

jsp:plugin:根据浏览器类型为Java插件生成OBJECT或EMBED标记

3、JSP中动态INCLUDE与静态INCLUDE的区别?

答:动态INCLUDE用jsp:include动作实现

<jsp:include page="included.jsp" flush="true" />它总是会检查所含文件中的变化,适合用于包含动态页面,并且可以带参数

静态INCLUDE用include伪码实现,定不会检查所含文件的变化,适用于包含静态页面

<%@ include file="included.htm" %>

4、两种跳转方式分别是什么?有什么区别?

答:有两种,分别为:

<jsp:include page="included.jsp" flush="true">

<jsp:forward page= "nextpage.jsp"/>

前者页面不会转向include所指的页面,只是显示该页的结果,主页面还是原来的页面。执行完后还会回来,相当于函数调用。并且可以带参数.后者完全转向新页面,不会再回来。相当于go to 语句。

Servlet方面

1、说一说Servlet的生命周期?

答:servlet有良好的生存期的定义,包括加载和实例化、初始化、处理请求以及服务结束。这个生存期由javax.servlet.Servlet接口的init,service和destroy方法表达。

2、Servlet版本间(忘了问的是哪两个版本了)的不同?

希望大家补上,谢谢

3、JAVA SERVLET API中forward() 与redirect()的区别?

答:前者仅是容器中控制权的转向,在客户端浏览器地址栏中不会显示出转向后的地址;后者则是完全的跳转,浏览器将会得到跳转的地址,并重新发送请求链接。这样,从浏览器的地址栏中可以看到跳转后的链接地址。所以,前者更加高效,在前者可以满足需要时,尽量使用forward()方法,并且,这样也有助于隐藏实际的链接。在有些情况下,比如,需要跳转到一个其它服务器上的资源,则必须使用sendRedirect()方法。

4、Servlet的基本架构

public class ServletName extends HttpServlet {

public void doPost(HttpServletRequest request, HttpServletResponse response) throws

ServletException, IOException {

}

public void doGet(HttpServletRequest request, HttpServletResponse response) throws

ServletException, IOException {

}

}

 

Jdbc、Jdo方面

1、可能会让你写一段Jdbc连Oracle的程序,并实现数据查询.

答:程序如下:

package hello.ant;

import java.sql.*;

public class jdbc

{

String dbUrl="jdbc:oracle:thin:@127.0.0.1:1521:orcl";

String theUser="admin";

String thePw="manager";

Connection c=null;

Statement conn;

ResultSet rs=null;

public jdbc()

{

try{

Class.forName("oracle.jdbc.driver.OracleDriver").newInstance();

c = DriverManager.getConnection(dbUrl,theUser,thePw);

conn=c.createStatement();

}catch(Exception e){

e.printStackTrace();

}

}

public boolean executeUpdate(String sql)

{

try

{

conn.executeUpdate(sql);

return true;

}

catch (SQLException e)

{

e.printStackTrace();

return false;

}

}

public ResultSet executeQuery(String sql)

{

rs=null;

try

{

rs=conn.executeQuery(sql);

}

catch (SQLException e)

{

e.printStackTrace();

}

return rs;

}

public void close()

{

try

{

conn.close();

c.close();

}

catch (Exception e)

{

e.printStackTrace();

}

}

public static void main(String[] args)

{

ResultSet rs;

jdbc conn = new jdbc();

rs=conn.executeQuery("select * from test");

try{

while (rs.next())

{

System.out.println(rs.getString("id"));

System.out.println(rs.getString("name"));

}

}catch(Exception e)

{

e.printStackTrace();

}

}

}

2、Class.forName的作用?为什么要用?

答:调用该访问返回一个以字符串指定类名的类的对象。

3、Jdo是什么?

答:JDO是Java对象持久化的新的规范,为java data object的简称,也是一个用于存取某种数据仓库中的对象的标准化API。JDO提供了透明的对象存储,因此对开发人员来说,存储数据对象完全不需要额外的代码(如JDBC API的使用)。这些繁琐的例行工作已经转移到JDO产品提供商身上,使开发人员解脱出来,从而集中时间和精力在业务逻辑上。另外,JDO很灵活,因为它可以在任何数据底层上运行。JDBC只是面向关系数据库(RDBMS)JDO更通用,提供到任何数据底层的存储功能,比如关系数据库、文件、XML以及对象数据库(ODBMS)等等,使得应用可移植性更强。

4、在ORACLE大数据量下的分页解决方法。一般用截取ID方法,还有是三层嵌套方法。

答:一种分页方法

<%

int i=1;

int numPages=14;

String pages = request.getParameter("page") ;

int currentPage = 1;

currentPage=(pages==null)?(1):{Integer.parseInt(pages)}

sql = "select count(*) from tables";

ResultSet rs = DBLink.executeQuery(sql) ;

while(rs.next()) i = rs.getInt(1) ;

int intPageCount=1;

intPageCount=(i%numPages==0)?(i/numPages):(i/numPages+1);

int nextPage ;

int upPage;

nextPage = currentPage+1;

if (nextPage>=intPageCount) nextPage=intPageCount;

upPage = currentPage-1;

if (upPage<=1) upPage=1;

rs.close();

sql="select * from tables";

rs=DBLink.executeQuery(sql);

i=0;

while((i<numPages*(currentPage-1))&&rs.next()){i++;}

%>

//输出内容

//输出翻页连接

合计:<%=currentPage%>/<%=intPageCount%><a href="List.jsp?page=1">第一页</a><a

 

href="List.jsp?page=<%=upPage%>">上一页</a>

<%

for(int j=1;j<=intPageCount;j++){

if(currentPage!=j){

%>

<a href="list.jsp?page=<%=j%>">[<%=j%>]</a>

<%

}else{

out.println(j);

}

}

%>

<a href="List.jsp?page=<%=nextPage%>">下一页</a><a href="List.jsp?page=<%=intPageCount%>">最后页

 

</a>


 

Xml方面

1、xml有哪些解析技术?区别是什么?

答:有DOM,SAX,STAX等

DOM:处理大型文件时其性能下降的非常厉害。这个问题是由DOM的树结构所造成的,这种结构占用的内存较多,而且DOM必须在解析文件之前把整个文档装入内存,适合对XML的随机访问SAX:不现于DOM,SAX是事件驱动型的XML解析方式。它顺序读取XML文件,不需要一次全部装载整个文件。当遇到像文件开头,文档结束,或者标签开头与标签结束时,它会触发一个事件,用户通过在其回调事件中写入处理代码来处理XML文件,适合对XML的顺序访问

STAX:Streaming API for XML (StAX)

2、你在项目中用到了xml技术的哪些方面?如何实现的?

答:用到了数据存贮,信息配置两方面。在做数据交换平台时,将不能数据源的数据组装成XML文件,然后将XML文件压缩打包加密后通过网络传送给接收者,接收解密与解压缩后再同XML文件中还原相关信息进行处理。在做软件配置时,利用XML可以很方便的进行,软件的各种配置参数都存贮在XML文件中。

3、用jdom解析xml文件时如何解决中文问题?如何解析?

答:看如下代码,用编码方式加以解决

package test;

import java.io.*;

public class DOMTest

{

private String inFile = "c:\people.xml";

private String outFile = "c:\people.xml";

public static void main(String args[])

{

new DOMTest();

}

public DOMTest()

{

try

{

javax.xml.parsers.DocumentBuilder builder =

javax.xml.parsers.DocumentBuilderFactory.newInstance().newDocumentBuilder();

org.w3c.dom.Document doc = builder.newDocument();

org.w3c.dom.Element root = doc.createElement("老师");

org.w3c.dom.Element wang = doc.createElement("王");

org.w3c.dom.Element liu = doc.createElement("刘");

wang.appendChild(doc.createTextNode("我是王老师"));

root.appendChild(wang);

doc.appendChild(root);

javax.xml.transform.Transformer transformer =

javax.xml.transform.TransformerFactory.newInstance().newTransformer();

transformer.setOutputProperty(javax.xml.transform.OutputKeys.ENCODING, "gb2312");

transformer.setOutputProperty(javax.xml.transform.OutputKeys.INDENT, "yes");

 

 

transformer.transform(new javax.xml.transform.dom.DOMSource(doc),

new

 

javax.xml.transform.stream.StreamResult(outFile));

}

catch (Exception e)

{

System.out.println (e.getMessage());

}

}

}

4、编程用JAVA解析XML的方式.

答:用SAX方式解析XML,XML文件如下:

<?xml version="1.0" encoding="gb2312"?>

<person>

<name>王小明</name>

<college>信息学院</college>

<telephone>6258113</telephone>

<notes>男,1955年生,博士,95年调入海南大学</notes>

</person>

事件回调类SAXHandler.java

import java.io.*;

import java.util.Hashtable;

import org.xml.sax.*;

public class SAXHandler extends HandlerBase

{

private Hashtable table = new Hashtable();

private String currentElement = null;

private String currentValue = null;

public void setTable(Hashtable table)

{

this.table = table;

}

public Hashtable getTable()

{

return table;

}

public void startElement(String tag, AttributeList attrs)

throws SAXException

{

currentElement = tag;

}

public void characters(char[] ch, int start, int length)

throws SAXException

{

currentValue = new String(ch, start, length);

}

public void endElement(String name) throws SAXException

{

if (currentElement.equals(name))

table.put(currentElement, currentValue);

}

}

JSP内容显示源码,SaxXml.jsp:

<HTML>

<HEAD>

<TITLE>剖析XML文件people.xml</TITLE>

</HEAD>

<BODY>

<%@ page errorPage="ErrPage.jsp"

contentType="text/html;charset=GB2312" %>

<%@ page import="java.io.*" %>

<%@ page import="java.util.Hashtable" %>

<%@ page import="org.w3c.dom.*" %>

<%@ page import="org.xml.sax.*" %>

<%@ page import="javax.xml.parsers.SAXParserFactory" %>

<%@ page import="javax.xml.parsers.SAXParser" %>

<%@ page import="SAXHandler" %>

<%

File file = new File("c:\people.xml");

FileReader reader = new FileReader(file);

Parser parser;

SAXParserFactory spf = SAXParserFactory.newInstance();

SAXParser sp = spf.newSAXParser();

SAXHandler handler = new SAXHandler();

sp.parse(new InputSource(reader), handler);

Hashtable hashTable = handler.getTable();

out.println("<TABLE BORDER=2><CAPTION>教师信息表</CAPTION>");

out.println("<TR><TD>姓名</TD>" + "<TD>" +

(String)hashTable.get(new String("name")) + "</TD></TR>");

out.println("<TR><TD>学院</TD>" + "<TD>" +

(String)hashTable.get(new String("college"))+"</TD></TR>");

out.println("<TR><TD>电话</TD>" + "<TD>" +

(String)hashTable.get(new String("telephone")) + "</TD></TR>");

out.println("<TR><TD>备注</TD>" + "<TD>" +

(String)hashTable.get(new String("notes")) + "</TD></TR>");

out.println("</TABLE>");

%>

</BODY>

</HTML>

EJB方面

1、EJB2.0有哪些内容?分别用在什么场合? EJB2.0和EJB1.1的区别?

答:规范内容包括Bean提供者,应用程序装配者,EJB容器,EJB配置工具,EJB服务提供者,系统管理员。这里面,EJB容器是EJB之所以能够运行的核心。EJB容器管理着EJB的创建,撤消,激活,去活,与数据库的连接等等重要的核心工作。JSP,Servlet,EJB,JNDI,JDBC,JMS…..

2、EJB与JAVA BEAN的区别?

答:Java Bean 是可复用的组件,对Java Bean并没有严格的规范,理论上讲,任何一个Java类都可以是一个Bean。但通常情况下,由于Java Bean是被容器所创建(如Tomcat)的,所以Java Bean应具有一个无参的构造器,另外,通常Java Bean还要实现Serializable接口用于实现Bean的持久性。Java Bean实际上相当于微软COM模型中的本地进程内COM组件,它是不能被跨进程访问的。Enterprise Java Bean 相当于DCOM,即分布式组件。它是基于Java的远程方法调用(RMI)技术的,所以EJB可以被远程访问(跨进程、跨计算机)。但EJB必须被布署在诸如Webspere、WebLogic这样的容器中,EJB客户从不直接访问真正的EJB组件,而是通过其容器访问。EJB容器是EJB组件的代理,EJB组件由容器所创建和管理。客户通过容器来访问真正的EJB组件。

3、EJB的基本架构

答:一个EJB包括三个部分:

Remote Interface 接口的代码

package Beans;

import javax.ejb.EJBObject;

import java.rmi.RemoteException;

public interface Add extends EJBObject

{

//some method declare

}

Home Interface 接口的代码

package Beans;

import java.rmi.RemoteException;

import jaax.ejb.CreateException;

import javax.ejb.EJBHome;

public interface AddHome extends EJBHome

{

//some method declare

}

EJB类的代码

package Beans;

import java.rmi.RemoteException;

import javax.ejb.SessionBean;

import javx.ejb.SessionContext;

public class AddBean Implements SessionBean

{

//some method declare

}

 

J2EE,MVC方面

1、MVC的各个部分都有那些技术来实现?如何实现?

答:MVC是Model-View-Controller的简写。"Model" 代表的是应用的业务逻辑(通过JavaBean,EJB组件实现), "View" 是应用的表示面(由JSP页面产生),"Controller" 是提供应用的处理过程控制(一般是一个Servlet),通过这种设计模型把应用逻辑,处理过程和显示逻辑分成不同的组件实现。这些组件可以进行交互和重用。

2、应用服务器与WEB SERVER的区别?

希望大家补上,谢谢

3、J2EE是什么?

答:Je22是Sun公司提出的多层(multi-diered),分布式(distributed),基于组件(component-base)的企业级应用模型(enterpriese application model).在这样的一个应用系统中,可按照功能划分为不同的组件,这些组件又可在不同计算机上,并且处于相应的层次(tier)中。所属层次包括客户层(clietn tier)组件,web层和组件,Business层和组件,企业信息系统(EIS)层。

4、WEB SERVICE名词解释。JSWDL开发包的介绍。JAXP、JAXM的解释。SOAP、UDDI,WSDL解释。

2005年05月05日

突然发生的心动过速,一般是指阵发性心动过速。有房性和室性两种,需要通过心电图的特征给予鉴别。 一般来说,短暂的心动过速且无明显症状,可不做特殊治疗。持续发作或有器质性心脏病者,应当采取措施给予急救。 房性心动过速(室上性心动过速)多见于无器质性心脏病的青中年人,常因情绪激动、精神紧张、过度劳累、烟酒刺激所诱发。有的人可因.胃肠功能失调、体位改变、吞咽过急所诱发。发作时间和频率因人而异。可为数秒、数小时、数日,有的人可达十几天。既可偶发,也可持续发生。心率可达160次/分~240次/分,脉律整齐,心音呈钟摆律。发作时病人常有恐惧感,心悸,头晕,心前区不适,甚至出现昏厥、休克、心力衰竭。 发生心动过速时,可按以下方法处理: 1.刺激法用压舌板或手指刺激悬雍垂;诱发恶心、呕吐。 2.屏气法深吸气后屏气,然后再厉力呼气,以此增加腹压。 3.变体位身体前弯或平卧,以腿高举悬空。 4.按摩颈动脉窦病人仰卧,头偏向左侧,颈后垫一软枕,他人以手指按压病人的颈动脉窦(在颈动脉相当于甲状软骨上绿水平位,血管搏动最明显处)-,上下旋转反复按摩,动作要轻柔,每次10秒~15秒,注意心率改变。无效时可休息片刻,改作另一侧颈动脉窦。 5.压迫眼球病人平卧位,闭眼向下看,他人用拇指在一侧框下适度用力压迫眼球上部,不可压迫角膜,略有疼痛,每次压迫10秒~30秒,心率一旦减慢,就要停止按压。 6.针刺治疗针刺内关、通里、三阴交穴,中强刺激。也可用耳针,取心区、神门、内分泌,埋压2~4小时。 7.药物治疗由医生合理选择药物。 发生室性心动过速时,可按下列方法处理: 1.体外同步直流电复律由医生操作。 2.药物治疗可选利多卡因、漫心律等,由医生来选择。 3.发生休克时,应先使用或同时使用升压药。 4.伴有传导阻滞时,应选心内膜起搏治疗。 5.手术治疗。 一旦发生时间较长,就应立即送往医院,以防发生弊死。

2005年05月02日

在面向对象的编程中,软件编程人员更加注重以前的代码的重用性和可维护性。

设计模式使人们可以更加简单方便地复用成功的设计和体系结构。将已证实的技术表述成设计模式也会使新系统开发者更加容易理解其设计思路。

一般而言,一个模式有四个基本要素

1. 模式名称(pattern name) 一个助记名,它用一两个词来描述模式的问题、解决方案和效果。命名一个新的模式增加了我们的设计词汇。设计模式允许我们在较高的抽象层次上进行设计。基于一个模式词汇表,我们自己以及同事之间就可以讨论模式并在编写文档时使用它们。模式名可以帮助我们思考,便于我们与其他人交流设计思想及设计结果。找到恰当的模式名也是我们设计模式编目工作的难点之一。

2. 问题(problem) 描述了应该在何时使用模式。它解释了设计问题和问题存在的前因后果,它可能描述了特定的设计问题,如怎样用对象表示算法等。也可能描述了导致不灵活设计的类或对象结构。有时候,问题部分会包括使用模式必须满足的一系列先决条件。

3. 解决方案(solution) 描述了设计的组成成分,它们之间的相互关系及各自的职责和协作方式。因为模式就像一个模板,可应用于多种不同场合,所以解决方案并不描述一个特定而具体的设计或实现,而是提供设计问题的抽象描述和怎样用一个具有一般意义的元素组合(类或对象组合)来解决这个问题。

4. 效果(consequences) 描述了模式应用的效果及使用模式应权衡的问题。尽管我们描述设计决策时,并不总提到模式效果,但它们对于评价设计选择和理解使用模式的代价及好处具有重要意义。软件效果大多关注对时间和空间的衡量,它们也表述了语言和实现问题。因为复用是面向对象设计的要素之一,所以模式效果包括它对系统的灵活性、扩充性或可移植性的影响,显式地列出这些效果对理解和评价这些模式很有帮助。

一些基本的设计模式

Abstract Factory:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

Adapter:将一个类的接口转换成客户希望的另外一个接口。A d a p t e r模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

Bridge:将抽象部分与它的实现部分分离,使它们都可以独立地变化。

Builder:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

Chain of Responsibility:为解除请求的发送者和接收者之间耦合,而使多个对象都有机会处理这个请求。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它。

Command:将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可取消的操作。

Composite:将对象组合成树形结构以表示“部分-整体”的层次结构。它使得客户对单个对象和复合对象的使用具有一致性。

Decorator:动态地给一个对象添加一些额外的职责。就扩展功能而言, 它比生成子类方式更为灵活。

Facade:为子系统中的一组接口提供一个一致的界面, F a c a d e模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。

Factory Method:定义一个用于创建对象的接口,让子类决定将哪一个类实例化。Factory Method使一个类的实例化延迟到其子类。

Flyweight:运用共享技术有效地支持大量细粒度的对象。

Interpreter:给定一个语言, 定义它的文法的一种表示,并定义一个解释器, 该解释器使用该表示来解释语言中的句子。

Iterator:提供一种方法顺序访问一个聚合对象中各个元素, 而又不需暴露该对象的内部表示。

Mediator:用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。

Memento:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到保存的状态。

Observer:定义对象间的一种一对多的依赖关系,以便当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动刷新。

Prototype:用原型实例指定创建对象的种类,并且通过拷贝这个原型来创建新的对象。

Proxy:为其他对象提供一个代理以控制对这个对象的访问。

Singleton:保证一个类仅有一个实例,并提供一个访问它的全局访问点。

State:允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它所属的类。

Strategy:定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。本模式使得算法的变化可独立于使用它的客户。

Template Method:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。Template Method使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

Visitor:表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。

一、软件重用定义


  软件重用(Software Reuse,又称软件复用或软件再用)的概念对于大家并不陌生。早在1968年的NATO软件工程会议上就已经提出可复用库的思想。软件重用的定义也很多,比较权威和通用的一种是:软件重用是利用事先建立好的软部品创建新软件系统的过程。这个定义蕴含着软件重用所必须包含的两个方面:


  1. 系统地开发可重用的软部品。这些软部品可以是代码,但不应该仅仅局限在代码。我们必须从更广泛和更高层次来理解,这样才会带来更大的重用收益。比如软部品还可以是:分析,设计,测试数据,原型,计划,文档,模板,框架等等。


  2. 系统地使用这些软部品作为构筑模块,来建立新的系统。


二、软件重用的好处


  软件重用会带来以下好处:
  1. 提高软件生成率。
  2. 缩短开发周期 。
  3. 降低软件开发和维护费用。
  4. 生产更加标准化的软件。
  5. 提高软件开发质量。
  6. 增强软件系统的互操作性。
  7. 减少软件开发人员数量。
  8. 使开发人员能比较容易的适应不同性质的项目开发。


三、软件重用形式


  软件重用的形式(或手段)很多,重用的级别有大有小。主要有以下几种:
  1. 源代码模块或者类一级的重用。这是最基本的软件重用形式。
  2. 二进制形式的重用。如组件重用。
  3. 组装式重用。比如:把好几个应用程序的功能集成在一起。例如,要建立一个门户站点应用,登陆用户既可以查询天气情况,又可以查看股市行情,还可以在线购物。这些功能由不同网络应用服务供应商提供,通过这种组装式重用,就可以非常容易地把上述功能都集成到新的门户站点中。
  4. 分析级别重用。
  5. 设计级别重用。
  6. 软件文档重用。


四、软件重用分类


  为软件重用分类比较困难,因为软件重用技术众多,一种重用技术可以包括多种重用形式。比如说:框架即可以包括代码级重用,也可以包括设计级重用。有一种分类方法是按照软件重用所应用的领域范围,把重用划分为两种:横向重用和纵向重用。


  1. 横向重用是指重用不同应用领域中的软件元素,例如数据结构、分类算法、人机界面构件等。标准函数库是一种典型的原始的横向重用机制。


  2. 纵向重用是指在一类具有较多公共性的应用领域之间进行软部品重用。因为在两个截然不同的应用领域之间实施软件重用非常困难,潜力不大,所以纵向重用才广受瞩目,并成为软件重用技术的真正所在。纵向重用活动的主要包括以下几个步骤:
  1) 首先进行域分析。根据应用领域的特征及相似性预测软部件的可重用性。
  2) 然后进行软部品的开发。一旦确认了软部件的重用价值,即可进行软部品的开发并对具有重用价值的软部品进行一般化,以便它们能够适应新的类似的应用领域。
  3) 最后,软部件及其文档即可进入软部品库,成为可供后续项目使用的可重用资源。


五、流行的软件重用技术


  最理想的重用技术是它的重用产品能够和用户的需求完全一致,不需要用户做任何自定义,并且能够无需用户学习就能够被使用。然而,一种重用技术能够适合今天,可能不适合明天。一个重用产品越是能够被自定义,它越是可能在一个特定的环境下被使用,但是这也需要用户进行更多的学习,研究和实践。


  自从软件重用思想产生以来,计算机科学家和软件工程师就致力与软件重用的技术的研究和实践。在30多年的时间内,出现多种软件重用技术,如:库函数,模板,面向对象、设计模式、组件、框架、构架。


  下面是应用程序框架和其它三种软件流行的重用技术的比较。


1. 库函数


  库函数是很早的软件重用技术。很多编程语言为了增强自身的功能,都提供了大量的库函数。对于库函数的使用者,他只要知道函数的名称,返回值的类型, 函数参数和函数功能就可以对其进行调用。


2. 面向对象


  面向对象技术是近三十年来学术界和工业界研究和应用的一个热点。面向对象技术通过方法、消息、类、继承、封装、和实例等机制构造软件系统,并为软件重用提供强有力的支持。面向对象方法已成为当今最有效、最先进的软件开发方法。与函数库对应,很多面向对象语言为应用程序开发者提供了易于使用的类库,如VC++中的MFC。


3. 模板


  模板相当于工业生产中所用的“模具”。有各种各样的模板(如文档模板,网页模板等),利用这些模板可以比较快速地建立对应的软件产品。模板把不变的封装在内部,对可能变化的部分提供了通用接口,由使用者来对这些接口进行设定或实现。


4. 设计模式


  设计模式作为重用设计信息的一种技术,在面向对象设计中越来越来流行。设计模式描述了在我们周围不断重复发生的问题,该问题的解决方案的核心和解决方案实施的上下文。设计模式命名一种技术并且描述它的成本和收益,共享一系列模式的开发者拥有共同的语言来描述他们的设计。


5. 构件


  普通意义上的构件应从以下几个方面来理解:
  1) 构件应是抽象的系统特征单元,具有封装性和信息隐蔽,其功能由它的接口定义。
  2) 构件可以是原子的,也可以是复合的。因此它可以是函数,过程或对象类,也可以是更大规模的单元。一个子系统是包含其它构件的构件。
  3) 构件是可配置和共享的,这是基于构件开发的基石,且构件之间能相互提供服务。


6. 构架


  普通意义上的构架应从以下几个方面来理解:
  1) 构架是与设计的同义理解,是系统原型或早期的实现。
  2) 构架是高层次的系统整体组织。
  3) 构架是关于特定技术如何合作组成一个特定系统的解释。


7. 框架


  如果把软件的构建过程看成是传统的建筑过程;框架的作用相当于为我们的房屋搭建的“架子”。框架从重用意义上说,是一个介于构件和构架之间的一个概念。构件,框架和构架三者的主要区别在于:对重用的支持程度的不同:
  1) 构件是基础,也是基于构件开发的最小单元。构件重用包括可重用构件的制作和利用可重用构件构造新构件或系统,
  2) 一个框架和构架包含多个构件。这些构件使用统一的框架(构架)接口,使得构造一个应用系统更为容易。
  3) 框架重用包括代码重用和分析设计重用,一个应用系统可能需要若干个框架的支撑,从这个意义上来说,框架也是一个“构件”的同时,框架又是一类特定领域的构架。
  4) 构架重用不仅包括代码重用和分析设计重用,更重要的是抽象层次更高的系统级重用。
  5) 框架和构架的重用层次更高,比构件更为抽象灵活,但也更难学习和使用。


六、软件重用技术的未来


  有人预测:到2003年至少70%的新应用程序将会由构筑模块(building blocks)构成,这些构筑模块(如软构件和应用程序框架)加快了产品的开发速度和企业的应变能力。以设计模式,框架和商业构件形势出现的软部品可以通过重用显著的提升开发品质和生产力,重用反过来也可以显著的降低成本和缩短软件开发周期。未来应用程序的开发依存于一个开放的,便利构件选择和装配的综合体系结构。信息系统组织必须为基于构件开发制定一项战略。

(本文转载自软件工程专家网www.21cmm.com) 

1.软件复用的特点和现状


  软件复用就是将已有的软件成分用于构造新的软件系统。可以被复用的软件成分一般称作可复用构件,无论对可复用构件原封不动地使用还是作适当的修改后再使用,只要是用来构造新软件,则都可称作复用。软件复用不仅仅是对程序的复用,它还包括对软件生产过程中任何活动所产生的制成品的复用,如项目计划、可行性报告、需求定义、分析模型、设计模型、详细说明、源程序、测试用例等等。如果是在一个系统中多次使用一个相同的软件成分,则不称作复用,而称作共享;对一个软件进行修改,使它运行于新的软硬件平台也不称作复用,而称作软件移值。 
目前及近期的未来最有可能产生显著效益的复用是对软件生命周期中一些主要开发阶段的软件制品的复用,按抽象程度的高低,可以划分为如下的复用级别:


(1)代码的复用

  包括目标代码和源代码的复用。其中目标代码的复用级别最低,历史也最久,当前大部分编程语言的运行支持系统都提供了连接(Link)、绑定(Binding)等功能来支持这种复用。源代码的复用级别略高于目标代码的复用,程序员在编程时把一些想复用的代码段复制到自己的程序中,但这样往往会产生一些新旧代码不匹配的错误。想大规模的实现源程序的复用只有依靠含有大量可复用构件的构件库。如”对象链接及嵌入”(OLE)技术,既支持在源程序级定义构件并用以构造新的系统,又使这些构件在目标代码的级别上仍然是一些独立的可复用构件,能够在运行时被灵活的得新组合为各种不同的应用。


(2)设计的复用

  设计结果比源程序的抽象级别更高,因此它的复用受实现环境的影响较少,从而使可复用构件被复用的机会更多,并且所需的修改更少。这种复用有三种途径,第一种途径是从现有系统的设计结果中提取一些可复用的设计构件,并把这些构件应用于新系统的设计;第二种途径是把一个现有系统的全部设计文档在新的软硬件平台上重新实现,也就是把一个设计运用于多个具体的实现;第三种途径是独立于任何具体的应用,有计划地开发一些可复用的设计构件。 


(3)分析的复用

  这是比设计结果更高级别的复用,可复用的分析构件是针对问题域的某些事物或某些问题的抽象程度更高的解法,受设计技术及实现条件的影响很少,所以可复用的机会更大。复用的途径也有三种,即从现有系统的分析结果中提取可复用构件用于新系统的分析;用一份完整的分析文档作输入产生针对不同软硬件平台和其它实现条件的多项设计;独立于具体应用,专门开发一些可复用的分析构件。


(4)测试信息的复用

  主要包括测试用例的复用和测试过程信息的复用。前者是把一个软件的测试用例在新的软件测试中使用,或者在软件作出修改时在新的一轮测试中使用。后者是在测试过程中通过软件工具自动地记录测试的过程信息,包括测试员的每一个操作、输入参数、测试用例及运行环境等一切信息。这种复用的级别,不便和分析、设计、编程的复用级别作准确的比较,因为被复用的不是同一事物的不同抽象层次,而是另一种信息,但从这些信息的形态看,大体处于与程序代码相当的级别。 


  由于软件生产过程主要是正向过程,即大部分软件的生产过程是使软件产品从抽象级别较高的形态向抽象级别较低的形态演化,所以较高级别的复用容易带动较低级别的复用,因而复用的级别越高,可得到的回报也越大,因此分析结果和设计结果在目前很受重视。用户可购买生产商的分析件和设计件,自己设计或编程,掌握系统的剪裁、扩充、维护、演化等活动。 


2.软件复用的根本因难


  软件复用各方面的困难,无论是技术问题还是非技术问题,都影响着软件复用的广泛实行。


(1)技术因素。

  构件与应用系统之间的差异。一些开发者开发的构件,要做到在被另一些人开发的系统中使用时正好合适,从内容到对外接口都恰好相符,或者作很少的修改,这不是一件简单的事;构件要达到一定的数量,才能支持有效的复用,而大量构件的获得需要有很高的投入和长期的积累;发现合用构件的困难,当构件达到较大的数量时,使用者要从中找到一个自己想要的构件,并断定它确实是自己需要的,不是一件轻而易举的事;基于复用的软件开发方法和软件过程是一个新的研究实践领域,需要一些新的理论、技术及支持环境,目前这方面的研究成果和实践经验都不够充分。 


(2)人的因素。

  软件开发是一种创造性工作,长期从事这个行业的人们形成了一种职业习惯:喜欢自己创造而不喜欢使用别人的东西,特别是当要对别人开发的软件作一些修改再使用时,他们常常喜欢自己另写一个。


(3)管理因素

  在软件生产的管理中,从以往沿习了一些与复用的目标很不协调的制度与政策,如计算工作量时,对复用的部分打很大的折扣,甚至不算工作量;另外,不是在项目开始时自觉地向着造就可复用构件的方向努力,而是在它完成之后,看看是否能从中找到一些可复用构件。这些弊端妨碍了复用水平的提高和复用规模的扩大,甚至会挫伤致力于复用的人员的积极性。


(4)教育因素

  在软件科学技术的教育与培训中,缺乏关于软件复用的内容,很少有这方面的专门教材及课程,即使在其它教材及课程中提到软件复用,其篇幅及内容也相当薄弱。


(5)法律因素

  在法律上还存在一些问题,例如,一个可复用构件在某个应用系统中出现了错误,而构件的开发者和应用系统的开发者不是一个厂商,那么责任应该由谁负?此外,在版权、政府政策等方面也存在一些悬而未决的问题。
另外,软件产品是一种精神产品,它的产生几乎完全是人脑思维的结果,它的价值,也几乎完全在于其中所凝结的思想;它的物质载体的制造过程与价值含量都是微不足道的。物质产品的生产受到人类制造能力的限制,现有的一却物质产品的复杂性都没有超过这种限度,软件却没有这种限制,只要人的大脑能想到的问题,都可能要求软件去解决,人脑所能思考的问题的复杂性,远远超出了人类能制造的物质产品的复杂性,因而使软件的复用更为困难。 


3. OO方法对软件复用的支持


  支持软件复用是人们对面向对象方法寄托的主要希望之一,也是这种方法受到广泛重视的主要原因之一。面向对象方法之所以特别有利于软件复用,是由于它的主要概念及原则与软件复用的要求十分吻合。


  面向对象方法从面向对象的编程发展到面向对象的分析与设计,使这种方法支持软件复用的固有特征能够从软件生命周期的前期阶段开始发挥作用,从而使OO方法对软件复用的支持达到了较高的级别。与其它软件工程方法相比,面向对象方法的一个重要优点是,它可以在整个软件生命周期达到概念、原则、术语及表示法的高度一致。这种一致性使得各个系统成分尽管在不同的开发与演化阶段有不同的形态,但可具有贯穿整个软件生命周期的良好映射。这一优点使OO方法不但能在各个级别支持软件复用,而且能对各个级别的复用形成统一的、高效的支持,达到良好的全局效果。做到这一点的必要条件是,从面向对象软件开发的前期阶段—OOA就把支持软件复用作为一个重点问题来考虑。运用OOA方法所定义的对象类具有适合作为可复用构件的许多特征,OOA结果对问题域的良好映射,使同类系统的开发者容易从问题出发,在已有的OOA结果中发现不同粒度的可复用构件。


(1)OOA模型

  OOA方法建立的系统模型分为基本模型(类图)和补充模型(主题图与交互图),强调在OOA基本模型中只表示最重要的系统建模信息,较为细节的信息则在详细说明中结出。这种表示策略使OOA基本模型体现了更高的抽象,更容易成为一个可复用的系统构架。当这个构架在不同的应用系统中复用时,在很多情况下可通过不同的详细说明体现系统之间的差异,因此对系统构件的改动较少。


(2)OOA与OOD的分工

  OOA只注重与问题域及系统责任有关的信息,OOD考虑与实现条件有关的因素。这种分工使OOA模型独立于具体的实现条件,从而使分析结果可以在问题域及系统责任相同而实现条件互异的多个系统中复用,并为从同一领域的多个系统的分析模型提炼领域模型创造了有利条件。


(3) 对象的表示

  所有的对象都用类作为其抽象描述。对象的一却信息,包括对象的属性、行为及其对外关系等等都是通过对象类来表示的。类作为一种可复用构件,在运用于不同系统时,不会出现因该类对象实例不同而使系统模型有所不同的情况。


(4) 一般-特殊结构

  引入对一般-特殊结构中多态性的表示法,从而增强了类的可复用性。通过对多态性的表示,使一个类可以在需求相似而未必完全相同的系统中被复用。 


(5)整体-部分结构

  把部分类作为可复用构件在整个类中使用,这种策略的原理与在特殊类中使用一般类是一致的,但在某些情况下,对问题域的映射比通过继承实现复用显得更为自然。另外还可通过整体-部分结构支持领域复用的策略—从整体对象中分离出一组可在领域范围内复用的属性与服务,定义为部分对象,使之成为领域复用构件。


(6)实例连接

  建议用简单的二元关系表示各种复杂关系和多元关系。这一策略使构成系统的基本成分(对象类)以及它们之间的关系在表示形式和实现技术上都是规范和一致的这种规范性和一致性对于可复用构件的组织、管理和使用,都是很有益的。


(7)类描述模板

  作为OOA详细说明主要成分的类描述模板,对于对象之间关系的描述注意到使用者与被使用者的区别,仅在使用者一端给出类之间关系的描述信息。这说明可复用构件之间的依赖关系不是对等的。因此,在继承、聚合、实例连接及消息连接等关系的使用者一端描述这些关系,有利于这些关系信息和由它们指出的被依赖成份的同时复用。在被用者一端不描述这些关系,则避免了因复用场合的不同所引起的修改。


(8)使用CASE

  由于使用CASE是对用户需求的一种规范化描述,因此它比普通形式的需求文档具有更强的可复用性。每个使用case 是对一个活动者使用系统的一项功能时的交互活动所进行描述,它具有完整性和一定的独立性,因此很适于作为可复用构件。


4. 复用技术对OO方法的支持


  面向对象的软件开发和软件复用之间的关系是相辅相成的。一方面,OO方法的基本概念、原则与技术提供了实现软件复用的有利条件;另一方面,软件复用技术也对面向对象的软件开发提供了有力的支持。


(1)类库

  在面向对象的软件开发中,类库是实现对象类复用的基本条件。人们己经开发了许多基于各种OOPL的编程类库,有力地支持了源程序级的软件复用,但要在更高的级别上实现软件复用,仅有编程类库是不够的。实现OOA结果和OOD结果的复用,必须有分析类库和设计类库的支持。为了更好地支持多个级别的软件复用,可以在OOA类库、OOD类库和OOP类库之间建立各个类在不同开发阶段的对应与演化关系。即建立一种线索,表明每个OOA的类对应着哪个(或哪些)OOD类,以及每个OOD类对应着各种OO编程语言类库中的哪个OOP类。


(2) 构件库

  类库可以看作一种特殊的可复用构件库,它为在面向对象的软件开发中实现软件复用提供了一种基本的支持。但类库只能存储和管理以类为单位的可复用构件,不能保存其它形式的构件;但是它可以更多地保持类构件之间的结构与连接关系。构件库中的可复用构件,既可以是类,也可以是其它系统单位;其组织方式,可以不考虑对象类特有的各种关系,只按一般的构件描述、分类及检索方法进行组织。在面向对象的软件开发中,可以提炼比对象类粒度更大的可复用构件,例如把某些结构或某些主题作为可复用构件;也可以提炼其它形式的构件,例如use case 或交互图。这些构件库中,构件的形式及内容比类库更丰富,可为面向对象的软件开发担供更强的支持。


(3)构架库

  如果在某个应用领域中已经运用OOA技术建立过一个或几个系统的OOA模型,则每个OOA模型都应该保存起来,为该领域新系统的开发提供参考。当一个领域已有多个OOA模型时,可以通过进一步抽象而产生一个可复用的软件构架。形成这种可复用软件构架的更正规的途径是开展领域分析。通过正规的领域分析获得的软件构架将更准确地反映一个领域中各个应用系统的共性,具有更强的可复用价值。


(4)工具

  有效的实行软件复用需要有一些支持复用的软件工具,包括类库或构件/构架库的管理、维护与浏览工具,构件提取及描述工具,以及构件检索工具等等。以复用支持为背景的OOA工具和OOD工具在设计上也有相应的要求,工具对OOA/OOD过程的支持功能应包括:从类库或构件/构架库中寻找可复用构件;对构件进行修改,并加入当前的系统模型;把当前系统开发中新定义的类(或其它构件)提交到类库(或构件库)。


(5)OOA过程

  在复用技术支持下的OOA过程,可以按两种策略进行组织。第一种策略是,基本保持某种OOA方法所建议的OOA过程原貌,在此基础上对其中的各个活动引入复用技术的支持;另一种策略是重新组织OOA过程。


  第一种策略是在原有的OOA过程基础上增加复用技术的支持,应补充说明的一点是,复用技术支持下的OOA过程应增加一个提交新构件的活动。即在一个具体应用系统的开发中,如果定义了一些有希望被其它系统复用的构件,则应该把它提交到可复用构件库中。第二种策略的前提是:在对一个系统进行面向对象的分析之前,己经用面向对象方法对该系统所属的领域进行过领域分析,得到了一个用面向对象方法表示的领域构架和一批类构件,并且具有构件/构架库、类库及相应工具的支持。在这种条件下,重新考虑OOA过程中各个活动的内容及活动之间的关系,力求以组装的方式产生OOA模型,将使OOA过程更为合理,并达到更高的开发效率。

现在软件设计里到处都是模式,框架。有次朋友问什么是模式?我也在学习中,就我的学习经验,给出以下小结。(注意:个人观点,仅供参考,欢迎指正。)

1.什么是模式?
模式,即pattern。其实就是解决某一类问题的方法论。你把解决某类问题的方法总结归纳到理论高度,那就是模式。

Alexander给出的经典定义是:每个模式都描述了一个在我们的环境中不断出现的问题,然后描述了该问题的解决方案的核心。通过这种方式,你可以无数次地使用那些已有的解决方案,无需在重复相同的工作。

模式有不同的领域,建筑领域有建筑模式,软件设计领域也有设计模式。当一个领域逐渐成熟的时候,自然会出现很多模式。

什么是框架?
框架,即framework。其实就是某种应用的半成品,就是一组组件,供你选用完成你自己的系统。简单说就是使用别人搭好的舞台,你来做表演。而且,框架一般是成熟的,不断升级的软件。

2.为什么要用模式?
因为模式是一种指导,在一个良好的指导下,有助于你完成任务,有助于你作出一个优良的设计方案,达到事半功倍的效果。而且会得到解决问题的最佳办法。

为什么要用框架?
因为软件系统发展到今天已经很复杂了,特别是服务器端软件,设计到的知识,内容,问题太多。在某些方面使用别人成熟的框架,就相当于让别人帮你完成一些基础工作,你只需要集中精力完成系统的业务逻辑设计。而且框架一般是成熟,稳健的,他可以处理系统很多细节问题,比如,事物处理,安全性,数据流控制等问题。还有框架一般都经过很多人使用,所以结构很好,所以扩展性也很好,而且它是不断升级的,你可以直接享受别人升级代码带来的好处。

框架一般处在低层应用平台(如J2EE)和高层业务逻辑之间的中间层。

软件为什么要分层?
为了实现“高内聚、低耦合”。把问题划分开来各个解决,易于控制,易于延展,易于分配资源…总之好处很多啦:)。

3.以下所述主要是JAVA,J2EE方面的模式和框架:
常见的设计模式有什么?
首先,你要了解的是GOF的《设计模式–可复用面向对象软件的基础》一书(这个可以说是程序员必备的了),注意:GOF不是一个人,而是指四个人。它的原意是Gangs Of Four,就是“四人帮”,就是指此书的四个作者:Erich Gamma,Richard Helm,Ralph Johnson,John Vlissides。这本书讲了23种主要的模式,包括:抽象工厂、适配器、外观模式等。
还有其他的很多模式,估计有100多种。

软件设计模式太多,就我的理解简单说一下最常见的MVC模式。
MVC模式是1996年由Buschmann提出的:
模型(Model):就是封装数据和所有基于对这些数据的操作。
视图(View):就是封装的是对数据显示,即用户界面。
控制器(Control):就是封装外界作用于模型的操作和对数据流向的控制等。

另外:
RUP(Rational Unified Process)软件统一过程,XP(Extreme Programming)极端编程,这些通常被叫做“过程方法”,是一种软件项目实施过程的方法论,它是针对软件项目的实施过程提出的方法策略。也是另一个角度的模式。

4.常见的JAVA框架有什么?
WAF:
全称:WEB APPLICATION FRAMEWORK
主要应用方面:EJB层,(WEB层也有,但是比较弱)。
主要应用技术:EJB等
出处:http://java.sun.com/blueprints/code/index.html
简述:这是SUN在展示J2EE平台时所用的例子PetStore(宠物商店系统)里面的框架。是SUN蓝皮书例子程序中提出的应用框架。它实现了 MVC和其他良好的设计模式。SUN的网站上有技术资料,最好下载PetStore来研究,WEBLOGIC里自带此系统,源码在bea\weblogic700\samples\server\src\petstore。这是学习了解J2EE的首选框架。
免费。

Struts:
主要应用方面:WEB层。
主要应用技术:JSP,TagLib,JavaBean,XML等
出处:http://jakarta.apache.org/struts/index.html
简述:这是APACHE的开源项目,目前应用很广泛。基于MVC模式,结构很好,基于JSP。Jbuilder8里已经集成了STRUTS1.02的制作。
免费。

简述WAF+STRUTS结合的例子:WEB层用STRUTS,EJB层用WAF:
JSP(TagLib)??>ActionForm??>Action ??> Event??>EJBAction??>EJB ??>DAO??>Database JSP(TagLib) (forward) <??Action <??EventResponse<??

Turbine:
主要应用方面:WEB层。
主要应用技术:servlet等
出处:http://jakarta.apache.org/turbine/index.html
简述:这是APACHE的开源项目。基于SERVLET。据说速度比较快,基于service(pluggable implementation可插拔的执行组件)的方式提供各种服务。
免费。

COCOON:
主要应用方面:WEB层。
主要应用技术:XML,XSP,servlet等
出处:http://cocoon.apache.org/2.0/
简述:这是APACHE的一个开源项目。基于XML,基于XSP(通俗地说,XSP是在XML静态文档中加入Java程序段后形成的动态XML文档。)。特点是可以与多种数据源交互,包括文件系统,数据库,LDAP,XML资源库,网络数据源等。
免费。

ECHO:
主要应用方面:WEB层。
主要应用技术:servlet等
出处:http://www.nextapp.com/products/echo/
简述:nextapp公司的一个开源项目。基于SERVLET。页面可以做的很漂亮,结合echopoint,可以作出很多图形效果(里面用了jfreechart包)。使用SWING的思想来作网页,把HTML当作JAVA的类来做。但是大量使用Session,页面分帧(Frame)很多,系统资源消耗很大。
免费。

JATO:
全称:SUN ONE Application Framework
主要应用方面:WEB层。
主要应用技术:JSP,TagLib,JavaBean等
出处:http://www.sun.com/
简述:这是SUN推出的一个商业性框架,一看名字就知道是结合SUN ONE的平台推出的。我下载了JATO2.0看了一下,感觉有些简单,使用了JSP+TagLib+JavaBean。如他的DOC所说JATO是适合用在小的WEB应用里。
免费。

TCF:
全称:Thin-Client Framework
主要应用方面:JAVA GUI。
主要应用技术:JAVA application等
出处:http://www.alphaworks.ibm.com/tech/tcf
简述:这是IBM出的一个框架。基于MVC模式,基于JAVA Application。推荐一篇介绍文章:http://www-900.ibm.com/developerWorks/cn/java/j-tcf1/index.shtml
收费:每个企业对象license:2000美元。

5.其实本文的目的在于“抛砖引玉”,希望各路高手请你们把各种框架的特点和出处罗列一下 ,供大家参考,选用。

罗文平
(广东省电信科学技术研究院多媒体研究事业部)
摘 要 软件复用和配置管理具有共同的目标,都是为了提高软件的质量和生产力,软件复用主要关注在技术方法的实施,配置管理主要关注在管理上的实施.在许多方面,这两种技术相互渗透.本文在介绍软件复用和配置管理的基础上,探讨了这两种技术的关系及其结合的方式.软件复用是目前普遍看好的一种技术,在基于软件复用的软件开发过程中提供配置管理的支持有助于更好的复用.
关键词 配置管理 软件复用 软件工程环境 软件构件
软件开发过程中的配置管理
配置管理并不是一个新概念,早期的软件工程环境就已经开始考虑配置管理了.现在人们越来认识到配置管理对于提高软件质量和软件开发过程的可靠性有着重要意义.
配置管理的概念
配置管理是软件配置管理的简称,它是指一套管理软件开发和软件维护以及其中各种中间软件产品的方法和规则,配置管理通过在特定的时刻选择软件配置(一组中间软件产品及描述),系统地控制对配置的修改,并在整个软件生存周期中维护配置的完整性和可追踪性.中间软件产品和用于创建中间软件产品的控制信息都应处于配置管理控制下.
配置管理(Configuration Management)是通过技术或行政手段对软件产品及其开发过程和生命周期进行控制,规范的一系列措施.配置管理的目标是记录软件产品的演化过程,确保软件开发者在软件生命周期中各个阶段都能得到精确的产品配置.
配置管理过程是对处于不断演化,完善过程中的软件产品的管理过程.其最终目标是实现软件产品的完整性,一致性,可控性,使产品极大程度地与用户需求相吻合.它通过控制,记录,追踪对软件的修改和每个修改生成的软件组成部件来实现对软件产品的管理功能.
配置管理对软件开发的重要意义
随着软件系统的日益复杂化和用户需求,软件更新的频繁化,配置管理逐渐成为软件生命周期中的重要控制过程,在软件开发过程中扮演着越来越来重要的角色.一个好的配置管理过程能覆盖软件开发和维护的各个方面,同时对软件开过程的宏观管理,即项目管理,也有重要的支持作用.良好的配置管理能使软件开发过程有更好的可预测性,使软件系统具有可重复性,使用户和主管部门用软件质量和开发小组有更强的信心.
由于配置管理对软件开发有着重要意义,配置管理在软件开发环境中的地位也日益提高,软件开发环境主要有两个重点:集成和管理.配置管理是软件开发环境中管理部分的核心,有些管理功能(比如过程管理)最初并不属于配置管理,但随着配置管理的不断发展,也逐渐成为了配置管理的一部分.集成原本是与管理相对独立的另一部分,但人们现在也逐渐认识到,配置管理对于集成也有一定帮助.大多数实现的软件开发环境中也都包含配置管理的功能.
配置管理的主要活动
从配置管理的定义中可以看出,配置管理中的活动主要包括:识别配置,变化控制,状态记录和报告以及审计.识别配置是指找出需要管理的中间产品,使其处于配置管理的控制之下,并维护它们之间的相关关系,一般说来,这些中间产品主要是程序和文档;变化控制是指记录变化的有关信息(包括变化的内容,原因和实现者等),用以保障软件产品的质量;状态记录和报告整个软件的变化过程的目的;审计是指利用配置记录验证软件达到了预期的要求.
配置管理工具的主要功能
配置管理工具基本上都是围绕上述四种主要活动的,有关这四方面的功能也是配置管理的基本功能,许多配置管理工具还提供一些基于基本功能的软件开发支持,过程管理也逐渐成为配置管理的一项功能.配置管理工具的主要功能如下:
配置支持 配置是一组有共同目的的中间软件产品,其中每一个中间软件产品称为一个配置项.配置管理支持用户建立配置项之间的各种关系,并对这些关系加以维护.维护这些关系有助于完成某些特定任务(比如创建)和标识某一变化对整个系统开发的影响.
版本控制 版本控制是配置管理的基本要求,它可以保证在任何时刻恢复任何一个配置项的任何一个版本.版本控制还记录了每个配置项的发展历史,这样就保证了版本之间的可追踪性,也为查找错误提供了帮助.版本控制也是支持并行开发的基础.
变化控制 变化控制是指在整个软件生存周期中的控制对软件的变化.变化控制系统记录每次变化的相关信息(变化的原因,变化的实施者以及变化的内容等).查看这些记录信息,有助于追踪出现的各种问题.记录正在执行的变化的信息,有助于做出正确的管理决策.
创建支持 软件系统往往由许多配置项构成,建立整个系统是个复杂和费时的过程,配置管理工具可以记录和追踪每个配置项信息,帮助用户自动和快速建立系统.和版本控制结合在一起,可以有效地支持同时开发系统的多个版本.
过程支持 过程详细描述了各种人员在整个软件生存周期中如何使用整个系统,过程控制本来是软件开发环境中一个独立的部分,现在配置管理也开始提供这部分功能.目前的配置管理工具对过程的支持还很不够,而且支持方式差别还很大.许多管理只是提供一个预先定义好的生存周期模型,并保证开发的每一步都按照这个模型规定来进行.
团队支持 团队支持是指对多个开发人员同时开发一个软件系统的支持.由于大多数软件系统都需要多个开发人员参与,有效的团队支持对开发人员来说是很有用的.团队支持主要包括:工作区管理,并行开发管理和远程开发管理,某些配置管理工具还包括对开发人员交互的支持,团队支持的基础是版本控制和版本合并.工作区管理是指为每个开发人员提供独立的工作区,开发人员可以互不干扰地进行工作,也可以选择某个时机向其它开发人员提供自己的最新修改结果或接受其它开发人员的修改结果;并行开发管理是指多个开发人员同时进行的修改可以合并,它可以尽可能地自动解决合并中可能出现的冲突;远程开发管理实际上是并行开发管理的特例,是指在广域网上并行开发的管理,许多适合于局域网的方法可能不适合广域网.
报告/查询 配置管理可以向用户提供配置库的各种信息,主要包括:依赖关系报告,变化影响报告,创建报告,配置项状态报告,版本差异报告,历史报告,访问控制报告,冲突检测报告.实际上,在许多配置管理工具中此项功能是分散在各种相应的功能中的.
审计控制 配置管理通过审计控制来验证配置管理过程,以保证配置库中所有配置项的完整性.简单的审计控制是记录配置管理工具执行的所有命令,复杂的审计控制还包括记录每个配置项的状态变化.
其它功能 除了以上的主要功能外,配置管理还可以提供权限控制,人员管理和配置管理等功能,这些功能主要是为配置管理实现以上功能提供保障.
基于复用的软件开发
随着软件规模的不断扩大,人们对软件生产效率和质量的要求越来越高,长期以来,研究人员一直致力于提高软件生产率和软件质量.目前,人们逐渐认识到,软件复用是解决上述问题的现实可行的途径.
基于复用的软件开发模型
基于复用的软件开发主要分为两个方面:开发可复用构件和利用可复用构件进行开发.开发可复用构件是指将过去的软件产品改造成或直接开发出更复用的构件,这些构件应具有较高的质量和较好的可复用价值;利用可复用构件进行开发是指在已有的可复用构件的基础上构件应用系统,此时应保证复用已有构件比重新开发更有好处.
开发可复用构件
大多数软件人员进行软件开发时并没有考虑到复用,他们生产出的产品可复用性很差.例如,大量的程序只能运行在特定的环境中,程序模块存在着多种多样的调用关系,没有独立性和封装性,没有标准的接口,使得以后的开发很难将其复用.因此,基于复用的软件开发的一个重要方面,就是将复用性较差的软件产品改造成具有较高可复用性的构件.
领域工程在构件开发中有着重要的地位.领域工程的主要思想是对应用于某一领域的已经存在的多个系统进行分析,通过相似系统之间的比较,寻找到该领域中存在的实体,关系和操作,并将它们进行抽象,最终改造为可复用的构件.构件开发出来后必须进行测试,以保证其可复用性.
为了方便复用者找到需要的构件,对构件要进行分类,并将其存入构件库.构件的分类和构件的提取是密切相关的.
利用可复用构件进行开发
基于复用的软件开发者与传统的软件开发者的工作有很大的不同,他们不必一切从零做起,如不必编写所有的程序代码.主要任务是使用已存在构件库中的构件去构造满足用户需求的构件系统.用构件进行开发包括下述环节:
构件提取 可复用构件可能数量庞大,它们应用于不同领域(如数据库管理和工程计算等),完成不同功能.为了方便复用者查找到,必须对构件进行有效的组织和管理,不同的构件系统提供了不同的分类和查询策略.好的分类和查询策略有助于提高查询的质量.基于关键词的分类和查询策略是一种最常见的分类和查询策略;REBOOT系统采用了一种基于刻面的分类方法,根据构件的抽象层次,功能,功能对象,环境依赖(不同平台)对构件进行描述和分类,在查询和提取构件时,复用者把对构件的要求转化为对刻面的描述,从构件库中查询和提取构件;JBⅢ中的构件系统采用了以刻面分类为主的多种分类和查询策略,这样可以结合各种策略的优点,提高查询的质量.另外,由于用户需求的不确定性,可能造成查询质量降低,因此构件库系统还可以提供模糊查询机制.
构件的适应性修改 一般说来,提取出来的构件很难完全达到复用者的要求,复用者需要对这两者之间的差距进行评估,并对构件做一定的适应性修改,使之达到要求.构件的适应性修改包括三个方面:选择,修改和集成,其中修改是最重要的一个方面.复用的基本要求是复用构件的代价要小于重新开发的代价,如何减少构件修改的代价对复用是非常重要的.
构件组装 找到所需的构件后,就要对构件进行组装,构件的组装并不一定要求将构件直接组装成应用系统,可以先将小粒度的构件组装成大粒度的构件,最后再组装成整个系统.
2.2 软件开发环境对软件复用的支持
整个基于复用的软件开发过程都离不开软件开发的环境的支持.软件开发环境将各种复用工具(如构件提取,构件组装,构件库的管理等)和开发工具集成一个系统,使各种工具能互相调用,协调工作;软件开发环境中所提供的通讯与并发机制加强了复用者之间的协调;软件开发环境为复用者提供了简单,一致和方便的界面.
配置管理是软件工程环境的热点,在其中占有重要位置.在基于复用的软件开发过程中,配置管理也起了重要作用.
基于复用的软件开发对配置管理的要求
随着新技术(ActiveX,OLE,JAVA,IDL,COBRA等)的出现,软件复用对配置管理提出了新的需求,配置管理应记录项目中正在使用的构件与相应的在构件库中的构件的追踪关系;针对可复用构件,配置管理应记录更多的信息.实际上,配置管理和软件复用有着更密切的关系.复用使得软件开发分为两个阶段;可复用构件的开发和利用可复用构件进行开发,配置管理在这两个阶段中都能起到重要作用.
开发可复用构件时的配置管理
复用构件的开发也是一种开发,配置管理对软件开发的作用也可以在其中体现出来.提高可复用性是开发可复用构件的一个重要目标,目前主要从技术的角度研究如何提高可复用性,实际上,好的管理也对提高可复用性有着重要意义.构件的质量是构件可复用性的一个重要方面,构件使用者对构件质量的要求比对普通软件质量的要求要高.配置管理可以有效地提高构件的质量,从而提高其可复用性.另外,开发可复用构件时也可能会复用已有的构件.
利用可复用构件进行软件开发时的配置管理
利用构件进行软件开发可以分为三个方面:构件提取,构件组装和构件的适应性修改.]在构件提取方面,配置管理可以提高构件提取的质量;在构件组装方面,配置管理有助于在提取的同时进行组装;在构件的适应性修改方面,配置管理可以控制构件的变化,保证构件的适应性修改正确完成.
构件提取时的配置管理
好的构件库组织和管理可以提高构件提取的质量,配置管理对构件库的组织和管理有很大的帮助.一个构件在构件库中往往需要保存多个版本,不同版本之间的差异可能是使用平台不同,也可能是功能或使用方式的差异.许多构件库系统都记录构件间的版本关系.
利用配置管理中的版本控制,可以更好地维护构件间的版本关系.配置管理采用增量存储,可以减少这些构件在构件库中所占的空间,从而提高构件库的容量.多个构件可能会包含相同的成分,比如有两个构件,每个构件都包含多个类,其中有些类是这两个构件共有的.利用配置管理,这些类就可以只保存一份,这样可以减少冗余,保证一致性.
由于构件库使用者对构件库的查询机制不够熟悉或者难以准确表达查询要求,有时所需构件在构件库中存在,但不能查出来,或者查出来许多显然不需要的构件,这样就会影响开发的效率.构件库中的构件大多数是领域相关的,因此在开发某一项目时,构件库中许多构件是不可能在该项目中复用的,有可能在该项目中复用的构件主要是通用构件和与该项目所处领域相关的构件.如果将这些构件的查询信息以配置的形式记录下来,使得开发人员对构件的查询可以仅在这些构件中进行,构件提取的质量会有较大提高.在构件库中寻找有可能在某一特定项目中复用的构件,可以由对构件库和该项目都比较熟悉的开发人员来完成,提取的质量也会比较高.
构件组装时的配置管理
在进行构件组装时,组装的结果可能仍是可复用的构件,这些组装出来的构件也应该保存在构件库中,供以后使用.由于这些构件的组成部分都已存在于构件库中,再在构件库中存储这些构件不是个好办法.因此,构件库应该在不直接存储组合构件的情况下,提供查询构件的功能.此时,由于不能穷举出构件库中所有的构件组合,必须采取一些实际可行的办法使得要查询的构件组合的数目降下来[3].从管理的角度看,可以把可能的构件组合以配置的形式记录下来,供使用者查询,甚至可以在构件库的使用过程中逐渐将新的构件组合添加到构件库中.
构件适应性修改时的配置管理
构件的使用者往往不是构件的开发者,对构件不够熟悉,构件的使用者在对构件进行修改时,可能为很小的修改要付出巨大的工作量;而且由于修改不当,还可能引入错误,造成构件复用的失败.配置管理有助于减少构件修改的工作量和保证构件修改正确完成.在对构件进行修改的过程中,构件使用者往往不能一次找到正确的修改办法,因此需要尝试多种修改办法.利用配置管理中的版本控制,记录构件修改过程中的多个版本,可以避免多次尝试之间的相互影响,也可以尽可能地保存以前有用的工作,减少重复劳动,从而减少构件修改的工作量.
一个构件可能包含多个部分,对一个部分的修改可能会对其它部分造成影响.利用配置管理中的配置支持和变化控制,还可以在构件修改过程中维护各个部分的一致性.配置管理可以记录每次修改对整个构件的影响,有助于使修改局部化,减少修改造成的对整个构件的影响,指导复用者正确完成构件的适应性修改.
软件复用的实际效益
日美的一些大公司的资料表明,软件复用率最高可望达到90%,而且软件复用使得企业在及时满足市场,软件质量,软件开发费用和维护率等方面得到显著的改进.例如,AT&T的电信操作支持系统软件复用率达40%~92%;Motorola公司在为编译器和编译器工具编写测试包时,复用率达85%;Ericssion AXE公司的电信开关系统产品,复用率达90%.Hewlett-Pachard公司早在1984年就开始开发可复用构件,1987年建立复用库,据80年代几个方面的统计,复用率达25%~50%.惠普公司在1990年开始实施一个"宏伟"的复用计划,收集并研究最好的体系结构,过程,组织结构,打算将其装备公司的各个部门,但通过实践,惠普公司采用典型示范先行的系统的过渡方法,成功地在公司内逐步地全面实施复用.
根据1991年的一份报告说,日本的软件大公司在80年代中期复用率就达50%左右;1997年的一份报告说,Hitachi的Eagle环境,达60%~98%,该环境让软件工程师可以复用标准的程序框架和函数过程.
除了复用率之外,在企业的经营管理方面也可望达到理想的效益.例如,上市时间可缩短2~5年;软件产品的缺陷密度可减少5~10倍;软件产品的维护可减少5~10倍;软件开发总费用可减少15%~75%,其中75%是针对长期项目,包括开发可复用资产以及支持复用的负担.
参考文献
Li Keqin, et al ,An Overview of JB(Jade Bird) Component Library System JBCL,Technology of Object-Oriented Languages and Systems Tools 24,1997
Burrows C,et al ,Configuration Management, Ovum Ltd,1998
Rational R1000 Development System Reference Manuals 1998
郑人杰编 软件工程 1999
CMM技术资料
作者简介
罗文平,1968年10月生,男,籍贯:江西,工程师,工学硕士.1994年3月研究生毕业于西安电子科技大学电子工程系.1994年4月在广东省电信科学技术研究院培训中心工作,1996年多媒体部成立后至今一直在多媒体部工作.主要研究方向:软件工程,群件工作流Exchange,CMM,软件和系统备份.
多媒体技术论坛2000(MTF2000)