??xml version="1.0" encoding="utf-8" standalone="yes"?> if(!user.role.equals("manager"))
W?课:切勿l过服务器端验证
作ؓ一位Y仉问,我曾有机会不但设计ƈ实现了Web应用E序Q而且q评?审核了许多Web应用E序。在复杂的、ƈ且用JavaScript客户?
装的应用程序内Q我l常遇到对用戯入信息执行大量检查的Web面。即使HTML元素h数据有效性的属性也如此Q例如MAXLENGTH。只有在?
功验证所有输入信息后Q才能提交HTML表单。结果,一旦服务器端收到通知表单Q请求)Q便恰当地执行业务逻辑?br />
在此Q您发现问题了么Q开发h员已l做了许多重要的假设。例如,他们假设所有的Web应用E序用户都同栯实。开发h员还假设所有用户将L使用他们
试q的览器访问Web应用E序。还有很多其他的假设。这些开发h员忘C利用可以免费得到的工P通过命o行很Ҏ地模拟类似浏览器的行为。事实上Q?
通过在浏览器H口中键入适当的URLQ您可以发送Q?#8220;posted”表单Q尽如此,通过用q些面的GEThQ您很容易地Lq样?#8220;表单?
?#8221;。但是,您不能阻止h们模拟甚臛_Z们自q览器来入R您的pȝ?br />
Ҏ的问题在于开发h员不能确定客L验证与服务器端验证的主要差别。两者的主要差别不在于验证究竟发生在哪里Q例如在客户端或者在服务器端。主要的差别在于验证背后的目的不同?br />
客户端验证仅仅是方便。执行它可ؓ用户提供快速反馈——应用E序g做出响应Q给ZU运行桌面应用程序的错觉?br />
另一斚wQ服务器端验证是构徏安全Web应用E序必需的。不在客户端一侧输入的是什么,它可以确保客L送往服务器的所有数据都是有效的?br />
因而,只有服务器端验证才可以提供真正应用程序的安全。许多开发h员陷入了错误感觉的圈套:只有在客Lq行所有数据的验证才能保安全。下面是说明此观点的一个常见的CZQ?br />
一个典型的d面拥有一个用来输入用户名的文本框和一个输入密码的文本框。在服务器端Q某人在接收servlet中可能遇C些代码,q些代码构成了下面Ş式的SQL查询Q?br />
"SELECT * FROM SecurityTable WHERE username = '" + form.getParameter("username")
+ "' AND password = '" + form.getParameter("password") + "';"Qƈ执行q些代码。如果查询在l果集的某一行返回,则用L录成功,否则用户dp|?br />
W一个问题是构造SQL的方式,但现在让我们暂时忽略它。如果用户在用户名中输入“Alice'--”会怎样呢?假设名ؓ“Alice”的用户已l在
SecurityTable中,q时此用P更恰当的说法是黑客)成功地登录。我把扑ևZ么会出现q种情况的原因做为留l您的一道习题?br />
许多创造性的客户端验证可以阻止一般的用户从浏览器中这L录。但对于已经用了JavaScript的客LQ或者那些能够用其他类似浏览器E序直接发送命令(HTTP
POST和GET命oQ的高用户Q或者说黑客Q来_我们又有什么办法呢Q服务器端验证是防止q种漏洞cd所必须的。这ӞSSL、防火墙{都z不上用Z?br />
W?课:安全q是附加物
如第1课所qͼ我曾有幸研究q许多Web应用E序。我发现所有的JavaServer PageQJSPQ都有一个共同的主题Q那是hcM下面伪代码的布局Q?br />
<%
User user =
session.getAttribute("User");
if(user == null)
{
// redirect to
// the logon page…
}
{
// redirect to the
// "unauthorized" page…
}
%>
HTML, JavaScript, and JSP
code to display data and
allow user interaction -->
如果目使用诸如Strutsq样的MVC框架Q所有的Action Bean都会hcM的代码。尽最后这些代码可能运行得很好Q但如果您发C个bugQ或者您必须d一个新的角Ԍ例如Q?#8220;guest”或?#8220;admin”Q,q就会代表一场维护恶梦?br />
此外Q所有的开发h员,不管您多q轻Q都需要熟悉这U编码模式。当Ӟ您可以用一些JSP标签来整理JSP代码Q可以创Z个清除派生Action Bean的基本Action
Bean。尽如此,׃与安全相关的代码会分布到多个地方Q所以维护时的恶梦仍旧存在。由于Web应用E序的安全是建立在应用程序代码的U别上(由多个开发h员)Q而不是徏立在架构U别上,所以Web应用E序q是很可能存在弱炏V?br />
很可能,Ҏ的问题是在项目接q完成时才处理安全性问题。最q作Z名架构师Q我曑֜一q多的时间里亲历了某一要实现项目的6个版本,而直到第四版?
我们才提C安全性——即使该目会将高度敏感的个人数据暴露于Web上,我们也没有注意到安全性。ؓ了更改发布计划,我们卷入了与目资助人及其管理h
员的争斗中,以便在第一版中包含所有与安全相关的功能,q将一?#8220;业务”功能攑֜后箋的版本中。最l,我们赢得了胜利。而且׃应用E序的安全性相当高Q?
能够保护客户的私有数据,q一Ҏ们引以ؓ荣,我们的客户也非常高兴?br />
遗憾的是Q在大多数应用程序中Q安全性看hq未增加M实际的商业h|所以直到最后才解决。发生这U情冉|Qh们才匆忙开发与安全相关的代码,?
丝毫没有考虑解决Ҏ的长期可l护性或者健壮性。忽视该安全性的另一个征兆是~Z全面的服务器端验证,如我在第1课中所qͼq一Ҏ安全Web应用E序?
一个重要组成部分?br />
CQJ2EE Web应用E序的安全性ƈ非仅仅是在Web.xml 和ejb-jar.xml文g中用合适的声明Q也不是使用J2EE技术,如Java
认证和授权服务(Java Authentication and Authorization ServiceQJAASQ。而是l过深思熟虑后的设计,且实C个支持它的架构?br />
W?课:国际化(I18NQ不再是U怸谈兵
当今世界的事实是许多p非母语的Z访问您的公共Web应用E序。随着电子政务的实行,׃它允思h们(某个国家的居民)在线与政府机构交互,所
以这一点特别真实。这L例子包括换发驄或者R辆登记证。许多第一语言不是p的h们很可能访问这L应用E序。国际化Q即Q?#8220;i18n”Q因为在
“internationalization”q个单词中,字母i和字母n之间一共有18个字母)使得您的应用E序能够支持多种语言?br />
昄Q如果您的JSP
面中有编码的文本Q或者您的Java代码q回编码的错误消息Q那么您要花费很多时间开发此Web应用E序的西班牙语版本。然而,在Web应用E序
中,Z支持多种语言Q文本不是惟一必须“具体?#8221;的部分。因多图像中嵌有文字Q所以图形和囑փ也应该是可配|的。在极端的情况下Q图像(或者颜Ԍ
在不同的文化背景中可能有完全不同的意思。类似地QQ何格式化数字和日期的Java代码也必L地化。但问题是:您的面布局可能也需要更攏V?br />
例如Q如果您使用HTML表格来格式化和显C单选项、应用程序题头或注脚Q则您可能必Mؓ每一U支持的语言更改每一栏的最宽度和表格其他可能的方面。ؓ了适应不同的字体和颜色Q您可能必须为每一U语a使用单独的样式表?br />
昄Q现在创Z个国际化的Web应用E序面的是架构挑战而不是应用程序方面的挑战。一个架构良好的Web应用E序意味着您的JSP面和所有与?
务相关的Q应用程序特有的QJava代码都不知不觉地选择了本地化。要C的教训是Q不要因为Java、J2EE支持国际化而不考虑国际化。您必须从第一
天vp住设计具有国际化的解x案?br />
W?课:在MVC表示中避免共同的错误
J2EE开发已l够成熟,在表C层Q大多数目使用MVC架构的某些Ş式,例如Struts。在q样的项目中Q我常见到的现象是对MVC模式的误用。下面是几个CZ?br />
常见的误用是在模型层Q例如,在Struts的Action Bean中)实现了所有的业务逻辑。不要忘了,表示层的模型层仍然是表示层的一部分。用该模型层的正确Ҏ是调用适当的业务层服务Q或对象Qƈ结果发送到视图层(view
layerQ。用设计模式术语来说QMVC表示层的模型应该作ؓ业务层的外观QFa?adeQ来实现。更好的Ҏ是,使用核心J2EE模式QCore J2EE
PatternsQ中到的Business Delegate模式。这D自书中摘录的内容精彩地概述了将您的模型作ؓBusiness Delegate来实现的要点和优点:
Business Delegate起到客户端业务抽象化的作用。它抽象化,q而隐藏业务服务的实现。用Business DelegateQ可以降低表C层客户端和pȝ的业务服?之间的耦合E度。根据实现策略不同,Business
Delegate可以在业务服务API的实CQ保护客L不受可能的变动性媄响。这P在业务服务API或其底层实现变化Ӟ可以潜在地减必M改表C层客户端代码的ơ数?br />
另一个常见的错误是在模型层中攄许多表示cd的逻辑。例如,如果JSP面需要以指定方式格式化的日期或者以指定方式排序的数据,某些人可能将该?
辑放|在模型层,对该逻辑来说Q这是错误的地方。实际上Q它应该在JSP面使用的一lhelpercM。当业务层返回数据时QAction
Bean应该数据{发给视图层。这P无需创徏模型和视图之间多余的耦合Q就能够灉|支持多个视图层(JSP、Velocity、XML{)。也使视?
能够定向用hC数据的最x式?br />
最后,我见q的大多数MVC应用E序都有未充分应用的控制器。例如,l大多数的Struts应用E序创Z个基本的Actionc,q完成所有与?
全相关的功能。其他所有的Action
Bean都是此基cȝzcR这U功能应该是控制器的一部分Q因为如果没有满_全条Ӟ则首先调用不应该到达Action
BeanQ即Q模型)。记住,一个设计良好的MVC架构的最强大功能之一是存在一个健壮的、可扩展的控制器。您应该利用该能力以加强自己的优ѝ?br />
W?课:不要被JOPO束缚住手?/strong>
我曾目睹许多目Z使用Enterprise JavaBean而用Enterprise JavaBean。因为EJBgl项目带来优感和妄自尊大的表现Q所以有时它是显L要素Qcoolness
factorQ。而其他时候,它会使J2EE和EJB引vh。记住,J2EE和EJB不是同意词。EJB只是J2EE 的一部分QJ2EE 是包含JSP、servlet、Java
消息服务QJMSQ、Java数据库连接(JDBCQ、JAAS?Java理扩展QJMXQ和EJB在内的一pd技术,同样也是有关如何共同使用q些技术徏立解x案的一l指导原则和模式?br />
如果在不需要用EJB的情况下使用EJBQ它们可能会影响E序的性能。与老的Web服务器相比,EJB一般对应用服务器有更多的需求。EJB提供?
所有增值服务一般需要消耗更大的内存和更多的CPU旉。许多应用程序不需要这些服务,因此应用服务器要与应用程序争源?br />
在某些情况下Q不必要C用EJB可能使应用程序崩溃。例如,最q我遇到了一个在开源应用服务器上开发的应用E序。业务逻辑装在一pd有状态会?
beanQEJBQ中。开发h员ؓ了在应用服务器中完全用q些bean?#8220;钝化”费了很大的劲。客L要求应用E序部v在某一商用应用服务器上Q而该?
务器是客L技术栈的一部分。该应用服务器却不允许关?#8220;钝化”功能。事实上Q客L不想改变与其合作的应用服务器的设M|。结果,开发商到了很大的
ȝ。(gQ有的事情是开发商自己都不能给Zؓ什么将代码用EJBQ而且q是有状态会话beanQ实现的好理由。不仅仅是开发商会遇到性能问题Q他?
的程序在客户那里也无法工作?br />
在Web应用E序中,无格式普通Java 对象QPOJOQ是EJB强有力的竞争者。POJO是轻量的,不像EJB那样负担额外的负担。在我看来,对许多EJB的优点,例如对象入池Q估计过高。POJO是您的朋友,不要被它束缚住手脚?br />
W?课:数据讉Kq不能托O/R映射
我曾参与q的所有Web应用E序都向用户提供从其他地方存取的数据Qƈ且因此需要一个数据访问层。这q不是说所有的目都需要标识ƈ建立q样一个层Q?
q仅仅说明这样层的存在不是隐含的是明确的。如果是隐含的数据层Q数据层是业务对象(卻I业务服务Q层的一部分。这适用于小型应用程序,但通常与大一?
目所接受的架构指导原则相抵触?br />
MQ数据访问层必须满或超Z下四个标准:
h透明?
业务对象在不知道数据源实现的具体l节情况下,可以使用数据源。由于实现细节隐藏在数据讉K层的内部Q所以访问是透明的?br />
易于q移
数据讉K层应用E序很容易迁Ud其他数据库实现。业务对象不了解底层的数据实玎ͼ所以迁UM仅涉及到修改数据讉K层。进一步地_如果您正在部|某
U工厂策略,您可以ؓ每个底层的存储实现提供具体的工厂实现。如果是那样的话Q迁Ud不同的存储实现意味着为应用程序提供一个新的工厂实现?br />
量减少业务对象中代码复杂?/strong>
因ؓ数据讉K层管理着所有的数据讉K复杂性,所以它可以化业务对象和使用数据讉K层的其他数据客户端的代码。数据访问层Q而不是业务对象,含有许多
与实现相关的代码Q例如SQL语句Q。这L开发h员带来了更高的效率、更好的可维护性、提高了代码的可L等一pd好处?br />
把所有的数据讉K集中在单独的层上
׃所有的数据讉K操作现在都委托给数据讉K层,所以您可以这个单独的数据讉K层看做能够将应用E序的其他部分与数据讉K实现怺隔离的层。这U集中化可以使应用程序易于维护和理?br />
注意Q?/strong>q?
些标准都不能明确地调出对O/RQ对象到关系Q映层的需求。O/R映射层一般用O/R映射工具创徏Q它提供对象对关pL据结构的查看和感知(look-
and-feelQ。在我看来,在项目中使用O/R映射与用EJBcM。在大多数情况下Qƈ不要求它。对于包含中{规模的联合以及多对多关pȝ关系型数
据库来说QO/R映射会变得相当复杂。由于增加O/R 映射解决Ҏ本n的内在复杂性,例如延迟加蝲Qlazy
loadingQ、高速缓冲等Q您ؓ您的目带来更大的复杂性(和风险)?br />
Zq一步支持我的观点,我将指出按照Sun
Microsystem所普及的实体BeanQO/R映射的一U实玎ͼ的许多失败的试Q这是自1.0版以来一直折h的难题。在SUN的防卫措施中Q一
些早期的问题是有关EJB规范的开发商实现的。这依次证明了实体Bean规范自n的复杂性。结果,大多数J2EE架构师一般认Z实体Bean中脱d?
是一个好L?br />
大多数应用程序在处理他们的数据时Q只能进行有限次数的查询。在q样的应用程序中Q访问数据的一U有效方法是实现一个数据访问层Q该层实现执行这些查
询的一pd服务Q或对象、或APIQ。如上所qͼ在这U情况下Q不需要O/R映射。当您要求查询灵zL时QO/R映射正合适,但要CQ这U附加的灉|?
q不是没有代L?br />
像我承诺的那样Q在本文中,我尽量避免陈腐的最佛_c相反,关于J2EE目中每一位架构师必须做出的最重要的决定,我集中讲解了我的观点。最
后,您应该记住:J2EEq某种具体的技术,也不是强行加入到解决Ҏ中的一些首字母~写。相反,您应该在适当的时机,恰当的地方,使用合适的技术,q?
遵@J2EE的指导原则和J2EE中所包含的比技术本w重要得多的实践?
<DataSets>
<Metadata>
<ItemDef OID="I0001" Name="姓名" DataType="string" Length=""
SignificantDigits="" SASFieldName="" SDSVarName="" Origin=""
Comment="" DefaultValue="" Note="输入您的名字"
RangeCheckRelationship="and">
<CodeListRef Name="" />
<Question>您的姓名?</Question>
<Control qualityControl="" operationType="" />
</ItemDef>
<ItemDef OID="I0002" Name="说明信息" DataType="text" Length=""
SignificantDigits="" SASFieldName="" SDSVarName="" Origin=""
Comment="" DefaultValue="7g" Note="" RangeCheckRelationship="and">
<CodeListRef Name="" />
<Question>您的详细说明</Question>
<Control qualityControl="" operationType="" />
</ItemDef>
<ItemGroupDef OID="G0001" Name="个h描述" Repeating="yes"
IsReferenceData="0" SASDatasetName="" Domain="" Origin="" Purpose=""
Comment=""
Note="个h描述信息,包括:[r]&lt;br/&gt;[/r]1, 姓名[r]&lt;br/&gt;[/r]2, 详细描述信息">
<ItemRef ItemOID="I0001" Name="姓名" OrderNumber="1"
Mandatory="no" />
<ItemRef ItemOID="I0002" Name="说明信息" OrderNumber="2"
Mandatory="no" />
</ItemGroupDef>
<FormDef OID="F0001" Name="预诊-1" Repeating="yes"
Note="预诊信息[r]&lt;br/&gt;[/r]误l输?>
<ItemGroupRef ItemGroupOID="G0001" Name="个h描述"
OrderNumber="1" Mandatory="no" />
</FormDef>
<StudyEventDef OID="E0001" Name="新徏诊断-1" Repeating="yes"
Type="Scheduled" Category="">
<FormRef FormOID="F0001" Name="预诊-1" OrderNumber="1"
Mandatory="no" page="1" />
</StudyEventDef>
<VarDef OID="var_0" DataType="string" Lable="您的姓名?"
Remark="E0001.1//F0001.1//G0001#1.I0001" Domain="" Function="">
<ItemRef Varname="v0" ItemOID="I0001" Eventpath="E0001.1"
Formpath="F0001.1" ItemGrouppath="G0001#1" />
</VarDef>
<VarDef OID="var_1" DataType="text" Lable="您的详细说明"
Remark="E0001.1//F0001.1//G0001#1.I0002" Domain="" Function="">
<ItemRef Varname="v0" ItemOID="I0002" Eventpath="E0001.1"
Formpath="F0001.1" ItemGrouppath="G0001#1" />
</VarDef>
</Metadata>
<DataSet Subject="0010">
<Event Name="abc">
<Data Value="1" VarRefOID="var_0" />
<Data Value="2" VarRefOID="var_1" />
</Event>
</DataSet>
<DataSet Subject="0009">
<Event Name="abc">
<Data Value="1" VarRefOID="var_0" />
<Data Value="1" VarRefOID="var_1" />
</Event>
</DataSet>
<DataSet Subject="0008">
<Event Name="abc">
<Data Value="1" VarRefOID="var_0" />
<Data Value="1" VarRefOID="var_1" />
</Event>
</DataSet>
<DataSet Subject="0007">
<Event Name="abc">
<Data Value="1" VarRefOID="var_0" />
<Data Value="1" VarRefOID="var_1" />
</Event>
</DataSet>
<DataSet Subject="0006">
<Event Name="abc">
<Data Value="1" VarRefOID="var_0" />
<Data Value="2" VarRefOID="var_1" />
</Event>
</DataSet>
<DataSet Subject="0005">
<Event Name="abc">
<Data Value="1" VarRefOID="var_0" />
<Data Value="2" VarRefOID="var_1" />
</Event>
</DataSet>
<DataSet Subject="0004">
<Event Name="abc">
<Data Value="1" VarRefOID="var_0" />
<Data Value="2" VarRefOID="var_1" />
</Event>
</DataSet>
<DataSet Subject="0003">
<Event Name="abc">
<Data Value="niahoa" VarRefOID="var_0" />
<Data Value="gee " VarRefOID="var_1" />
</Event>
</DataSet>
<DataSet Subject="0002">
<Event Name="abc">
<Data Value="12" VarRefOID="var_0" />
<Data Value="3" VarRefOID="var_1" />
</Event>
</DataSet>
<DataSet Subject="0001">
<Event Name="abc">
<Data Value="qqq" VarRefOID="var_0" />
<Data Value="gg" VarRefOID="var_1" />
</Event>
</DataSet>
</DataSets>
xslt文g的格?br />
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:variable name="columnitems" select="http://VarDef"/>
<xsl:variable name="rowitems" select="http://DataSet"/>
<xsl:call-template name="listcolumns">
<xsl:with-param name="items" select="$columnitems"/>
</xsl:call-template>
<xsl:call-template name="listrows">
<xsl:with-param name="citems" select="$columnitems"/>
<xsl:with-param name="ritems" select="$rowitems"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="listcolumns">
<xsl:param name="items"/>
<xsl:text>SubjectCode,</xsl:text>
<xsl:for-each select="$items">
<xsl:choose>
<xsl:when test="position() != last() and last() >= 2">
<xsl:value-of select="@OID"/>,<xsl:text/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="@OID"/><xsl:text/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
<xsl:text>
</xsl:text>
<xsl:text>,</xsl:text>
<xsl:for-each select="$items">
<xsl:choose>
<xsl:when test="position() != last() and last() >= 2">
<xsl:value-of select="@Label"/>,<xsl:text/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="@Label"/><xsl:text/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
<xsl:text>
</xsl:text>
</xsl:template>
<xsl:template name="listrows">
<xsl:param name="citems"/>
<xsl:param name="ritems"/>
<xsl:for-each select="$ritems">
<xsl:text>"</xsl:text>
<xsl:value-of select="@Subject"/><xsl:text/>
<xsl:text>",</xsl:text>
<xsl:variable name="item" select="Event"/>
<xsl:call-template name="listrow">
<xsl:with-param name="dataitem" select="$item"/>
<xsl:with-param name="matchcitems" select="$citems"/>
</xsl:call-template>
</xsl:for-each>
</xsl:template>
<xsl:template name="listrow">
<xsl:param name="dataitem"/>
<xsl:param name="matchcitems"/>
<xsl:for-each select="$matchcitems">
<xsl:variable name="have">
<xsl:variable name="cellname" select="@OID"/>
<xsl:call-template name="printcell">
<xsl:with-param name="cn" select="$cellname"/>
<xsl:with-param name="celldatas" select="$dataitem"/>
</xsl:call-template>
</xsl:variable>
<xsl:choose>
<xsl:when test="position() != last() and last() >= 2">
<xsl:value-of select="$have"/>,<xsl:text/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$have"/><xsl:text/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
<xsl:text>
</xsl:text>
</xsl:template>
<xsl:template name="printcell">
<xsl:param name="cn"/>
<xsl:param name="celldatas"/>
<xsl:for-each select="$celldatas/Data">
<xsl:if test="$cn = @VarRefOID">
<xsl:text>"</xsl:text>
<xsl:value-of select="@Value"/><xsl:text/>
<xsl:text>"</xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
RUPQ?/span>Rational Unified ProcessQ统一软g开发过E,l一软gq程)是一个面向对象且Z|络的程序开发方法论。根?/span>Rational(Rational Rose和统一建模语言的开发?/span>)的说法,好像一个在U的指导者,它可以ؓ所有方面和层次的程序开发提供指导方针,模版以及事例支持?/span> RUP和类似的产品--例如面向对象的Y件过E(OOSPQ,以及OPEN Process都是理解性的软g工程工具--把开发中面向q程的方面(例如定义的阶D,技术和实践Q和其他开发的lgQ例如文档,模型Q手册以及代码等{)整合在一个统一的框架内?/span>
一、六大经?/span>
q代式开发。在软g开发的早期阶段想完全、准的捕获用户的需求几乎是? 可能的。实际上Q我们经帔R到的问题是需求在整个软g开发工E中l常会改变。P代式开发允许在每次q代q程中需求可能有变化Q通过不断l化来加深对问题? 理解。P代式开发不仅可以降低项目的风险Q而且每个q代q程以可以执行版本结束,可以鼓舞开发h员?/span>
理需求。确定系l的需求是一个连l的q程Q开发h员在开发系l之前不可能完全详细的说明一个系l的真正需求?/span>RUP描述了如何提取、组l系l的功能和约束条件ƈ其文档化,用例和脚本的使用以被证明是捕获功能性需求的有效Ҏ?/span>
Zlg的体pȝ构。组件重用成ؓ可能Q系l可以由lgl成。基于独立的、可替换的、模块化lg的体pȝ构有助于理复杂性,提高重用率?/span>RUP描述了如何设计一个有Ҏ的、能适应变化的、易于理解的、有助于重用的Y件体pȝ构?/span>
可视化徏模?/span>RUP往往?/span>UML联系在一P对Y件系l徏立可视化模型帮助Z提供理软g复杂性的能力?/span>RUP告诉我们如何可视化的对Y件系l徏模,获取有关体系l构于组件的l构和行Z息?/span>
验证软g质量。在RUP中Y件质量评C再是事后q行或单独小l进行的分离zdQ而是内徏于过E中的所有活动,q样可以及早发现软g中的~陷?/span>
控制软g变更。P代式开发中如果没有严格的控制和协调Q整个Y件开发过E很快就陷入混ؕ之中Q?/span>RUP描述了如何控制、跟t、监控、修改以保成功的P代开 发?/span>RUP通过软g开发过E中的制品,隔离来自其他工作I间的变_以此为每个开发h员徏立安全的工作I间?/span>
二、统一软g开发过E?/span>RUP的二l开发模?/span>
RUP软g开发生命周期是一个二l的软g开发模型?/span> 横u通过旉l织Q是q程展开的生命周期特征,体现开发过E的动态结构,用来描述它的术语主要包括周期(Cycle)、阶D?/span>(Phase)、P?/span> (Iteration)和里E碑(Milestone)Q纵轴以内容来组lؓ自然的逻辑zdQ体现开发过E的静态结构,用来描述它的术语主要包括zd (Activity)、?/span>(Artifact)、工作?/span>(Worker)和工作流(Workflow)。如?/span>1Q?/span>
三、统一软g开发过E?/span>RUP核心概念
RUP中定义了一些核心概念,如下图:
角色Q描q某个h或者一个小l的行ؓ与职责?/span>RUP预先定义了很多角艌Ӏ?/span>
zdQ是一个有明确目的的独立工作单元?/span>
工gQ是zd生成、创建或修改的一D信息?/span>
1.先写个简单的被测试类Q?span lang="EN-US">
package test.junit;
public class
BaseClass {
public String method() {
return this.getClass().getName();
}
}
2.对于q个cȝunit试Q我们先看看?span lang="EN-US">junit3中怎么q行试的吧Q?span lang="EN-US">
package test.junit;
import junit.framework.TestCase;//引入TestCasec?/span>
public class
BaseClassTest extends TestCase {//cdȝ?span lang="EN-US">TestCase
BaseClass baseClass;
protected void
setUp() throws Exception {
super.setUp();
baseClass = new BaseClass();
}
public void
testMethod (){//试Ҏ必须?span lang="EN-US">test开?/span>
//通过assert*来检?/span>
assertTrue(baseClass.method
().equals("test.junit.BaseClass"));
}
}
通过上面q个c,我们?span lang="EN-US">junit3q行一些ȝQ?/span>
1. 必须引入c?span lang="EN-US">TestCaseQ?strong>import junit.framework.TestCase;Q,
2. 必须l承c?span lang="EN-US">TestCase (class BaseClassTest extends TestCase)
3. 试Ҏ必须?span lang="EN-US">test开_public void testMethod ()Q?span lang="EN-US">
4. 通过assert*Ҏ来判断结果(assertTrue(baseClass.method ().equals("test.junit.BaseClass"));Q?span lang="EN-US">
3. 下面来了解一?span lang="EN-US">junit4里面的内部关pdQ下面来看看我们今天的主?span lang="EN-US">junit4是怎么处理q些的吧Q?span lang="EN-US">
package test.junit;
import org.junit.Test;//引入Testc?/span>
import static
org.junit.Assert.*;//引入Assert.*?/span>
public class
BaseClassTestNew {//q里不用再?span lang="EN-US">TestCasecM?/span>
BaseClass baseClass = new BaseClass();
@Test public void methodOne(){//名字可以按你自己的爱好。但是必M@Test开?/span>
//q是?span lang="EN-US">assert*来判?/span>
assertTrue(baseClass.methodOne().equals("test.junit.BaseClass"));
}
}
看看多么的简z,好像了很多U束Q下面通过?span lang="EN-US">junit3的特性进行比较ȝjunit4的特性:
1.必须引入c?span lang="EN-US">TestCaseQ?strong>import junit.framework.TestCase;
è必须引入c?span lang="EN-US">TestQ?strong>import org.junit.Test;Q?span lang="EN-US">,必须引入c(import static org.junit.Assert.*;Q?span lang="EN-US">
2.必须l承c?span lang="EN-US">TestCase (class BaseClassTest extends TestCase)
è不需?span lang="EN-US">
3.试Ҏ必须?span lang="EN-US">test开_public void testMethod ()Q?span lang="EN-US">
è不需要,但是cd始的时候要标记 @Test
4.通过assert*Ҏ来判断结果(assertTrue(baseClass.method ).equals("test.junit.BaseClass"));Q?span lang="EN-US">
从上面的ҎQ可以看?span lang="EN-US">junit4?span lang="EN-US">junit3的区别在于:
L与类TestCase的偶联性,唯一触发TestCase的地方就是在@Test上?/span>
4.以上是junit4?span lang="EN-US">junit3的区别与改进Q下面再l箋看看junit4有哪些新增加的功能吧Q?span lang="EN-US">
1. @Before ?span lang="EN-US"> @After
cM?span lang="EN-US">init() ?span lang="EN-US">destory(),可以一些初始化和释攑ַ作放到这里面Q每个类都只有一?span style="color: #7f0055;" lang="EN-US">@Before ?span lang="EN-US"> @After。ؓ了实现这个功能,我们测试类修改如下Q?/span>
import org.junit.Test;
//新增加两个类
import org.junit.Before;
import org.junit.After;
import static
org.junit.Assert.*;
public class
BaseClassTestNew {
BaseClass baseClass ; //q里不用q行初始?/span>
@Before public void
runBeforeTest(){
baseClass = new BaseClass();
}
@Test public void method (){
assertTrue(baseClass.method
().equals("test.junit.BaseClass"));
}
@After public void
runAfterTest(){
baseClass.teardownList();
baseClass = null;
}
}
@Before Ҏ会在 @Test 之前q行Q相?span lang="EN-US"> @After 会在所有方法运行完q行?/span>
Junit4同时q支?span lang="EN-US"> @BeforeClass ?span lang="EN-US"> @AfterClassQ原理一P大家可以参考相兌料?span lang="EN-US">
2.TimeOut 属?span lang="EN-US">
Junit4支持timeoutQ运用如下:
a. 首先在被试cM增加用于试timeout的方法:
public void
methodTimeOut() {
try {
Thread.sleep(50);//?span lang="EN-US">50U?/span>
System.out.println("methodTimeOut");
} catch (InterruptedException e) {
}
}
b.看看怎么试q个ҎQ?/span>
@Test(timeout=5)
public void
methodTimeOut(){
baseClass.methodTimeOut();
}
1. 增加Q?span lang="EN-US">timeout=5Q这个设|项
2. q行?没有输出methodTimeOutQ说明这个方法没有执行完Q达到效果?/span>
junit中的assertҎ全部攑֜AssertcMQȝ一?/span>junitcMassertҎ的分cR?span lang="EN-US">
1.assertTrue/False([String message,]boolean condition);
判断一个条件是trueq是false。感觉这个最好用了,不用C来那么多的方法名?span lang="EN-US">
2.fail([String message,]);
p|Q可以有消息Q也可以没有消息?span lang="EN-US">
3.assertEquals([String message,]Object expected,Object actual);
判断是否相等Q可以指定输出错误信息?span lang="EN-US">
W一个参数是期望|W二个参数是实际的倹{?span lang="EN-US">
q个Ҏ对各个变量有多种实现。在JDK1.5中基本一栗?span lang="EN-US">
但是需要主意的?/span>float?/span>double最后面多一?/span>delta的|可能是误差范_不确定这?/span>
4.assertNotNull/Null([String message,]Object obj);
判读一个对象是否非I?/span>(非空)?span lang="EN-US">
5.assertSame/NotSame([String message,]Object expected,Object actual);
判断两个对象是否指向同一个对象。看内存地址?span lang="EN-US">
7.failNotSame/failNotEquals(String message, Object expected, Object actual)
当不指向同一个内存地址或者不相等的时候,输出错误信息?span lang="EN-US">
注意信息是必ȝQ而且q个输出是格式化q的?span lang="EN-US">
大家都知?/span>电视遥控器节目面板(ProgramPanQ是p目按钮组成,通过选择相应的节目按钮,可以切换到相应的节目屏道?/span>
下来让我们看看如何实现通过遥控器按钮选择节目屏道的过E吧?/font>
1、在q里Q先定义遥控器按钮(RemoteControlButtonQ接口:
public interface RemoteControlButton {
public abstract void selectProgram(); //选择节目屏道
}
2、再定义遥控器按钮(RemoteControlButtonQ接口的实现c:
AQ卡通节目按钮(CartonProgramButtonQ类Q?/span>
public class CartonProgramButton implements RemoteControlButton{
public void selectProgram() {
System.out.println("选择了卡通屏道!");
}
}
BQ电视剧节目按钮Q?/span>TvPlanProgramButtonQ类Q?/span>
public class TvPlanProgramButton implements RemoteControlButton {
public void selectProgram() {
System.out.println("选择了电视剧屏道Q?/span>");
}
}
CQ球节目按钮(FootProgramButtonQ类Q?/span>
public class FootProgramButton implements RemoteControlButton {
public void selectProgram() {
System.out.println("选择了球屏道!");
}
}
3、遥控器节目面板Q?/span>ProgramPanQ类Q用来控制节目按钮,昄节目
public class ProgramPan {
public static List programList() {
List list = new ArrayList(); //节目屏道按钮列表
list.add(new CartonProgramButton()); //卡通屏道按?/span>
list.add(new TvPlanProgramButton()); //电视剧屏道按?/span>
list.add(new FootProgramButton()); //球屏道按钮
return list;
}
}
4、编写测试类Q?/span>
public class TestCommand {
public static void main(String[] args) {
List list = ProgramPan.programList(); //获得节目屏道按钮
for (Iterator it = list.iterator();it.hasNext();)
( (RemoteControlButton) it.next()).selectProgram(); //选择节目屏道中对应的节目
}
}
5、说明:
AQ?/span>Command说白了就是通过选择一个个命oQ然后执行相应动作?/span>
BQ?/span>Command是对行ؓq行装的典型模式,在本例中通过遥控器节目面板(ProgramPanQ这个封装类来实现我们看电视节目的目的?/span>
CQ?/span>Command模式?/span>Facade(外观)模式g比较怼。都是通过装cLq行讉K的。如何区分,对这Ҏ也比较疑惑?/span>