2006年08月08日

常用正则表代式集

匹配中文字符的正则表达式: [\u4e00-\u9fa5]

匹配双字节字符(包括汉字在内):[^\x00-\xff]

应用:计算字符串的长度(一个双字节字符长度计2,ASCII字符计1)

String.prototype.len=function(){return this.replace([^\x00-\xff]/g,"aa").length;}

匹配空行的正则表达式:\n[\s| ]*\r

匹配HTML标记的正则表达式:/<(.*)>.*<\/\1>|<(.*) \/>/

匹配首尾空格的正则表达式:(^\s*)|(\s*$)

应用:javascript中没有像vbscript那样的trim函数,我们就可以利用这个表达式来实现,如下:

String.prototype.trim = function()
{
  return this.replace(/(^\s*)|(\s*$)/g, "");
}

利用正则表达式分解和转换IP地址:

下面是利用正则表达式匹配IP地址,并将IP地址转换成对应数值的Javascript程序:

function IP2V(ip)
{
re=/(\d+)\.(\d+)\.(\d+)\.(\d+)/g //匹配IP地址的正则表达式
if(re.test(ip))
{
return RegExp.$1*Math.pow(255,3))+RegExp.$2*Math.pow(255,2))+RegExp.$3*255+RegExp.$4*1
}
else
{
throw new Error("Not a valid IP address!")
}
}

不过上面的程序如果不用正则表达式,而直接用split函数来分解可能更简单,程序如下:

var ip="10.100.20.168"
ip=ip.split(".")
alert("IP值是:"+(ip[0]*255*255*255+ip[1]*255*255+ip[2]*255+ip[3]*1))

匹配Email地址的正则表达式:\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*

匹配网址URL的正则表达式:http://([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)?

利用正则表达式去除字串中重复的字符的算法程序:

var s="abacabefgeeii"
var s1=s.replace(/(.).*\1/g,"$1")
var re=new RegExp("["+s1+"]","g")
var s2=s.replace(re,"")
alert(s1+s2) //结果为:abcefgi

得用正则表达式从URL地址中提取文件名的javascript程序,如下结果为page1

s="http://www.9499.net/page1.htm"
s=s.replace(/(.*\/){0,}([^\.]+).*/ig,"$2")
alert(s)

利用正则表达式限制网页表单里的文本框输入内容:

用正则表达式限制只能输入中文:onkeyup="value=value.replace(/[^\u4E00-\u9FA5]/g,”)" onbeforepaste="clipboardData.setData(‘text’,clipboardData.getData(‘text’).replace(/[^\u4E00-\u9FA5]/g,”))"

用正则表达式限制只能输入全角字符: onkeyup="value=value.replace(/[^\uFF00-\uFFFF]/g,”)" onbeforepaste="clipboardData.setData(‘text’,clipboardData.getData(‘text’).replace(/[^\uFF00-\uFFFF]/g,”))"

用正则表达式限制只能输入数字:onkeyup="value=value.replace(/[^\d]/g,”) "onbeforepaste="clipboardData.setData(‘text’,clipboardData.getData(‘text’).replace(/[^\d]/g,”))"

用正则表达式限制只能输入数字和英文:onkeyup="value=value.replace(/[\W]/g,”) "onbeforepaste="clipboardData.setData(‘text’,clipboardData.getData(‘text’).replace(/[^\d]/g,”))"

"^\d+$"  //非负整数(正整数 + 0)
"^[0-9]*[1-9][0-9]*$"  //正整数
"^((-\d+)|(0+))$"  //非正整数(负整数 + 0)
"^-[0-9]*[1-9][0-9]*$"  //负整数
"^-?\d+$"    //整数
"^\d+(\.\d+)?$"  //非负浮点数(正浮点数 + 0)
"^(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*))$"  //正浮点数
"^((-\d+(\.\d+)?)|(0+(\.0+)?))$"  //非正浮点数(负浮点数 + 0)
"^(-(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*)))$"  //负浮点数
"^(-?\d+)(\.\d+)?$"  //浮点数
"^[A-Za-z]+$"  //由26个英文字母组成的字符串
"^[A-Z]+$"  //由26个英文字母的大写组成的字符串
"^[a-z]+$"  //由26个英文字母的小写组成的字符串
"^[A-Za-z0-9]+$"  //由数字和26个英文字母组成的字符串
"^\w+$"  //由数字、26个英文字母或者下划线组成的字符串
"^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$"    //email地址
"^[a-zA-z]+://(\w+(-\w+)*)(\.(\w+(-\w+)*))*(\?\S*)?$"  //url
/^(d{2}|d{4})-((0([1-9]{1}))|(1[1|2]))-(([0-2]([1-9]{1}))|(3[0|1]))$/   // 年-月-日
/^((0([1-9]{1}))|(1[1|2]))/(([0-2]([1-9]{1}))|(3[0|1]))/(d{2}|d{4})$/   // 月/日/年
"^([w-.]+)@(([[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.)|(([w-]+.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(]?)$"   //Emil
"(d+-)?(d{4}-?d{7}|d{3}-?d{8}|^d{7,8})(-d+)?"   //电话号码
"^(d{1,2}|1dd|2[0-4]d|25[0-5]).(d{1,2}|1dd|2[0-4]d|25[0-5]).(d{1,2}|1dd|2[0-4]d|25[0-5]).(d{1,2}|1dd|2[0-4]d|25[0-5])$"   //IP地址

2006年02月24日

这个插件是一个代码生成器,它能够让开发者使用appfuse五个组合框架struts, jsf, spring, tapestry或webwork其中的一个来快速开发We应用程序。 https://appfusegenerator.dev.java.net/

本篇文章使用aigaogao Blog软件发布, “我的Blog要备份”

https://equinox.dev.java.net/

本篇文章使用aigaogao Blog软件发布, “我的Blog要备份”

2006年02月23日

接口和抽象类
在去年的12 月初, 我在Design Patterns Explained 的讨论组上看到这个系列帖子,
觉得很有意思, 所以整理出来。如果你喜欢这种形式, 请写信到gigix@263.net, 我会整理
更多有趣的帖子出来。
面向对象中有一个术语: 派生。在用Java 编程的时候, 派生的来源可以是抽象类
( abstract class)、也可以是接口( interface)。何时使用抽象类? 何时使用接口? 这几
个帖子正是讨论了这个问题。
( 提出问题)
Alan, 我买了你的书, 读到了其中“ Bridge 模式” 那一章。然后, 我把一位朋友— —
他是个Java专家, 有五、六年使用Java的经验— — 叫来, 把你的书给他看, 并请他为我解
释你的Java 代码。我自己比较偏向于使用接口, 而你更多的使用了抽象类。他得出的结论
是: 你偏向于使用抽象类, 这反映出你对C++的爱好多于对Java的爱好。毕竟Java才有接
口这个概念, 而C++只有抽象类。因此, 我们感觉: 如果把你的示例代码中的抽象类换成接
口, 也许会使它们更能反映出实际情况和“ 按照协议设计” 的思想。实际上, 如果读过Peter
Coad的书, 你会发现他也主要使用接口。
( Alan Shalloway 的答复)
感谢你对这本书的评论。
我用Java 也有五、六年了, 所以我不认为使用抽象类就表示没有经验。实际上, 我觉
得Java 的一大缺憾就是它不支持多继承, 所以开发者经常被迫使用接口, 因为继承只能使
用一次。当然了, 这里面涉及到多继承的性能问题和基类成员的名字重复问题。但是不管怎
么说, 不允许多继承总是值得质疑的。
至于我这本书, 我选择使用抽象类, 是因为大多数人都熟悉它— — 不是每个人都喜欢去
看章节后面的C++示例的, 所以我决定用这种更通用的示例。
接口的问题就是它无法拥有默认行为。如果你用接口来定义一个什么东西并希望跨过这
层封装来添加一些行为, 你就必须使用委托。在我的记忆中, 有很多次就是因为不支持默认
行为而造成了麻烦。为了绕过这个限制, 我会使用委托, 但是这也是个痛苦的事情。
BTW: 我很喜欢Coad的书— — 我认为那是有史以来最好的设计书籍之一。
( Scott Bain 的跟贴)
从我自己的角度来说, 我是一个Java程序员( 从来没用过C++)。但是每当我遇到单继
承关系的时候, 我倾向于使用抽象类, 尽管我知道Java 不支持多继承。为什么这样? 因为
即使我现在没有任何默认行为, 将来我还可以放进一些默认行为, 而不必修改子类的结构。
而且, 如果要在一个继承体系中放置工厂方法来生成子类的实例, 抽象类是就是最好、
最合乎逻辑的地方。因为客户对象只需要“ 知道” 抽象类就可以了, 通过转型得到的实例完
全封装了子类的存在和选择子类的规则。这使得系统的扩展变得简单,而且不会造成副作用。
只有当需要让一个类“ 象什么” 而非“ 是什么” 的时候, 我才会使用接口。
( Alan Shalloway 的回复)
PDF created with FinePrint pdfFactory trial version http://www.fineprint.com
精彩! 很高兴看到这样的回复! J
我也发现, 在Java 中缺少默认行为造成的影响似乎还没有真正引起人们的重视。从一
开始, SUN 似乎就忽略了向后兼容的问题。我可以大胆的预言: 完全用接口构建成的系统将
在几年之后发现这是一个大问题。据我所知, 有几个基于COM 模型( 也就是接口) 构建的应
用程序的开发者现在已经陷入了维护问题, 因为当他们想修改接口来应对新的情况( 比如添
加新的参数或新的方法) 时, 这会耗去他们大量的时间。如果使用抽象类, 修改就不会这么
困难, 因为你可以只修改默认方法, 子类就不用动了。
我可不是说不能使用接口, 但是你必须清楚: 有些时候应该使用接口, 而有些时候就是
应该使用抽象类。不幸的是, 由于Java不支持多继承, 你必须很小心的做出选择。
( Scott Bain 的跟贴)
同样精彩! 关于接口, 我再来废话几句:
有时候, 一个东西可以“ 是” 几个东西。不明白这句话吗? 我就拿我自己来举个例子吧:
我“ 是”一个程序员、一个老师、一个作者、一个父亲… … 你可以说我“ 实现了老师的接口”,
学生们会向我询问关于上课的问题。我还“ 实现了父亲的接口”, 但是我的学生可能知道这
一点, 也可能不知道。如果他们知道这一点, 他们就可以放心的向我询问关于抚养小孩的问
题了。
但是, 尽管我实现了所有这些接口, 我还有一个抽象类— — “ 人”。这个抽象类给我提
供了所有的默认行为( 比如说我偶尔会走背运)。

本篇文章使用aigaogao Blog软件发布, “我的Blog要备份”

2006年02月20日

作者简介



肖菁,软件工程师,IBM developerWorks/Bea dev2dev/sun 技术开发者撰稿人,主要研究J2EE、web services以及他们在websphere、weblogic平台上的实现,拥有IBM的 Developing With Websphere Studio证书。您可以通过guilaida@163.com和作者取得联系,或者查看作者的主页获取更多信息。


文章摘要



Cache是一种用于提高系统响应速度、改善系统运行性能的技术。尤其是在Web应用中,通过缓存页面的输出结果,可以很显著的改善系统运行性能。本文中作者给大家介绍一个实现J2EE框架中Web应用层缓存功能的开放源代码项目—-OSCache。通过应用OSCache,我们不但可以实现通常的Cache功能,还能够改善系统的稳定性。


1 面临的问题



1.1 需要处理的特殊动态内容



在信息系统建设过程中我们通常会遇到这样的问题:


1. 基础数据的变更问题


信息系统中需要处理的基础数据的内容短时间内是不会发生变化的,但是在一个相对长一些的时间里,它却可能是动态增加或者减少的。


举个例子:电子商务中关于送货区域的定义,可能短时间内不会发生变化,但是随着电子商务企业业务的扩大,系统中需要处理的送货区域就可能增加。所以我们的系统中不得不在每次向客户展示送货区域信息的时候都和数据库(假设送货区域信息保存在数据库中,这也是通常采用的处理方法)进行交互。


2. 统计报表(不仅限于统计报表)的问题


一般来说,统计报表是一个周期性的工作,可能是半个月、一个月或者更长的时间才会需要更新一次,然而统计报表通常是图形显示或者是生成pdf、word、excel等格式的文件,这些图形内容、文件的生成通常需要消耗很多的系统资源,给系统运行造成很大的负担。


1.2 问题的共同点



通过比较分析,不难发现这两类问题有一些共同点:


1、被处理的内容短时间不变,所以短时间内可以作为静态内容进行处理


2、在一个不太长的时间内,被处理的内容可能或者必定产生变化,所以必须将他们作为动态内容进行处理


3、在合理的时间区段内可以忽略被处理内容变化后带来的影响


4、对这些内容的处理动作比较消耗系统性能,影响系统响应时间


1.3 解决方法



缓存技术可以帮助我们很好的解决这个问题:


1、缓存信息


当上述的基础数据或者统计报表第一次被访问时,被处理的内容被当作动态信息,基础数库从数据库中获得,统计报表也会被生成符合要求的图形、文件,然后这些信息都会被放入缓存信息中。


2、响应信息由缓存提供


当上述的基础数据或者统计报表继续被访问时,系统将会首先检查缓存信息中是否有对应的内容和我们设定的缓存规则,如果符合缓存信息存在而且符合缓存规则,给出的响应将来自于缓存信息,如果没有或者缓存信息已经不符合设定的要求,系统将重复上一步的动作。


很显然,上面的步骤2中,多数情况下,当用户请求到达时,被处理的内容将来自于缓存,所以大大的减少了与数据库的交互,或者不再需要为每个请求都生成一次报表图形或者文件,这部分工作的减少对于降低系统性能消耗、提高系统稳定性和并发处理能力是非常有益的。


2 OSCache简介



OSCache是OpenSymphony组织提供的一个J2EE架构中Web应用层的缓存技术实现组件,它的出现解决了我们面临的问题。 OSCache目前最新的稳定版本是2.0,本文中的例子都是基于这个版本的,如果大家运行例子的过程中发生问题,请首先确认是否采用了正确的软件版本。


2.1 主要特征



1. 兼容多种支持JSP的web服务器


已经通过兼容测试的web服务器包括OrionServer (1.4.0或者以上版本) 、Macromedia JRun (3.0或者以上版本) 、BEA Weblogic (7.x或者以上版本) 、IBM Websphere (5.0版本)、Silverstream (3.7.4版本)、Caucho Resin (1.2.3或者以上版本)、Tomcat (4.0或者以上版本) ,其他支持servlet2.3、jsp1.2的web服务器应该都是完全兼容OSCache的。


2. 可选的缓存区


你可以使用内存、硬盘空间、同时使用内存和硬盘或者提供自己的其他资源(需要自己提供适配器)作为缓存区。



  • 使用内存作为缓存区将可以提供更好的性能
  • 使用硬盘作为缓存区可以在服务器重起后迅速恢复缓存内容
  • 同时使用内存和硬盘作为缓存区则可以减少对内存的占用

3. 灵活的缓存系统


OSCache支持对部分页面内容或者对页面级的响应内容进行缓存,编程者可以根据不同的需求、不同的环境选择不同的缓存级别。


4. 容错


在一般的web应用中,如果某个页面需要和数据库打交道,而当客户请求到达时,web应用和数据库之间无法进行交互,那么将返回给用户”系统出错”或者类似的提示信息,如果使用了OSCache的话,你可以使用缓存提供给用户,给自己赢得维护系统或者采取其他补救的时间。


其它特性还包括对集群的支持、缓存主动刷新等特性,大家可以参考OpenSymphony网站上的其他资源获取更多的信息。


3 OSCache组件的安装



OSCache是一个基于web应用的组件,他的安装工作主要是对web应用进行配置,大概的步骤如下:


1. 下载、解压缩OSCache


请到OSCache的主页http://www.opensymphony.com/oscache/download.html下载Oscache的最新版本,作者下载的是OSCache的最新稳定版本2.0。


将下载后的。Zip文件解压缩到c:\oscache(后面的章节中将使用%OSCache_Home%来表示这个目录)目录下


2. 新建立一个web应用


3. 将主要组件%OSCache_Home%\oscache.jar放入WEB-INF\lib目录


4. commons-logging.jar、commons-collections.jar的处理



  • OSCache组件用Jakarta Commons Logging来处理日志信息,所以需要commons-logging.jar的支持,请将%OSCache_Home%\lib\core\commons-logging.jar放入classpath(通常意味着将这个文件放入WEB-INF\lib目录)
  • 如果使用JDK1.3,请将%OSCache_Home%\lib\core\commons-collections.jar放入classpath,如果使用JDK1.4或者以上版本,则不需要了

5. 将oscache.properties、oscache.tld放入WEB-INF\class目录



  • %OSCache_Home%\oscache.properties包含了对OSCache运行特征值的设置信息
  • %OSCache_Home%\oscache.tld包含了OSCache提供的标签库的定义内容

6. 修改web.xml文件


在web.xml文件中增加下面的内容,增加对OSCache提供的taglib的支持:

 
oscache
/WEB-INF/classes/ oscache.tld


4 开始使用OSCache中的缓存组件



OSCache中按照缓存范围的不同分为两种不同的方式:一种是缓存JSP页面中部分或者全部内容,一种是基于整个页面文件的缓存。


4.1 JSP部分内容缓存



4.1.1 Cache-OSCache提供的缓存标签



这是OSCache提供的标签库中最重要的一个标签,包括在标签中的内容将应用缓存机制进行处理,处理的方式将取决于编程者对cache标签属性的设置。


第一次请求到达时,标签中的内容被处理并且缓存起来,当下一个请求到达时,缓存系统会检查这部分内容的缓存是否已经失效,主要是以下几项:



  • 1. 缓存时间超过了cache标签设置的time或者duration属性规定的超时时间
  • 2. cron属性规定的时间比缓存信息的开始时间更晚
  • 3. 标签中缓存的内容在缓存后又被重新刷新过
  • 4. 其他缓存超期设定

如果符合上面四项中的任何一项,被缓存的内容视为已经失效,这时被缓存的内容将被重新处理并且返回处理过后的信息,如果被缓存的内容没有失效,那么返回给用户的将是缓存中的信息。


cache标签的属性说明:


key – 标识缓存内容的关键词。在指定的作用范围内必须是唯一的。默认的key是被访问页面的URI和后面的请求字符串。


你可以在同一个页面中使用很多cache标签而不指定他的key属性,这种情况下系统使用该页面的URI和后面的请求字符串,另外再自动给这些key增加一个索引值来区分这些缓存内容。但是不推荐采用这样的方式。


scope – 缓存发生作用的范围,可以是application或者session


time – 缓存内容的时间段,单位是秒,默认是3600秒,也就是一个小时,如果设定一个负值,那么这部分被缓存的内容将永远不过期。


duration – 指定缓存内容失效的时间,是相对time的另一个选择,可以使用简单日期格式或者符合USO-8601的日期格式。如:duration=’PT5M’ duration=’5s’等


refresh – false 或者true。


如果refresh属性设置为true,不管其他的属性是否符合条件,这部分被缓存的内容都将被更新,这给编程者一种选择,决定什么时候必须刷新。


mode – 如果编程者不希望被缓存的内容增加到给用户的响应中,可以设置mode属性为”silent”


其它可用的属性还包括:cron 、groups、language、refreshpolicyclass、refreshpolicyparam。


上面的这些属性可以单独使用,也可以根据需要组合使用,下面的例子将讲解这些常用属性的使用方式。


4.1.2 Cache标签实例分析:



1. 最简单的cache标签用法


使用默认的关键字来标识cache内容,超时时间是默认的3600秒






<%
//自己的JSP代码内容
%>



2. 用自己指定的字符串标识缓存内容,并且设定作用范围为session。





 
<%
//自己的JSP代码内容
%>



3.动态设定key值,使用自己指定的time属性设定缓存内容的超时时间,使用动态refresh值决定是否强制内容刷新。


因为OSCache使用key值来标识缓存内容,使用相同的key值将会被认为使用相同的的缓存内容,所以使用动态的key值可以自由的根据不同的角色、不同的要求决定使用不同的缓存内容。






<%
//自己的JSP代码内容
%>


5. 使用duration属性设置超期时间






<%
//自己的JSP代码内容
%>


6. 使用mode属性使被缓存的内容不加入给客户的响应中






<%
//自己的JSP代码内容
%>


4.2 用CashFilter实现页面级缓存



在OSCache组件中提供了一个CacheFilter用于实现页面级的缓存,主要用于对web应用中的某些动态页面进行缓存,尤其是那些需要生成pdf格式文件/报表、图片文件等的页面,不仅减少了数据库的交互、减少数据库服务器的压力,而且对于减少web服务器的性能消耗有很显著的效果。


这种功能的实现是通过在web.xml中进行配置来决定缓存哪一个或者一组页面,而且还可以设置缓存的相关属性,这种基于配置文件的实现方式对于J2EE来说应该是一种标准的实现方式了。


[注] 只有客户访问时返回http头信息中代码为200(也就是访问已经成功)的页面信息才能够被缓存


1. 缓存单个文件


修改web.xml,增加如下内容,确定对/testContent.jsp页面进行缓存。






CacheFilter
com.opensymphony.oscache.web.filter.CacheFilter


CacheFilter

/testContent.jsp



2. 缓存URL pattern


修改web.xml,增加如下内容,确定对*.jsp页面进行缓存。

 
CacheFilter
com.opensymphony.oscache.web.filter.CacheFilter


CacheFilter

*.jsp

3. 自己设定缓存属性

在页面级缓存的情况下,可以通过设置CacheFilter的初始属性来决定缓存的一些特性:time属性设置缓存的时间段,默认为3600秒,可以根据自己的需要只有的设置,而scope属性设置,默认为application,可选项包括application、session

 
CacheFilter
com.opensymphony.oscache.web.filter.CacheFilter

time
600

scope
session



CacheFilter

*.jsp


 


5 性能测试结果



5.1 测试环境



系统平台:windows 2000 高级服务器/ P3 800 /512M内存


web服务器:websphere 5.0


数据库服务器:mysql 4.0.18-nt


性能测试用工具:apache Jmeter


5.2 测试计划



这次性能测试对比方为使用缓存和不使用缓存两种,他们的访问代码都是一样的:通过数据源从本地mysql数据库中获取person表的所有记录,然后显示在页面上。


测试中将模仿10个用户,每个用户发起5次请求,然后统计所有访问花费的时间。


5.3 测试结果



使用缓存后的测试结果 不使用缓存时的测试结果


所有请求花费的总时间(毫秒) 20569 22870


性能测试的详细结果请大家查看下载内容中的《不使用cache时的系统性能测试结果.txt》和《使用cache后系统性能测试结果.txt》


6 总结



在J2EE系统中,我们经常需要处理一些特殊的动态内容,这些内容在一个时间段内的变更非常有限,但是又不得不将他们确定为动态内容进行输出,而且非常消耗数据库系统资源或者web服务器的资源,这时我们就可以采用Cache—-一种用于提高系统响应速度、改善系统运行性能的技术—-来优化我们的系统。尤其是在Web应用中,这种处理可以很显著的改善系统运行性能。


本文中作者给大家介绍一个实现J2EE框架中Web应用层缓存功能的开放源代码项目—-OSCache。它提供了在J2EE系统中实现缓存需要的丰富的功能。通过应用OSCache,我们不但可以实现通常的Cache功能、自由的设定cache的相关特性比如缓存时间段/缓存内容等,提升系统性能,而且还能有效的改善系统的稳定性。除此之外,OSCache组件还提供了更多的特性比如集群、容错、灵活的缓存区选择等。


作者根据自己的使用经验给大家提供了一些简单的例子,他们部分演示了如何使用OSCache组件提供的丰富特性,OSCache提供的特性远不止这些,需要大家在今后的时间里深入的研究,同时也希望大家通过E-mail和作者贡献研究成果。


参考资料



OpenSymphony网站中关于OSCache的部分 http://www.opensymphony.com/oscache/


OSCache下载地址 http://www.opensymphony.com/oscache/download.html

本篇文章使用aigaogao Blog软件发布, “我的Blog要备份”

2006年01月21日


转自:http://www.jdon.com/jive/thread.jsp?forum=91&thread=24681


 


前段时间读了《软件的最大追求是什么》,击节叫好,深以为然,虽然该文章很多地方显得有点极端。

如今的软件系统越来越复杂,如果软件的结构不好会影响软件的可维护性,重构代码是一件极其痛苦的事情。

关于软件的复杂性问题,我做了一些思考:

1) Cyclomatic Complexity (圈复杂性 = number of decision points +1    其中number of decision points是指一个if else之类的条件判断语句 (引自《软件的最大追求是什么》)

if else 语句可以被行为模式/策略模式代替,不妨看下列的例子:

假设我们要根据条件判断来完成不同的行为,用if else 是这样写的:

main() {

if(case A){

//do with strategy A

}else(case B){

//do with strategy B

}else(case C){

//do with strategy C

}

}



用策略模式则如下:

class runner{

do();

}



class A extends runner{

do(){

//do with strategy A

}

}



class B extends runner{

do(){

//do with strategy B

}

}



class C extends runner {

do(){

//do with strategy C

}

}



main(){

runner.do();

}



用了策略模式后main()中的语句的确简单多了,再也看不到该死的if else了^_^酷~~~



可实际上是这样简单吗???仔细研究一下上面的代码就能看出问题出来,首先这两段代码中的A、B、C的实际意义是不一样的:第一段代码中ABC代表的是某一个逻辑条件的值而第二段中的ABC则是具体的类;第二段得到如此简化的前提是它得到的逻辑条件就是一个类;如果得到的仍然只是一个逻辑条件,那么为了达到代码简化的效果,必须由另一个类(或方法)完成这种逻辑条件到具体类的转换,会出现类似下列的代码

class RunnerFactory{

runner getInstante(case){

if (case A) return new A();

else if (case B) return new B();

else if (case C) return new C(); } }



从测试的角度来说,两者的测试分支都是3,复杂度相同,而第二种方法在很多的时候貌似还要测试借口的有效性。用策略模式还有一个缺点就是会使系统中的类的数量大大的增加,如上的例子,采用if else类的数量为1,而采用策略模式的类个数为5个或6个(主要取决于逻辑映射是否用单独的类)。

如果逻辑判断的条件有三个,每个逻辑条件有三种可能的话,用策略模式系统至少会增加10个新类;如果条件更多的话类的个数也会更多 这么看来GOF的策略模式还要它干嘛??

当然不是,策略模式在很多情况下是一种非常好的解决方案。

这还要从if else 语句造成程序复杂以至难以维护的真正原因说起。就我个人的感觉真正造成if else语句难以维护的原因是每一个逻辑分支中的处理语句过长。比如我现在工作中维护的代码,常常一个条件下面的业务处理语句有两三千行,每次我光确定某个逻辑分支的结束位置就要找半天,头晕-_-!。如果是多层条件的话情况就更糟了。一个分支就一千多行,几个分支上万行自然很难维护。 if else 语句本质上是程序的流程控制语句,而分支中N长的代码通常是业务处理语句。

行为模式/策略模式就是把流程判断和业务处理进行了一次解耦,将业务逻辑封装成一个个单独的类。换句话说,行为模式/策略模式并不是不需要if else 语句(事实上该判断的还是要判断),只不过的换了地方或者是别的代码帮你做了。另一方面,进行逻辑判断的语句被集中起来而不是分散在程序的各个角落,有利于逻辑本身的维护。策略模式/行为模式还有一个明显的好处就是如果新增加了一种状态,我们只需要新增加一个策略类(同上的ABC)就可以了,避免了在程序中改动那些大段大段让人厌烦的if else 语句。

所以对于你的程序来说到底是使用设计模式还是简单的使用if else 关键在于你的程序是否复杂,有没有必要将控制逻辑和业务逻辑进行解耦。当然如果你可以用别的方式实现解耦也是非常好的。



2) Response for Class(RFC) 当一个类和很多其他类存在依赖时,它就变得复杂甚至难以修改和维护,这样,RFC值越大,表示你的系统味道越坏。(引自《软件的最大追求是什么》)



复杂性是由类与类之间的依赖关系(dependency)造成的。

具体如下所示:

interface Runner;

class A implement runner{ do(){}; }



一个大型的系统中很多地方用到了runner接口,于是在很多地方出现了如下的相同代码:

{

Runner r = new A();

r.do();

}



如果因为某种原因runner接口的实现类改为B,则在所有用到runner接口的地方代码都要统统改为:

{

//Runner r = new A();

Runner r = new B(); r.do();

}



这些遍布系统各个角落的改动是繁琐且容易出错的。

于是出现了各种框架和模式,其中最著名的当然是IOC(Inversion of Control)反转控制或者称之为依赖型注射(Dependency Injection) 那些讨厌的代码变成了如下:

{

ApplicationContext ctx = new FileSystemXmlApplicationContext(“spring.xml”);

Runner r = (Runner)ctx.getBean(“Runner”);

r.do();

}



这样我们就不需要对各个角落的代码进行维护了,容器会自动的为我们选择合适的类。

我到觉得维护的工作还是我们的,为了让容器注射入合适的类,我们必须要维护一个叫spring.xml的配置文件,或者如果你不喜欢配置文件这个东东的话(比如偶)就得自己写一个注册类,将依赖关系一一注册进去。你还要为使用该接口的类定义一个以该接口为参数的构造函数或者该接口的setter方法,好让容器可以将实现类顺利的注射进来。

该做的还是要做,只不过换了一个地方做而已。但就是换了个地方,实现了对依赖关系的集中维护(又是集中),大大的改善了系统的结构,明确了不同职责单位之间的分工。呵呵,有时自己也极端的觉得设计的工作说到底就是解决代码结构的问题^_^



IOC一直是和轻量级框架联系在一起的。所谓的重量级框架EJB也有实现相同功能的解决方案:Service Locator Service Locator就是一个定位器,它维护了一个数据结构(比如一个表),通过这个定位器你可以准确的定位到你的接口想要的实现类,Service Locator同样使你免去了改变接口实现类后的维护恶梦:

{

Runner r = (Runner)ServiceLocator.lookup(“Runner”);

r.do();

}



无论是IOC还是Service Locator都帮助你维护了类之间的依赖关系。那么是否我们在编程中一定要用呢,这又是个权衡的问题,IOC带来了很多的好处,不过我个人认为它的代码是让人费解的(你根本不知道它做了什么),并且为了用它,一方面你要借助于容器,另一方面你要编写配置文件,要为依赖型注射提供合适的途径。

如果你的系统类之间的依赖型错综复杂,需求的变化常常导致实现类的变化,同时你又希望采用测试驱动的快速开发模式,IOC毫无疑问是一个完美的解决方案;如果你的系统不存在上述的问题,为什么不简简单单的在程序中写死呢,何苦去维护一堆配置文件(我所在的开发部门貌似都比较痛恨配置文件这个东东)。Service Locator也有很多缺点,被骂的最多的就是没法快速测试。

反转控制,即转换控制权。依赖关系控制权的转换是对代码结构的一次重构,重构的目标还是解耦,让不同的职责代码集中放到不同的地方,于是程序员可以更加专注的解决特定的问题,比如业务逻辑。

程序设计的发展就是对代码结构的不断调整,不断解耦,让特定的代码解决特定的问题而不是什么都混在一起。从面向过程到面向对象难道不是这样吗,封装的本质也是解耦。



在实际问题的解决当中,最重要的信条就是合适,要记住任何结构的改进都会付出代价,你的改进是否值得你为此付出的代价。比如当你做一个嵌入式程序的时候你首要考虑的自然是效率问题;而如果你做的是一个ERP产品,在系统设计的时候,光系统的可维护性问题就让你不得不绞尽脑汁考虑一下代码的结构。



一句话,只做最对的。

程序设计的最大追求就是在合适的地方做正确的事。

本篇文章使用aigaogao Blog软件发布, “我的Blog要备份”

2005年11月22日


 
 /** 


* 提供小数位四舍五入处理。 


* @param v 需要四舍五入的数字 


* @param scale 小数点后保留几位 


* @return 四舍五入后的结果 


*/ 


public static double round(double v,int scale){ 
String temp=”#,##0.”; 
for (int i=0;i
temp+=”0″; 

return Double.valueOf(new java.text.DecimalFormat(temp).format(v)); 



Java带的Math.round不提供精确到位数。上面方法可以完成此功能

本篇文章使用aigaogao Blog软件发布, “我的Blog要备份”

2005年11月12日

float f=new Float(“2.25″).floatValue();


本篇文章使用aigaogao Blog软件发布, “我的Blog要备份”

2005年11月08日


  • 将字串 String 转换成数字类型


String 转 int



1.) int i = Integer.parseInt([String]); 或 i = Integer.parseInt([String],[int radix]);
2.) int i = Integer.valueOf(my_str).intValue();


String 转 Float



Float f = Integer.valueOf(my_str).floatValue();


String 转 float



float f=new Float(my+str).floatValue();



注: 字串转成 Double, Float, Long 的方法大同小异.



  • 将整数 int 转换成字串 String


1.) String s = String.valueOf(i);
2.) String s = Integer.toString(i);
3.) String s = “” + i;

注: Double, Float, Long 转成字串的方法大同小异.



JAVA中常用数据类型转换函数
虽然都能在JAVA API中找到,整理一下做个备份。


 

string->byte
Byte static byte parseByte(String s) 

 

byte->string
Byte static String toString(byte b)

 

char->string
Character static String to String (char c)

 

string->Short
Short static Short parseShort(String s)

 

Short->String
Short static String toString(Short s)

 

String->Integer
Integer static int parseInt(String s)

 

Integer->String
Integer static String tostring(int i)

 

String->Long
Long static long parseLong(String s)

 

Long->String
Long static String toString(Long i)

 

String->Float
Float static float parseFloat(String s)

 

Float->String
Float static String toString(float f)

 

String->Double
Double static double parseDouble(String s)

 

Double->String
Double static String toString(Double

 这是一个例子,说的是JAVA中数据数型的转换.供大家学习引


 

package cn.com.lwkj.erts.register;
import java.sql.Date;
public class TypeChange {
  public TypeChange() {
  }
  //change the string type to the int type
  public static  int stringToInt(String intstr)  {
    Integer integer;
    integer = Integer.valueOf(intstr);
    return integer.intValue();
  }
  //change int type to the string type
  public static String intToString(int value)  {
    Integer integer = new Integer(value);
    return integer.toString();
  }
  //change the string type to the float type
  public static  float stringToFloat(String floatstr)  {
    Float floatee;
    floatee = Float.valueOf(floatstr);
    return floatee.floatValue();
  }
  //change the float type to the string type
  public static String floatToString(float value)  {
    Float floatee = new Float(value);
    return floatee.toString();
  }
  //change the string type to the sqlDate type
  public static java.sql.Date stringToDate(String dateStr)  {
    return  java.sql.Date.valueOf(dateStr);
  }
  //change the sqlDate type to the string type
  public static String dateToString(java.sql.Date datee)  {
    return datee.toString();
  }

 

  public static void main(String[] args)  {
    java.sql.Date day ;
    day = TypeChange.stringToDate(“2003-11-3″);
    String strday = TypeChange.dateToString(day);
    System.out.println(strday);
  }

 


}

 


本篇文章使用aigaogao Blog软件发布, “我的Blog要备份”

2005年11月02日

数组 哈希表 属性类


动态小数据操作是WEB开发中不可避免的,这就涉及到数组 哈希表 属性类等几个功能;本文提供本人常用语法和简单解释:


一般数组定义



//定义数组catalogs
public static final String[] catalogs =
{“Business”,”Entertainment”,”Law”,”Real Estate”,”Medical”,”Services”,”Computers & Internet”,”Food”,”Other”};


//在form中逐个显示数组内容


 


vector


vector是java的动态数组的功能, 随着更多元素加入其中,数组变的更大.

public Vector getGraphFile(Hashtable hashtable)
{
  Vector vector = new Vector();
  for(Enumeration enumeration = hashtable.keys(); enumeration.hasMoreElements();)
  {
    String s1 = (String)enumeration.nextElement();
    if (isGraphFile(s1))
    {
      vector.addElement(s1);
    }
  }

  return vector;
}
本例是将hashtable 的关键词转为vector数组. 要遍历vector或hashtable使用java的Enumeration

for (Enumeration e = v.elements(); e.hasMoreElements();)
{
  out.println((String)e.nextElement());
}


这是将Vector中的值输出.注意使用类型强制转换.



Hashtable



Hashtable是一个关键字对应关键值的表,使用该表,可以迅速索引到某个值.这个表很好用,补充Vector不足.
Hashtable hashtable=new Hashtable();
hashtable.put(“1″,”男”);
hashtable.put(“2″,”女”);
out.println(“性别是”+(String)hashtable.get(“2″))


如果一个关键字对应多个关键值,而且这几个关键词中不一定是字符形,可能包含其他复杂类型:
//加入数据
hashtable.put(name, new UploadedFile(dir.toString(), fileName,filePart.getContentType()));


//取数据
UploadedFile file = (UploadedFile)hashtable.get(name);
return file.getFilesystemName(); //得fileName
return file.getContentType(); //得type


//类 UploadedFile定义:
class UploadedFile {


private String dir;
private String filename;
private String type;


UploadedFile(String dir, String filename, String type) {
this.dir = dir;
this.filename = filename;
this.type = type;
}


public String getContentType() {
return type;
}


public String getFilesystemName() {
return filename;
}


public File getFile() {
if (dir == null || filename == null) {
return null;
}
else {
return new File(dir + File.separator + filename);
}
}


台湾蔡学镛先生有专门一篇 谈论Collection 文章,有兴趣者可进一步研究.



Properties



下面是获取系统属性的方法
import java.util.Properties;
public class getSystemProperties
{
  public static void main(String args[])
  {
    //通过获得系统属性构造属性类 prop
    Properties prop = new Properties(
    System.getProperties() );
    //在标准输出中输出系统属性的内容
    prop.list(System.out);
  }
    //根据获取的系统属性确定程序执行流程
}


属性文件的操作:
Properties prop = new Properties();
try
{
  FileInputStream fis=new FileInputStream(“firstProp.txt”);
}catch (FileNotFoundException ex) {
  throw new Exception(“error:”+ex.getMessage());
}
prop.load(fis);
out.println(getProperty(“fruits”))



Properties可以对下列文件格式进行操作:
firstProp.txt


Truth = Beauty
Truth:Beauty
Truth :Beauty
fruits apple, banana, pear, \
    cantaloupe, watermelon, \
    kiwi, mango


 


 

本篇文章使用aigaogao Blog软件发布, “我的Blog要备份”