|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
大型的应用一般不会用这些框架(因为性能考虑);开发人员根据需要选择用一些框架,也可以不选用框架;不用框架并不代表要自己写框架;修改框架的可能性更小。作者ariesram
电子邮件地点ariesram@linuxaid.com.cn,或ariesram@may10.ca
本文及自己一切文章均搜集在bambi.may10.ca/~ariesram/articles/中。
本文受权给www.linuxaid.com.cn。
注释:
任何一种面向对象言语都有它的库。任何一种面向对象的言语也都离不开库的撑持。用我们熟习的面向对象言语为例子,C++有STL,Java有API函数,详细到开辟工具,VisualC++供应了MFC,BorlandC++供应了OWL。也有良多第三方供应的库。我们在开辟使用程序的时分,也觉察我们也许必要某些特定的库来完成特定的功效。那末,怎样编写本人的库呢?
使用Java的面向对象特征,如封装,承继,和一些计划形式,我们能够用尺度的办法来创建本人的库。必要分明的一点:在你必要完成某个功效的时分,不要用专有的、特定的办法往编写代码,而要通盘思索,用通用的办法来完成,如许,在堆集了必定数目的库今后,你就可以重用这些库来完成新的功效,而不必每回都重头编写代码。这也是面向对象言语供应给我们的优点。也能够用J2EE的标准为例子,J2EE供应了一个CBT(ComponentBasedTransaction),一切的组件都敬服J2EE标准,在CBT中运转,如许,编写开辟而且重用尺度的通用的组件库,能够延长开辟周期勤俭本钱,而且可以在任何切合J2EE标准的使用程序服务器(APPLICATIONSERVER)中运转,而且能够承继,扩大已有的组件库完成新的义务大概顺应新的变更。
在本文中,我将先会商怎样创建本人的库,必要依据哪些尺度,然后给出一个复杂的例子。在第二部分中,我将经由过程一个功效对照完美的库来做进一步的会商。
甚么是库?库是一个能够重用的组件,它接纳通用的计划,完成通用的义务,能够勤俭开辟者的时间,延长开辟周期勤俭开辟本钱。一个计划完美的库,其实不只是为了完成某一个特定的义务,而是能够完成各类分歧的义务。计划一个库是坚苦的。写一个算法其实不难,可是计划库的时分必要一种对照好的布局,它可以被用在各类必要的情况下,完成各类分歧的义务,可是还不克不及影响利用它的程序代码布局。
为何要重用代码?重头开辟一个新的软件,事情量长短常伟大的,不管你用甚么工具甚么言语。而代码重用可以勤俭年夜部分工夫,而把工夫花在新的功效的开辟上。从必定的意义下去说,写一个新的软件是使用了现有的代码,从头拼装以完成新的功效。从别的一个角度下去讲,即便你没有打算把你写的代码酿成一个通用的库并分发给其别人利用,从计划的角度来说,接纳一种通盘的通用的计划办法也能让你对所要完成的义务有更好的了解,而且优化你的计划历程,从而优化你的代码布局。
接纳开辟库而且让他人来利用它的体例,可以匡助你在利用它的时分发明它的计划上的缺点大概代码中的毛病,并匡助你更正它。例如说,你写了一个库让他人来利用,你不能不思索通用的计划,由于你其实不能预感他人将在甚么情况下利用和利用的目标。在其别人利用你的库的过程当中,大概会碰到一些成绩,有的多是你的文档写得不敷分明分明,有的也多是你程序上的毛病,也有大概是利用者以为在布局上利用起来不便利大概不准确。那末你能够持续作一些修正事情,在坚持布局和接口稳定化的情形下,做一些调剂。
在计划库的时分,你必要以一个利用者的目光来看成绩,思索怎样计划和完成它。你必要分明,
1、必要办理的成绩是甚么?必要到达一个甚么目标?
2、利用者体贴的成绩是甚么?利用者必要失掉一个甚么了局?
3、利用者不必要体贴的成绩是甚么?甚么细节是能够对利用者埋没的?
上面,我们用一个复杂的例子来讲明怎样计划和完成一个有效处的库。
计划一个收集服务程序,我们必要思索几点:
1、监听一个端口
2、承受毗连
3、读取大概写进毗连的流
4、处置输出的数据,而且前往一个了局
关于我们将要完成的库来讲,必要完成的是前三点,而最初一点我们留给利用者往完成,这也是使用者必要完成和体贴的中央。
库的次要类叫做Server,测试的类叫做EchoServer.EchoServer完成了一个复杂的服务,从客户端读取数据,而且前往一样的数据。
计划准绳一:封装
一个好的库必需是一个松散的干系严密的全体,而不是一个分离的干系松懈的对象的汇合。
package是Java供应的一品种库的封装机制。一个package是一个Java类文件的汇合,寄存在统一个目录中。package有专有的名字空间。
专有的名字空间的一个优点是,你不必忧虑称号的抵触。由于,假如你的类的称号和他人的类的名称抵触,可是他们不在统一个package中,使用这一点能够制止名字的抵触。
每个package都有一个字符串来代表,好比java.lang,大概javax.swing.plaf.basic.实践上每个类的全名都是由package的名字加上类的名字来代表的,如许就制止了名字的抵触,比如,java.lang.Object大概javax.swing.plaf.basic.BasicMenuBarUI.
注重,有一个特别的package叫做defaultpackage。假如你不声明你的类属于任何一个package,那末它就被假定属于defaultpackage.
每个package的名字都对应一个目次。好比,java.lang.Object寄存在java/lang/Object.java中,每个.对应一个/.defaultpackage寄存的目次是以后目次。
声明一个package.
//Server.java
packagemylib;
publicclassServerimplementsRunnable
{
//...
假如有import语句,必需放在package语句的前面。
固然你也能够引进其余package.比方:
importmylib.Server;
//...
Serverserver=newServer(portNum);
Java同意你决意package中的哪些类对内部是可见的。public类能够被包外的代码利用,而private类则不可。
好比,让Server类能被内部的代码利用:
//Server.java
packagemylib;
importjava.io.*;
importjava.net.*;
publicclassServerimplementsRunnable
{
假如你不想让类被内部的代码利用,能够用缺省的属性,往失落public.比方:
//Reporter.java
packagemylib;
classReporterimplementsRunnable
{
计划准绳二:承继
在我们的例子中,Server是次要的类。假如你看这个类的代码,就可以看到,它自己实在甚么也不做。主轮回用来监听毗连。当毗连创建今后,它把处置毗连的义务交给一个叫做handleConnection()的函数。
//subclassmustsupplyanimplementation
abstractpublicvoidhandleConnection(Sockets);
由于没有完成这一函数,以是这个类被声明为abstract,利用者必需完成这个函数。
//ThisiscalledbytheServerclasswhenaconnection
//comesin."in"and"out"comefromtheincomingsocket
//connection
publicvoidhandleConnection(Socketsocket){
try{
InputStreamin=socket.getInputStream();
OutputStreamout=socket.getOutputStream();
//justcopytheinputtotheoutput
while(true)
out.write(in.read());
}catch(IOExceptionie){
System.out.println(ie);
}
}
能够说,这一承继的历程叫做定制。由于在Server类中,并没有界说该函数的举措,而是把这个定义的历程留给利用者,让他们来完成所必要的特定的功效。
别的一个定制函数:cleanUp().
在计划类的时分,常常你能思索到利用者必要的功效,比方下面的handleConnection().可是,也必要思索别的一种定制,比方在这里,在Server加入背景运转体例的时分,挪用了这个cleanUp()函数,在Server类中的完成为空,甚么都不做,这把时机留给利用者,利用者能够用这个函数来做一些清除事情,这类函数也能够称之为"钩子"。
计划准绳三:调试
没有人可以做到写出一个相对完善的程序,没有任何的毛病。以是,调试是不成短少的。偶然候,利用者大概会碰到一个成绩,从而必要晓得在库的代码中产生了甚么成绩。这个毛病多是库代码的成绩,也多是利用者的代码在库代码中引发的成绩。
假如你供应了库的源代码,利用者能够用debugger来调试毛病。可是,你不克不及完整依附于调试器。在库代码中到场打印调试信息的语句,是一个好习气。它能够匡助利用者分明,甚么中央产生了错误。
上面的例子申明了这一手艺。利用者的代码利用Server.setDebugStream(),指定一个PrintStream对象。然后,调试信息就被输入到这个流中。
//setthistoaprintstreamifyouwantdebuginfo
//senttoit;otherwise,leaveitnull
staticprivatePrintStreamdebugStream;
//callthistosendthedebuggingoutputsomewhere
staticpublicvoidsetDebugStream(PrintStreamps){
debugStream=ps;
}
当利用者利用了调试的流的时分,你的库代码能够打印毛病:
//senddebuginfototheprintstream,ifthereisone
staticpublicvoiddebug(Strings){
if(debugStream!=null)
debugStream.println(s);
}
上面,来完全的看一看这个详细的例子:
EchoServer
//$Id$
importjava.io.*;
importjava.net.*;
importmylib.*;
publicclassEchoServerextendsServer
{
publicEchoServer(intport){
//Thesuperclassknowswhattodowiththeportnumber,we
//donthavetocareaboutit
super(port);
}
//ThisiscalledbytheServerclasswhenaconnection
//comesin."in"and"out"comefromtheincomingsocket
//connection
publicvoidhandleConnection(Socketsocket){
try{
InputStreamin=socket.getInputStream();
OutputStreamout=socket.getOutputStream();
//justcopytheinputtotheoutput
while(true)
out.write(in.read());
}catch(IOExceptionie){
System.out.println(ie);
}
}
protectedvoidcleanUp(){
System.out.println("Cleaningup");
}
staticpublicvoidmain(Stringargs[])throwsException{
//Grabtheportnumberfromthecommand-line
intport=Integer.parseInt(args[0]);
//Havedebugginginfosenttostandarderrorstream
Server.setDebugStream(System.err);
//Createtheserver,anditsupandrunning
newEchoServer(port);
}
}
mylib.Server
//$Id$
packagemylib;
importjava.io.*;
importjava.net.*;
abstractpublicclassServerimplementsRunnable
{
//theportwellbelisteningon
privateintport;
//howmanyconnectionswevehandled
intnumConnections;
//theReporterthatsreportingonthisServer
privateReporterreporter;
//setthistotruetotellthethreadtostopaccepting
//connections
privatebooleanmustQuit=false;
publicServer(intport){
//remembertheportnumbersothethreadcan
//listenonit
this.port=port;
//theconstructorstartsabackgroundthread
newThread(this).start();
//andstartareporter
reporter=newReporter(this);
}
//thisisourbackgroundthread
publicvoidrun(){
ServerSocketss=null;
try{
//getreadytolisten
ss=newServerSocket(port);
while(!mustQuit){
//giveoutsomedebugginginfo
debug("Listeningon"+port);
//waitforanincomingconnection
Sockets=ss.accept();
//recordthatwegotanotherconnection
numConnections++;
//moredebugginginfo
debug("Gotconnectionon"+s);
//processtheconnection--thisisimplemented
//bythesubclass
handleConnection(s);
}
}catch(IOExceptionie){
debug(ie.toString());
}
debug("Shuttingdown"+ss);
cleanUp();
}
//thedefaultimplementationdoesnothing
abstractpublicvoidhandleConnection(Sockets);
//tellthethreadtostopacceptingconnections
publicvoidclose(){
mustQuit=true;
reporter.close();
}
//Putanylast-minuteclean-upstuffinhere
protectedvoidcleanUp(){
}
//everythingbelowprovidesasimpledebugsystemfor
//thispackage
//setthistoaprintstreamifyouwantdebuginfo
//senttoit;otherwise,leaveitnull
staticprivatePrintStreamdebugStream;
//wehavetwoversionsofthis...
staticpublicvoidsetDebugStream(PrintStreamps){
debugStream=ps;
}
//...justforconvenience
staticpublicvoidsetDebugStream(OutputStreamout){
debugStream=newPrintStream(out);
}
//senddebuginfototheprintstream,ifthereisone
staticpublicvoiddebug(Strings){
if(debugStream!=null)
debugStream.println(s);
}
}
mylib.Reporter
//$Id$
packagemylib;
classReporterimplementsRunnable
{
//theServerwearereportingon
privateServerserver;
//ourbackgroundthread
privateThreadthread;
//setthistotruetotellthethreadtostopaccepting
//connections
privatebooleanmustQuit=false;
Reporter(Serverserver){
this.server=server;
//createabackgroundthread
thread=newThread(this);
thread.start();
}
publicvoidrun(){
while(!mustQuit){
//dothereporting
Server.debug("serverhashad"+server.numConnections+"connections");
//thenpauseawhile
try{
Thread.sleep(5000);
}catch(InterruptedExceptionie){}
}
}
//tellthebackgroundthreadtoquit
publicvoidclose(){
mustQuit=true;
}
}
上面,我们以一个详细的有实践用途的类库来进一步会商库的计划办法。这个库是jregex.(jregex.sourceforge.net)。这是一个用Java完成的兼容Perl5.6的正则表达式的库。有良多如许的库,好比gnu.regexp,com.stevesoft.pat,另有J2SESDK1.4中新增添的regex.为何选用jregex呢?是由于它是今朝源代码公然的regex库中兼容Perl5.6的正则表达式,并且方才更新过源代码,而且是不乱版,别的一个就是,它的内核算法选用了NFA(NotFiniteAutomata)。
要懂得一个包,在看源代码之前先应当看的就是它的API文档。那末,看文档的第一步应当看甚么呢?固然是树形布局。
ClassHierarchy
classjava.lang.Object
classjregex.Matcher(implementsjregex.MatchResult)
classjregex.Optimizer
classjregex.util.io.PathPattern
classjregex.Pattern(implementsjregex.REFlags,java.io.Serializable)
classjregex.PerlSubstitution(implementsjregex.Substitution)
classjregex.Replacer
classjregex.RETokenizer(implementsjava.util.Enumeration)
classjava.lang.Throwable(implementsjava.io.Serializable)
classjava.lang.Exception
classjava.lang.RuntimeException
classjava.lang.IllegalArgumentException
classjregex.PatternSyntaxException
classjregex.util.io.WildcardFilter(implementsjava.io.FilenameFilter)
InterfaceHierarchy
interfacejregex.MatchIterator
interfacejregex.MatchResult
interfacejregex.REFlags
interfacejregex.Substitution
interfacejregex.TextBuffer
interfacejregex.Replacer.WriterWrap
--------------------------------------------------------------------------------
在一个正则表达式中,我们晓得有两个元素很主要,第一个就是Pattern(形式),第二个是Matcher(匹配了局字符串)。在jregex中,Pattern类完成了jregex.REFlagsinterface,和java.io.Serializable。先来看看jregex.REFlags的申明。jregex.REFlags界说了一些静态的常量,看起来是一些标记。Pattern完成了jregex.REFlags,也就是说,Pattern类中包括了这些静态的常量。
下一步,我们看看Pattern的API申明:
Pattern是一个预编译好的正则表达式的暗示。要婚配一个正则表达式,先创立一个Pattern实例:
Patternp=newPattern(myExpr);
然后获得Matcher的实例
Matchermatcher=p.matcher(myText);
Matcher的实例是一个主动的婚配和搜刮的对象。它供应以下办法:
搜刮婚配了局:matcher.find()ormatcher.findAll();
监测是不是全文婚配:matcher.matches();
监测是不是婚配开首:matcher.isStart();
带选项的查找:matcher.find(intoptions)
标记
标记(参考REFlags)改动了在预编译的时分正则表达式标记的意义。这些标记是:
REFlags.IGNORE_CASE-疏忽巨细写
REFlags.MULTILINE-用^和$来暗示一行文本的开首和开头
REFlags.DOTALL-用.来暗示回车换行
REFlags.IGNORE_SPACES-疏忽空格
REFlags.UNICODE-利用UNICODE,即w,d不再被注释为正则表达式的意义,而是被注释为UNICODE.
REFlags.XML_SCHEMA-利用XML语义。
线程
Pattern是线程平安的。也就是说,你能够在分歧的线程中利用统一个Pattern的实例。
在API函数申明中,我们还能看到Pattern类的public办法。这一点将鄙人面有效处。先来看看:
机关函数
Pattern(java.lang.Stringregex)
Compilesanexpressionwithdefaultflags.
Pattern(java.lang.Stringregex,intflags)
CompilesaregularexpressionusingREFlags.
Pattern(java.lang.Stringregex,java.lang.Stringflags)
CompilesaregularexpressionusingPerl5-styleflags.
办法
intgroupCount()
Howmanycapturinggroupsthisexpressionincludes?
java.lang.IntegergroupId(java.lang.Stringname)
Getnumericidforagroupname.
Matchermatcher()
Returnsatargetlessmatcher.
Matchermatcher(char[]data,intstart,intend)
Returnsamatcherforaspecifiedregion.
Matchermatcher(MatchResultres,intgroupId)
Returnsamatcherforamatchresult(inaperformance-friendlyway).
Matchermatcher(MatchResultres,java.lang.StringgroupName)
Justasabove,yetwithsymbolicgroupname.
Matchermatcher(java.io.Readertext,intlength)
Returnsamatchertakingatextstreamastarget.
Matchermatcher(java.lang.Strings)
Returnsamatcherforaspecifiedstring.
Replacerreplacer(java.lang.Stringexpr)
Returnsareplacerofapatternbyspecifiedperl-likeexpression.
Replacerreplacer(Substitutionmodel)
Returnsareplacerwillsubstitutealloccurencesofapatternthroughapplyingauser-definedsubstitutionmodel.
RETokenizertokenizer(char[]data,intoff,intlen)
Tokenizesaspecifiedregionbyanoccurencesofthepattern.
RETokenizertokenizer(java.io.Readerin,intlength)
Tokenizesaspecifiedregionbyanoccurencesofthepattern.
RETokenizertokenizer(java.lang.Stringtext)
Tokenizesatextbyanoccurencesofthepattern.
java.lang.StringtoString_d()
Returnsalessormorereadablerepresentationofabytecodeforthepattern.
java.lang.StringtoString()
接上去,我们来看看Pattern类的内容。这里有两种办法,一种是间接浏览源代码,别的一种是先用工具剖析一下Pattern类的内容。这里,我接纳第二种办法,用javap来看类的内容。
[games]$javap-classpath..-privatejregex.Pattern
Compiledfromjregex/Pattern.java
publicclassjregex.Patternextendsjava.lang.Objectimplementsjava.io.Serializable,jregex.REFlags{
java.lang.StringstringRepr;
jregex.Termroot;
jregex.Termroot0;
intmemregs;
intcounters;
intlookaheads;
java.util.HashtablenamedGroupMap;
privatejregex.Pattern()throwsjregex.PatternSyntaxException;
publicjregex.Pattern(java.lang.String)throwsjregex.PatternSyntaxException;
publicjregex.Pattern(java.lang.String,java.lang.String)throwsjregex.PatternSyntaxException;
publicjregex.Pattern(java.lang.String,int)throwsjregex.PatternSyntaxException;
privatevoidcompile(java.lang.String,int)throwsjregex.PatternSyntaxException;
publicintgroupCount();
publicjava.lang.IntegergroupId(java.lang.String);
publicjregex.Matchermatcher();
publicjregex.Matchermatcher(java.lang.String);
publicjregex.Matchermatcher(char[],int,int);
publicjregex.Matchermatcher(jregex.MatchResult,int);
publicjregex.Matchermatcher(jregex.MatchResult,java.lang.String);
publicjregex.Matchermatcher(java.io.Reader,int)throwsjava.io.IOException;
publicjregex.Replacerreplacer(java.lang.String);
publicjregex.Replacerreplacer(jregex.Substitution);
publicjregex.RETokenizertokenizer(java.lang.String);
publicjregex.RETokenizertokenizer(char[],int,int);
publicjregex.RETokenizertokenizer(java.io.Reader,int)throwsjava.io.IOException;
publicjava.lang.StringtoString();
publicjava.lang.StringtoString_d();
staticintparseFlags(java.lang.String)throwsjregex.PatternSyntaxException;
staticintparseFlags(char[],int,int)throwsjregex.PatternSyntaxException;
privatestaticintgetFlag(char)throwsjregex.PatternSyntaxException;
}
个中,要体贴private和protected成员,由于在利用类的时分,我们只需体贴public成员就好了,但是,要浏览源代码,分明类的组成,就必需注重private和protected成员。
privatePattern()throwsPatternSyntaxException{}
publicPattern(Stringregex)throwsPatternSyntaxException{
this(regex,DEFAULT);
}
publicPattern(Stringregex,Stringflags)throwsPatternSyntaxException{
stringRepr=regex;
compile(regex,parseFlags(flags));
}
publicPattern(Stringregex,intflags)throwsPatternSyntaxException{
stringRepr=regex;
compile(regex,flags);
}
能够看出,机关函数中,有一个缺省的机关函数是private。而第二个挪用了最初一个机关函数,用this()。第三个和最初一个都是用了一个函数compile来完成机关正则表达式的义务。在下面javap的输入我们也能够看到,compile是一个private函数。
来看看它的申明:
privatevoidcompile(Stringregex,intflags)throwsPatternSyntaxException{
Term.makeTree(regex,flags,this);
}
详细到Term类,超越了本文的局限,它接纳了hashtable等办法,机关了一个正则表达式,并前往。而我们体贴的是库的布局。从这里我们能够分明一点:机关函数常常必要挪用别的一个机关函数来完成,而不必要把一样的代码在各个机关函数中都完成。同时,也能够接纳别的一个private函数来完成机关函数的功效,而只需在机关函数中挪用它就好了。
从MatcherAPI申明文档中,我们能够看到,有两种经由过程Pattern实例机关Matcher实例的办法,而在javap的输入中能够看到,有几种分歧的matcher函数。
publicMatchermatcher(){
returnnewMatcher(this);
}
publicMatchermatcher(Strings){
Matcherm=newMatcher(this);
m.setTarget(s);
returnm;
}
publicMatchermatcher(char[]data,intstart,intend){
Matcherm=newMatcher(this);
m.setTarget(data,start,end);
returnm;
}
publicMatchermatcher(MatchResultres,intgroupId){
Matcherm=newMatcher(this);
if(resinstanceofMatcher){
m.setTarget((Matcher)res,groupId);
}
else{
m.setTarget(res.targetChars(),res.start(groupId)+res.targetStart(),res.length(groupId));
}
returnm;
}
publicMatchermatcher(Readertext,intlength)throwsIOException{
Matcherm=newMatcher(this);
m.setTarget(text,length);
returnm;
}
以上都是用来完成第一种前往matcher的办法。能够看到,这几种完成都是经由过程newMatcher实例,以this(即Pattern实例)为参数,然后依据参数分歧挪用Matcher.setTarget()办法。
publicMatchermatcher(MatchResultres,StringgroupName){
Integerid=res.pattern().groupId(groupName);
if(id==null)thrownewIllegalArgumentException("groupnotfound:"+groupName);
intgroup=id.intValue();
returnmatcher(res,group);
}
这是第二种前往matcher的办法。
从这里我们能够发明一种对照好的办法:当你必要两个相互联系关系的类,一个类的实例必要机关另外一个类的实例,能够在第一个类顶用一个public办法,办法完成为挪用第二个类的机关函数,并能够用机关出来的实例挪用其他办法,依据第一个类的实例的数据来设置第二个类的实例。
一样的,也有Replacer,和Tokenizer的机关办法。
publicReplacerreplacer(Stringexpr){
returnnewReplacer(this,expr);
}
/**
*Returnsareplacerwillsubstitutealloccurencesofapattern
*throughapplyingauser-definedsubstitutionmodel.
*@parammodelaSubstitutionobjectwhichisinchargeformatchsubstitution
*@seeReplacer
*/
publicReplacerreplacer(Substitutionmodel){
returnnewReplacer(this,model);
}
publicRETokenizertokenizer(Stringtext){
returnnewRETokenizer(this,text);
}
/**
*Tokenizesaspecifiedregionbyanoccurencesofthepattern.
*Notethataseriesofadjacentmatchesareregardedasasingleseparator.
*ThesameasnewRETokenizer(Pattern,char[],int,int);
*@seeRETokenizer
*@seeRETokenizer#RETokenizer(jregex.Pattern,char[],int,int)
*/
publicRETokenizertokenizer(char[]data,intoff,intlen){
returnnewRETokenizer(this,data,off,len);
}
/**
*Tokenizesaspecifiedregionbyanoccurencesofthepattern.
*Notethataseriesofadjacentmatchesareregardedasasingleseparator.
*ThesameasnewRETokenizer(Pattern,Reader,int);
*@seeRETokenizer
*@seeRETokenizer#RETokenizer(jregex.Pattern,java.io.Reader,int)
*/
publicRETokenizertokenizer(Readerin,intlength)throwsIOException{
returnnewRETokenizer(this,in,length);
}
回想一下,我在本文的开首,已经提到过:"一个好的库必需是一个松散的干系严密的全体,而不是一个分离的干系松懈的对象的汇合。"从API申明文档所显现的这个库的树形布局,其实不能看出这些类之间的接洽。而从源代码的角度,我们则能够分明地看到这一点。在这一部分的会商中,我们也分明了两点:
1、怎样编写重载机关函数
2、在一个类的实例中前往别的一个类的实例
接上去,看看Matcher类。这个类完成了MatchResultinterface.看看MatchResult的界说:
[games]$javap-classpath..-sjregex.MatchResult
Compiledfromjregex/MatchResult.java
publicinterfacejregex.MatchResult
/*ACC_SUPERbitNOTset*/
{
publicstaticfinalintMATCH;
/*I*/
publicstaticfinalintPREFIX;
/*I*/
publicstaticfinalintSUFFIX;
/*I*/
publicstaticfinalintTARGET;
/*I*/
publicabstractjregex.Patternpattern();
/*()Ljregex/Pattern;*/
publicabstractintgroupCount();
/*()I*/
publicabstractbooleanisCaptured();
/*()Z*/
publicabstractbooleanisCaptured(int);
/*(I)Z*/
publicabstractbooleanisCaptured(java.lang.String);
/*(Ljava/lang/String;)Z*/
publicabstractjava.lang.Stringgroup(int);
/*(I)Ljava/lang/String;*/
publicabstractbooleangetGroup(int,java.lang.StringBuffer);
/*(ILjava/lang/StringBuffer;)Z*/
publicabstractbooleangetGroup(int,jregex.TextBuffer);
/*(ILjregex/TextBuffer;)Z*/
publicabstractjava.lang.Stringgroup(java.lang.String);
/*(Ljava/lang/String;)Ljava/lang/String;*/
publicabstractbooleangetGroup(java.lang.String,java.lang.StringBuffer);
/*(Ljava/lang/String;Ljava/lang/StringBuffer;)Z*/
publicabstractbooleangetGroup(java.lang.String,jregex.TextBuffer);
/*(Ljava/lang/String;Ljregex/TextBuffer;)Z*/
publicabstractjava.lang.Stringprefix();
/*()Ljava/lang/String;*/
publicabstractjava.lang.Stringsuffix();
/*()Ljava/lang/String;*/
publicabstractjava.lang.Stringtarget();
/*()Ljava/lang/String;*/
publicabstractinttargetStart();
/*()I*/
publicabstractinttargetEnd();
/*()I*/
publicabstractchartargetChars()[];
/*()[C*/
publicabstractintstart();
/*()I*/
publicabstractintend();
/*()I*/
publicabstractintlength();
/*()I*/
publicabstractintstart(int);
/*(I)I*/
publicabstractintend(int);
/*(I)I*/
publicabstractintlength(int);
/*(I)I*/
publicabstractcharcharAt(int);
/*(I)C*/
publicabstractcharcharAt(int,int);
/*(II)C*/
}
jregex.MatchResult界说了一些abstract函数。有甚么感化?在前面我们将会会商到。
再看看Matcher的完成。
[games]$javap-classpath..-sjregex.Matcher
Compiledfromjregex/Matcher.java
publicclassjregex.Matcherextendsjava.lang.Objectimplementsjregex.MatchResult{
publicstaticfinalintANCHOR_START;
/*I*/
publicstaticfinalintANCHOR_LASTMATCH;
/*I*/
publicstaticfinalintANCHOR_END;
/*I*/
publicstaticfinalintACCEPT_INCOMPLETE;
/*I*/
jregex.Matcher(jregex.Pattern);
/*(Ljregex/Pattern;)V*/
publicfinalvoidsetTarget(jregex.Matcher,int);
/*(Ljregex/Matcher;I)V*/
publicvoidsetTarget(java.lang.String);
/*(Ljava/lang/String;)V*/
publicvoidsetTarget(java.lang.String,int,int);
/*(Ljava/lang/String;II)V*/
publicvoidsetTarget(char[],int,int);
/*([CII)V*/
publicfinalvoidsetTarget(char[],int,int,boolean);
/*([CIIZ)V*/
publicvoidsetTarget(java.io.Reader,int)throwsjava.io.IOException;
/*(Ljava/io/Reader;I)V*/
publicfinalbooleanisStart();
/*()Z*/
publicfinalbooleanmatches();
/*()Z*/
publicfinalbooleanmatches(java.lang.String);
/*(Ljava/lang/String;)Z*/
publicvoidsetPosition(int);
/*(I)V*/
publicfinalbooleanfind();
/*()Z*/
publicfinalbooleanfind(int);
/*(I)Z*/
publicjregex.MatchIteratorfindAll();
/*()Ljregex/MatchIterator;*/
publicjregex.MatchIteratorfindAll(int);
/*(I)Ljregex/MatchIterator;*/
publicfinalbooleanproceed();
/*()Z*/
publicfinalbooleanproceed(int);
/*(I)Z*/
publicfinalvoidskip();
/*()V*/
publicjava.lang.StringtoString();
/*()Ljava/lang/String;*/
publicjregex.Patternpattern();
/*()Ljregex/Pattern;*/
publicjava.lang.Stringtarget();
/*()Ljava/lang/String;*/
publicchartargetChars()[];
/*()[C*/
publicinttargetStart();
/*()I*/
publicinttargetEnd();
/*()I*/
publiccharcharAt(int);
/*(I)C*/
publiccharcharAt(int,int);
/*(II)C*/
publicfinalintlength();
/*()I*/
publicfinalintstart();
/*()I*/
publicfinalintend();
/*()I*/
publicjava.lang.Stringprefix();
/*()Ljava/lang/String;*/
publicjava.lang.Stringsuffix();
/*()Ljava/lang/String;*/
publicintgroupCount();
/*()I*/
publicjava.lang.Stringgroup(int);
/*(I)Ljava/lang/String;*/
publicjava.lang.Stringgroup(java.lang.String);
/*(Ljava/lang/String;)Ljava/lang/String;*/
publicbooleangetGroup(int,jregex.TextBuffer);
/*(ILjregex/TextBuffer;)Z*/
publicbooleangetGroup(java.lang.String,jregex.TextBuffer);
/*(Ljava/lang/String;Ljregex/TextBuffer;)Z*/
publicbooleangetGroup(int,java.lang.StringBuffer);
/*(ILjava/lang/StringBuffer;)Z*/
publicbooleangetGroup(java.lang.String,java.lang.StringBuffer);
/*(Ljava/lang/String;Ljava/lang/StringBuffer;)Z*/
publicjava.lang.Stringgroups()[];
/*()[Ljava/lang/String;*/
publicjava.util.Vectorgroupv();
/*()Ljava/util/Vector;*/
publicfinalbooleanisCaptured();
/*()Z*/
publicfinalbooleanisCaptured(int);
/*(I)Z*/
publicfinalbooleanisCaptured(java.lang.String);
/*(Ljava/lang/String;)Z*/
publicfinalintlength(int);
/*(I)I*/
publicfinalintstart(int);
/*(I)I*/
publicfinalintend(int);
/*(I)I*/
publicjava.lang.StringtoString_d();
/*()Ljava/lang/String;*/
static{};
/*()V*/
先来看看它的机关函数,这个函数在Pattern中被挪用用来机关Matcher类的实例。
Matcher(Patternregex){
//注重上面这一行,它申明Matcher类的外部有一个指向Pattern实例的reference.
this.re=regex;
//intmemregCount=(memregs=newMemReg[regex.memregs]).length;
//for(inti=0;i<memregCount;i++){
//this.memregs[i]=newMemReg(-1);//unlikelytoSearchEntry,inthiscaseweknowmemregindiciesbyd
efinition
//}
//counters=newint[regex.counters];
//intlookaheadCount=(lookaheads=newLAEntry[regex.lookaheads]).length;
//for(inti=0;i<lookaheadCount;i++){
//this.lookaheads[i]=newLAEntry();
//}
//界说了一些外部的数据,MemReg是一个有三个整数的类。类的声明见下。
intmemregCount,counterCount,lookaheadCount;
if((memregCount=regex.memregs)>0){
MemReg[]memregs=newMemReg[memregCount];
for(inti=0;i<memregCount;i++){
memregs[i]=newMemReg(-1);//unlikelytoSearchEntry,inthiscaseweknowmemregindiciesbydefin
ition
}
this.memregs=memregs;
}
if((counterCount=regex.counters)>0)counters=newint[counterCount];
//界说了一些外部的数据。类的声明见下。
if((lookaheadCount=regex.lookaheads)>0){
LAEntry[]lookaheads=newLAEntry[lookaheadCount];
for(inti=0;i<lookaheadCount;i++){
lookaheads[i]=newLAEntry();
}
this.lookaheads=lookaheads;
}
this.memregCount=memregCount;
this.counterCount=counterCount;
this.lookaheadCount=lookaheadCount;
first=newSearchEntry(memregCount,counterCount);
defaultEntry=newSearchEntry(memregCount,counterCount);
minQueueLength=regex.stringRepr.length()/2;//evaluation!!!
}
把这两个类申明为default属性,申明这两个类在jregex这个包的外部可见,而在内部是不成见的。这两个类的感化是专有的,而不是通用的。回想一下本文后面提到过的,包的封装,"假如你不想让类被内部的代码利用,能够用缺省的属性,往失落public."
classMemReg{
intindex;
intin=-1,out=-1;
inttmp=-1;//forassumingatGROUP_IN
MemReg(intindex){
this.index=index;
}
voidreset(){
in=out=-1;
}
}
classLAEntry{
intindex;
SearchEntrytop,actual;
}
别的,关于包的布局和包中的类的干系,我们感乐趣的另有,MatchIterator。
[games]$javap-classpath..-sjregex.MatchIterator
Compiledfromjregex/MatchIterator.java
publicinterfacejregex.MatchIterator
/*ACC_SUPERbitNOTset*/
{
publicabstractbooleanhasMore();
/*()Z*/
publicabstractjregex.MatchResultnextMatch();
/*()Ljregex/MatchResult;*/
publicabstractintcount();
/*()I*/
}
这是一个interface,界说了iterator的经常使用办法,列出一切的MatchResult的实例。
从这里能够看出,界说一个MatchResultinterface然后再用Matcher来完成这个interface是一种对照好的办法。由于,如许界说了对照好的条理布局,关于前面的扩大,程序的更新开展都有对照年夜的好处。在这里也能够看出,MatchIterator的办法前往的也是MatchResultinterface,而不是Matcher类。同时能够看到MatchIterator自己也是一个interface。这就表现了别的一个我们必要分明的主要成绩:对interface编程,而不是对类编程。详细说来,假如在今后从头写了一个新的类,叫做AnotherMatcher,一样也是完成MatchResultinterface,在AnotherMatcher中一样可使用办法来前往一个MatchIterator的实例,它大概是Matcher类的实例,也能够是AnotherMatcher类的实例。可是,虽然我们作了良多修改,可是没有影响到之前的程序,不必要对之前的程序作任何的修改。从利用者的角度来看,假如他只是利用了MatchResult和MatchIterator这两个interfaces中界说的办法,那末,不管是用Matcher仍是AnotherMatcher,他都不必要修正他的代码。
下一个必要看的函数是findAll().
//挪用了带参数的重载函数findAll(int)
publicMatchIteratorfindAll(){
returnfindAll(0);
}
/**
*Returnsaniteratoroverthematchesfoundbysubsequentlycallingfind(options),thesearchstartsfromt
hezeroposition.
*/
publicMatchIteratorfindAll(finalintoptions){
//setPosition(0);
//不必体贴详细的完成办法,可是我们能够看到,在这里它界说了一个anonymousinnerclass,这个innerclass完成了MatchIterator,用于把Matcher中婚配到的了局一个个的前往。这个innerclass的对象实例的reference被findAll()办法前往。可以用于内部的一个reference,并能读出这些了局。这也是一种必要注重的办法。
returnnewMatchIterator(){
privatebooleanchecked=false;
privatebooleanhasMore=false;
publicbooleanhasMore(){
if(!checked)check();
returnhasMore;
}
publicMatchResultnextMatch(){
if(!checked)check();
if(!hasMore)thrownewNoSuchElementException();
checked=false;
returnMatcher.this;
}
privatefinalvoidcheck(){
hasMore=find(options);
checked=true;
}
publicintcount(){
if(!checked)check();
if(!hasMore)return0;
intc=1;
while(find(options))c++;
checked=false;
returnc;
}
};
}
在下面这段代码中,我们分明了以下3点:
1、对interface编程,而不是对类编程。
2、怎样编写一个iterator(迭代器)。必要有最少的两个函数:hasMore(),NextElement()。如许,利用者能够用以下轮回读取迭代器中的内容:
for(;iterator.hasMore();)
iterator.NextElement();
3、把处置办法和处置了局分隔。用Matcher来暗示婚配的办法的详细完成,而把了局放在别的一个类中。如许做的优点是处置办法和了局分隔,加倍的天真能够恣意的变动处置的办法,而了局类不遭到影响。也能够变动了局类的读取体例,可是不影响处置办法类的完成。这类办法已被J2SESDK中的良多汇合类所利用。也能够参考它们的完成办法。
固然,另有一点必要注重的就是利用了匿名的innerclass办法。
jregex中能够会商的成绩另有良多,好比它所接纳的NotFiniteAutomata办法,它所接纳的正则表达式标准等等。可是,作为本文所会商的局限,即怎样计划和完成一个库,已能够展现它的计划方法了。有乐趣的读者能够到jregex.sourceforge.net下载它的源代码包,二进制文件包,利用典范和文档申明做进一步的研讨。
小结:
在本文中,我们会商了计划和完成一个Java库的多少准绳,并用一个复杂的例子来讲了然这些原则。随后,我们用一个实践使用局限很广的库来会商了其他的一些必要注重的成绩。
计划准绳一:封装
一个好的库必需是一个松散的干系严密的全体,而不是一个分离的干系松懈的对象的汇合。
计划准绳二:承继
接纳abstract函数,interface,和"钩子"函数。
计划准绳三:调试
在库代码中到场打印调试信息的语句。
和一些计划办法:
1、怎样编写重载机关函数。
2、在一个类的实例中前往别的一个类的实例
3、对interface编程,而不是对类编程。
4、怎样编写一个iterator(迭代器)。
5、把处置办法和处置了局分隔。
6、怎样利用innerclass.
同时,我们也会商了浏览一个Java库源代码的办法:
1、浏览API文档申明,库的树形布局,分明类,interface的干系。
2、用javap来列出类的函数申明,变量,interface中界说的函数,常量等等。
3、注重private,protected函数。
前些天,在CSDN上看到了一个消息,说是ASP.NETAJAX成功在Linux上运行,这一点对我触动很大,而且引发了我许多感叹,所以想写出来分享一下。 |
|