Cavingdeep的.NET专栏

软件开发与工程,又一种艺术。
随笔 - 8, 评论 - 83, 引用 - 5

导航

关于

逆水行舟,不进则退!

业精于勤,荒于嘻
行成于思,毁于随



月 [下月] [上月]


标签

每月存档

最新留言

  • 回复: 使用Subversion做.NET项目
    项目中单独的文件权限好像不能控制吧, 不知道可以控制目录不?
    by Daniel(匿名) on 2007/7/24 20:54:00
  • re: 使用Subversion做.NET项目
    请问怎样控制一个repository中不同文件的权限?
    by xmpp(匿名) on 2006/9/9 23:53:00
  • DotNet 网上资源
    相关网站列表http://blog.joycode.com/博客堂主要是微软专家的帖子,内容较新http://www.cnblogs.com/博客园 http://www.microsoft....
    by ldz(匿名) on 2006/8/23 15:34:00
  • re: 使用Subversion做.NET项目
    单位只允许80端口,而同一个服务器上有两个web服务器IIS and Apache。IIS跑asp.net & asp ,Apache 跑svn。怎么解决这个端口问题啊?
    by Daniel(匿名) on 2006/8/21 19:01:00
  • DotNet 网上相关资源
    <br>DotNet 网上相关资源 <br> <br>相关网站列表 http://blog.joycode.com/ 博客堂 主要是微软专家的帖子,内容较新 ht...
    by Killkill's Blog(匿名) on 2006/5/5 14:33:00
  • DotNet资源
    DotNet相关网站列表
    by OX(匿名) on 2006/4/29 14:03:00
  • re: 又一个语言识别工具(ANTLR)
    今后不用再想写编译器了我……
    by S.F.(匿名) on 2005/4/22 20:10:00
  • re: 用DCG做单元测试报表
    呵呵,你的DCG又想到新的应用了:)
    by Laser.NET(匿名) on 2005/4/19 19:53:00
  • re: DCG添加简单调试功能并开源
    多谢sa指点!^_^
    by Cavingdeep(匿名) on 2005/4/18 8:16:00
  • re: DCG添加简单调试功能并开源
    文件压缩格式最好用zip,因为rar格式是商业格式,再opensource时要注意这点:)
    by sa(匿名) on 2005/4/17 10:28:00
  • re: AOP在.NET中的现实
    to 衰人, <br> <br>代理模式的用意之一就是要能够替代(代理)一个对象,那么如果不从这个对象继承下来,如何代理它呢?我明白你说的意思,你说的是以实现一个行为接口,通过...
    by Cavingdeep(匿名) on 2005/4/16 16:17:00
  • re: 又一个语言识别工具(ANTLR)
    等我学会了西班牙语后,我都火化了!...
    by csharphack(匿名) on 2005/4/15 18:23:00
  • re: 又一个语言识别工具(ANTLR)
    呵呵,还是懂多国语言的好啊~~牛!! <br>西班牙语我就别指望了:)呵呵,还是以后要用的时候再问你吧:)
    by Laser.NET(匿名) on 2005/4/13 14:24:00
  • re: 又一个语言识别工具(ANTLR)
    这个语法看起来的确很复杂,其实从根本来说,ANTLR就是一个生成器,只不过它只生成识别语言的源代码,道理与我的DCG类似,我的采用ASP的写法就足够了,不过ANTLR要自己有一个写法才可以(也就是它的...
    by Cavingdeep(匿名) on 2005/4/12 17:42:00
  • re: 又一个语言识别工具(ANTLR)
    语法有点看不懂,呵呵,有没有语法介绍啊? <br>看来cavingdeep的编译原理的基本功很扎实嘛!!赞一下:)
    by Laser.NET(匿名) on 2005/4/12 16:45:00

广告

2005年04月29日

想像如果我们可以用设计Web页面的方式去设计Windows(Linux、Unix,任何你可以想像到的图形化操作系统)界面那会是什么样子的?我们将可以统一界面设计,我们可以将可以跨平台设计界面。

我们都知道Web就是这样做的,为了让全世界的人能够在不同的地方不同的操作系统下看到同样的界面,W3C推出了一系列的标准:HTML、CSS、DOM等等,如果一个Web浏览器完全符合标准的话那么我们在世界的每一个角落看到的网页将都是相同的。同样Java下的Swing也有同样的目标,为不同的平台提供同样的界面与设计方式。但是它们都有一个很大的缺点(或不足),那就是它们的设计方式还不够好,没有将界面设计的要领规划分类、使界面的设计成为一种很漫长的过程。什么意思呢?让我们来看看,做界面的需求不外乎以下几大类:

  1. 元素:界面上可以有的界面元素,如Button、TextBox、图片、文字等。
  2. 排版:界面元素的位置以及元素间位置的关系。
  3. 样式:元素的大小、颜色等装饰性的特性。
  4. 文化:多国语言、语言间差异性的处理等。
  5. 文件模型:将界面通过一个模型来展现,通过文件模型与UI脚本可以动态地定义界面。
  6. UI脚本:用来控制文件模型好实现一些动态效果,如动画等。

现在的问题是HTML、Swing、System.Windows.Forms等(对Avalon不是很熟悉,略微看了一下,不是特别强)都没有将这几种需求分开处理,而是混合在了一起,这样设计出来的界面就很死板,再想改界面的时候就会发现很麻烦。但是注意现在W3C已经开始推荐XHTML+CSS来实现界面设计了,也就是说,它将元素与样式分隔开了,但是排版与元素还没有完全分隔开,还是需要DIV元素来做排版,而且由于XHTML的性质没有做到文化处理。

现在想像一下,如果我们可以使用一种类似XHTML 1.1的基于XML的语言来定义界面元素,用类似CSS的语言来定义样式,用自创的方便排版的语言来定义排版信息,再用资源文件+自定义文化语言来定义文化的话我们能实现什么效果呢?答案当然是所有我们上面所谈过的了,统一的、跨平台的、支持多国语言的、动态的、样式可替换的(不仅仅是皮肤的功能)并且可多次转换(因为基于XML)的界面定义。Cool, no?

当然这些都只是一些构想,实现它们需要相当的时间,而且也需要一定推广,不过我相信有朝一日当W3C等标准化组织推出相关标准后这一切就都会变成可能了。目前,我们还是可以利用一部分概念小面积的改善我们项目中的UI设计体验!

posted on 2005-04-29 14:18:00 by cavingdeep  评论(2) 阅读(5245)

 
2005年04月15日

最近因为工作需要写了个将NUnit的XML结果输出转成报表的动态模板,我制作的样式虽然不太好看不过倒瞒实用的,有兴趣的朋友也可以去下载DCG来生成自己的报表哦!

下面是报表的示例。

D:\Visual Studio Projects\PWF-Framework\Framework\bin\Debug-UnitTests\PWF.Framework.exe

用例数量 27
失败个数 0
没运行个数 1
日期 2005-4-15 9:38
准备人 Seth Yuan

ConditionSyntaxTest

描述

测试条件表达式的语法

结果

成功

所用时间

0.15625

描述

已运行

结果

所用时间

一个有变量的表达式
$fe > 3 and faLse <> true

成功

0.063

一个Not变量表达式
not $_fe

成功

0.000

有括号的复杂一点的表达式
((true and false = false or false = not true)>= false) < true

成功

0.016

更为复杂的表达式
(("a"<>"A")>=("123"<"456") or (123>345)) and (($_v=true) or (1<=2)) and not (false)

成功

0.000

多重括号表达式
not (true and (false or (true and (false or (true and (false or not ($_v<>true)))))))

成功

0.016

FlowCompilerTest

描述

测试流程编译器的正确性。

结果

成功

所用时间

1.65625

描述

已运行

结果

所用时间

测试对流程定义对象的代码生成,生成期间无异常便通过。

成功

0.906

模拟一个请假流程的定义,然后试图编译,看是否编译出错,不出错既是通过。

成功

0.719

FlowDefinitionTest

描述

测试流程定义对象的准确性

结果

成功

所用时间

1.375

描述

已运行

结果

所用时间

测试签核节点的At属性为空时的处理,应抛出异常。

成功

0.000

测试条件逻辑块对象的ConditionExpression属性为空时的处理,应抛出异常。

成功

0.000

测试签核条件对象的ConditionExpression属性为空时的处理,应抛出异常。

成功

0.000

测试ExternalProgramCallLB对象的ExePath属性为空时的处理,应抛出异常。

成功

0.016

测试流程定义对象的序列化

成功

1.063

又一个更复杂的序列化测试

成功

0.016

测试流程定义对象名字有非法字符串时的处理(空格),应抛出异常。

成功

0.000

测试流程定义对象名字有非法字符串时的处理(特殊字符),应抛出异常。

成功

0.016

测试流程定义对象为空时的处理,应抛出异常。

成功

0.000

测试流程节点对象的条件属性为空时的处理,应抛出异常。

成功

0.000

测试流程节点对象的唯一名字为空时的处理,应抛出异常。

成功

0.000

测试FlowPropertyChangeLB对象的PropName属性为空时的处理,应抛出异常。

成功

0.016

测试FlowPropertyChangeLB对象的PropValue属性为空时的处理,应抛出异常。

成功

0.000

测试FlowStateChangeLB对象的Description属性为空时的处理,应抛出异常。

成功

0.016

测试流程定义对象的唯一名字为空时的处理,应抛出异常。

成功

0.016

测试JumpToNodeLB对象的Node属性为空时的处理,应抛出异常。

成功

0.000

测试JumpToNodeLB对象的PrevNode属性为空时的处理,应抛出异常。

成功

0.000

测试Param对象的Value属性为空时的处理,应抛出异常。

成功

0.000

测试流程属性对象的初始值为空时的处理,应抛出异常。

成功

0.000

测试流程属性对象的名字为空时的处理,应抛出异常。

成功

0.000

测试流程属性对象的类型为空时的处理,应抛出异常。

下面是相关的两个模板文件的内容:

UnitTestReport.dt

<%@ Template Name="" Language="C#" %>
<%@ Assembly Location="system.xml.dll" %>
<%@ Import Namespace="System.Xml" %>
<%@ Parameter Name="title" DataType="String" %>
<%@ Parameter Name="preparedBy" DataType="String" %>
<%@ Parameter Name="reportFileName" DataType="String" %>

<%
if (reportFileName == null || reportFileName.Length == 0) {
   throw new ArgumentNullException("reportFileName");
}

XmlDocument doc = new XmlDocument();
doc.Load(reportFileName);
%>
<html>
   <head>
      <title><%=title%></title>
   </head>
   <body>
      <TABLE cellSpacing="0" cellPadding="1" width="90%" align="center" border="0">
        <!--DWLayoutTable-->
         <TR>
            <TD height="50" colspan="2" valign="top">
            <P align="center"><FONT color="navy" size="5" face="黑体"><STRONG><%=doc.DocumentElement.Attributes["name"].Value%></STRONG></FONT></P></TD>
         </TR>
         <TR bgcolor="#FFFF99">
           <TD width="97"><font size="2"><strong>用例数量</strong></font></TD>
           <TD width="538" bgcolor="#FFFFCC"><font size="2"><%=doc.DocumentElement.Attributes["total"].Value%></font></TD>
         </TR>
         <TR bgcolor="#FFFF99">
           <TD><font size="2"><strong>失败个数</strong></font></TD>
           <TD bgcolor="#FFFFCC"><font color="#FF0000" size="2"><%=doc.DocumentElement.Attributes["failures"].Value%></font></TD>
         </TR>
         <TR bgcolor="#FFFF99">
           <TD><font size="2"><strong>没运行个数</strong></font></TD>
           <TD bgcolor="#FFFFCC"><font color="#FF9900" size="2"><%=doc.DocumentElement.Attributes["not-run"].Value%></font></TD>
         </TR>
         <TR bgcolor="#FFFF99">
           <TD><font size="2"><strong>日期</strong></font></TD>
           <TD bgcolor="#FFFFCC"><font size="2"><%=doc.DocumentElement.Attributes["date"].Value + " " + doc.DocumentElement.Attributes["time"].Value%></font></TD>
         </TR>
         <TR bgcolor="#FFFF99">
           <TD><font size="2"><strong>准备人</strong></font></TD>
           <TD bgcolor="#FFFFCC"><font size="2"><%=preparedBy%></font></TD>
         </TR>
         <TR>
            <TD height="144" colspan="2" valign="top">
               <% XmlNodeList testSuites = doc.DocumentElement.SelectNodes("/descendant::test-suite[./results/test-case]"); %>
               <% foreach (XmlNode testSuiteNode in testSuites) { %>
               <%= DCG.CallTemplate(DCG.FileInfo.Directory + "\\UnitTestReport-TestSuite.dt", new object[] {testSuiteNode}) %>
               <% } %>
            </TD>
        </TR>
      </TABLE>
   </body>
</html>

<%@ Template Name="" Language="C#" %>UnitTestReport-TestSuite.dt

<%@ Template Name="" Language="C#" %><%@ Template Name="" Language="C#" %>
<%@ Assembly Location="system.xml.dll" %>
<%@ Import Namespace="System.Xml" %>
<%@ Parameter Name="testSuiteNode" DataType="XmlNode" %>

<TABLE cellSpacing="0" cellPadding="1" width="100%" border="0">
   <TR>
      <TD colSpan="5" height="40">
         <P align="center"><STRONG><FONT color="navy" size="4" face="黑体"><%=testSuiteNode.Attributes["name"].Value%></FONT></STRONG></P>
      </TD>
   </TR>
   <TR>
      <TD valign="top">
         <P align="left"><FONT size="2"><STRONG>描述</STRONG></FONT></P>
      </TD>
      <TD colspan="4" valign="top">
         <% if (testSuiteNode.Attributes["description"] != null) { %>
         <P align="left"><font size="2"><%=testSuiteNode.Attributes["description"].Value%></font></P>
         <% } %>
      </TD>
   </TR>
   <TR>
      <TD width="15%" valign="top">
         <P align="left"><FONT size="2"><STRONG>结果</STRONG></FONT></P>
      </TD>
      <TD colspan="4" valign="top" width=".">
         <% if (testSuiteNode.Attributes["success"] != null) { %>
         <%-- 如果成功就用绿色,如果失败就用红色 --%>
         <% bool success = bool.Parse(testSuiteNode.Attributes["success"].Value); %>
         <% if (success) { %>
         <P align="left"><FONT color="#009900" size="2">成功</FONT></P>
         <% } else { %>
         <P align="left"><FONT color="#FF0000" size="2">失败</FONT></P>
         <% } %>
         <% } %>
      </TD>
   </TR>
   <TR>
      <TD valign="top">
         <P align="left"><FONT size="2"><STRONG>所用时间</STRONG></FONT></P>
      </TD>
      <TD colspan="4" valign="top">
         <% if (testSuiteNode.Attributes["time"] != null) { %>
         <P align="left"><FONT size="2"><%=testSuiteNode.Attributes["time"].Value%></FONT></P>
         <% } %>
      </TD>
   </TR>
   <TR>
      <TD height="30" colspan="2">
         <P align="center"><font size="2"><strong>描述</strong></font></P>
      </TD>
      <TD width="61">
         <P align="center"><font size="2"><strong>已运行</strong></font></P>
      </TD>
      <TD width="55">
         <P align="center"><font size="2"><strong>结果</strong></font></P>
      </TD>
      <TD width="78">
         <P align="center"><font size="2"><strong>所用时间</strong></font></P>
      </TD>
   </TR>
   <% bool firstLine = true; %>
   <% XmlNodeList testCases = testSuiteNode.SelectNodes("results/test-case"); %>
   <% foreach (XmlNode testCaseNode in testCases) { %>
   <% if (firstLine) { %>
   <TR bgcolor="gainsboro">
      <TD colspan="2">
         <% if (testCaseNode.Attributes["description"] != null) { %>
         <P align="left"><FONT size="2"><%=testCaseNode.Attributes["description"].Value%></FONT></P>
         <% } %>
      </TD>
      <TD>
         <P align="center"><FONT size="2"><%=(bool.Parse(testCaseNode.Attributes["executed"].Value) ? "是" : "否")%></FONT></P>
      </TD>
      <TD>
         <% if (testCaseNode.Attributes["success"] != null) { %>
         <%-- 如果成功就用绿色,如果失败就用红色 --%>
         <% bool success2 = bool.Parse(testCaseNode.Attributes["success"].Value); %>
         <% if (success2) { %>
         <P align="left"><FONT color="#009900" size="2">成功</FONT></P>
         <% } else { %>
         <P align="left"><FONT color="#FF0000" size="2">失败</FONT></P>
         <% } %>
         <% } %>
      </TD>
      <TD>
         <% if (testCaseNode.Attributes["time"] != null) { %>
         <P align="center"><FONT size="2"><%=testCaseNode.Attributes["time"].Value%></FONT></P>
         <% } %>
      </TD>
   </TR>
   <% } else { %>
   <TR bgcolor="beige">
      <TD colspan="2">
         <% if (testCaseNode.Attributes["description"] != null) { %>
         <P align="left"><FONT size="2"><%=testCaseNode.Attributes["description"].Value%></FONT></P>
         <% } %>
      </TD>
      <TD>
         <P align="center"><FONT size="2"><%=(bool.Parse(testCaseNode.Attributes["executed"].Value) ? "是" : "否")%></FONT></P>
      </TD>
      <TD>
         <% if (testCaseNode.Attributes["success"] != null) { %>
         <%-- 如果成功就用绿色,如果失败就用红色 --%>
         <% bool success3 = bool.Parse(testCaseNode.Attributes["success"].Value); %>
         <% if (success3) { %>
         <P align="left"><FONT color="#009900" size="2">成功</FONT></P>
         <% } else { %>
         <P align="left"><FONT color="#FF0000" size="2">失败</FONT></P>
         <% } %>
         <% } %>
      </TD>
      <TD>
         <% if (testCaseNode.Attributes["time"] != null) { %>
         <P align="center"><FONT size="2"><%=testCaseNode.Attributes["time"].Value%></FONT></P>
         <% } %>
      </TD>
   </TR>
   <% } %>
   <% firstLine = ! firstLine; %>
   <% } %>
</TABLE>

还不知道的DCG的朋友,它是我做的一个动态代码生成器,其目的与CodeSmith一样,就是一个动态模板,更多关于DCG请看我Blog中的文章部分。

其实我也完全可以用XSLT来做这个转换,可是我很清楚XSLT的限制,而且它的语法布局不是特别的清晰,而且函数有限,总之做起来不直观,很慢。我更喜欢ASP的语法,这也是为什么我选择并制作了DCG。你问我为什么不用CodeSmith?因为我不太喜欢它的扩展方法,因为它不够开放,因为它的功能不够达到我的目的,很多的原因使我选择了自己制作。

posted on 2005-04-15 13:54:00 by cavingdeep  评论(3) 阅读(3104)

 
2005年04月05日

刚刚看到《动态计算字串表达式值的类》,好像有许多人表示更喜欢解析器形式的求值类。其实我个人倒觉得用反射实现没什么不好,恰恰相反,我觉得这种实现方法很聪明!另外,装配中的脑袋的代码可以稍微修改一下来提高效率,那样再用的话就不会因为反复编译而影响效率了。

言规正传,不知道大家有没有注意我最近在Blog中添加了一个叫ANTLR的连接,它就是一个著名的“编译器的编译器”,不过它实际上是ANother Tool for Language Recognition(ANTLR),它的描述语言可以生成词法分析器、语法分析器与语义分析器,也就是说,我们可以用它来识别加工不同的语言(编译器的编译器)。它同时支持3大类语言的输出:C++, Java, C#(按照生日排序),也就是说,我们可以利用它来用C#生成编译器。ANTLR是免费开源的,而且历史也很久远了,目前的版本是2.7.5,有兴趣的朋友可以在我的Blog的连接中找到网址。

那么,除了用反射轻松的实现计算值之外我们也可以用ANTLR来描述这些数学表达式,下面我附上一个我在看到《动态计算字串表达式值的类》后写的一个语法文件,感兴趣的朋友可以去下载ANTLR来试一试下面的语法哦!

文件Calc.g

header
{
using CharBuffer = antlr.CharBuffer;
}
options
{
language = "CSharp";
namespace = "Cavingdeep.Test.Calc";
}
/******************************\
Calc Parser
\******************************/
class CalcParser extends Parser;
options
{
exportVocab = Calc;
buildAST = true;
}
expr
: sumExpr EOF
;
sumExpr
: mulExpr ( ( SUM^ | NEG^ ) mulExpr )*
;
mulExpr
: powExpr ( ( MUL^ | DIV^ ) powExpr )*
;
powExpr
: negExpr ( POW^ negExpr )*
;
negExpr
: ( NEG^ )* a:atom!
{
if (#negExpr != null) {
AST lastNegNode = #negExpr;
while (lastNegNode.getNumberOfChildren() > 0) {
lastNegNode = lastNegNode.getFirstChild();
}
lastNegNode.setFirstChild(#a);
} else {
## = #a;
}
}
;
atom
: NUMBER
| LCURLY! sumExpr RCURLY!
;

/***************************\
Calc Lexer
\***************************/
class CalcLexer extends Lexer;
options
{
caseSensitive = false;
exportVocab = Calc;
}
WS
: ( ' '
| '\t'
| '\n'
| '\r' )+ { $setType(Token.SKIP); }
;
NUMBER
: ( ( '0'..'9' )+ '.' ( '0'..'9' )+ ) => ( '0'..'9' )+ '.' ( '0'..'9' )+
| '0'..'9'
;
SUM : '+' ;
NEG : '-' ;
MUL : '*' ;
DIV : '/' ;
POW : '^' ;
LCURLY : '(' ;
RCURLY : ')' ;

/***************************\
Calc Tree Parser
\***************************/
class CalcTreeParser extends TreeParser;
options
{
exportVocab = Calc;
}
{
public static void Main(string[] args) {
CalcLexer lexer = new CalcLexer(new CharBuffer(Console.In));
CalcParser parser = new CalcParser(lexer);
parser.expr();
AST ast = parser.getAST();
CalcTreeParser treeParser = new CalcTreeParser();

double r = treeParser.eval(ast);
Console.WriteLine("结果是:" + r);
}
}
eval returns [double v = 0]
: v=expr
;
protected
expr returns [double v = 0]
{
double izq = 0;
double der = 0;
}
: i:NUMBER { v = Convert.ToDouble(i.getText()); }
| #( SUM izq=expr der=expr ) { v = izq + der; }
| #( DIV izq=expr der=expr ) { v = izq / der; }
| ( #( NEG expr expr ) ) => #( NEG izq=expr der=expr ) { v = izq - der; }
| #( MUL izq=expr der=expr ) { v = izq * der; }
| #( POW izq=expr der=expr ) { v = Math.Pow(izq, der); }
| #( NEG izq=expr ) { v = -(izq);}
;

posted on 2005-04-05 15:24:00 by cavingdeep  评论(18) 阅读(13858)

 
2005年04月04日

好消息,DCG的最新版本的下载已上传至SourceForge,大家已可以通过SourceForge来获得DCG的最新版本以及源代码了!^_^这里要感谢朋友及同事Peter Yao,谢谢!

DCG项目网址为:http://sourceforge.net/projects/dcg

posted on 2005-04-04 10:07:00 by cavingdeep  评论(0) 阅读(1110)

 

这的确是一条振奋人心的消息,作为作者的我,终于决定将DCG开源,开源许可(License)为GPL。目前已经在SourceForge上申请通过,不过由于没有FTP权限,所以我还在想解决办法,要是有哪位热心人对DCG非常关注并且有能力上传文件的话请联系我,我相信大家都会非常感谢你的。^_^

DCG的开源网址为http://sourceforge.net/projects/dcg,今后关于DCG的任何动态都将在上面告知,有兴趣的朋友可以常去看看。

另外的一条消息则是DCG再次升级,这次升级至版本1.5.1.0,终于添加了简单的调试功能。实际上就是将最终的执行代码暴露出来,并添加适当的注释,注释标明了在动态模板中的行数。定义模板时在模板头(<%@% Template ... >)添加Debug="True"即可输出有注释的最终执行代码。剩余的调试工作理所当然的就是调试这个最终源代码喽!^_^

希望有朋友尽快帮我上传源代码,好让大家都可以改进它,使它可以拥有更多人的创意!

posted on 2005-04-04 10:06:00 by cavingdeep  评论(2) 阅读(1102)

 

看来越来越多的人对DCG感兴趣了,这我很高兴,不过大家不必太着急,我也是在边做边给大家一个预览,这总比等待所有都做好后在告诉大家要好的多,大家可以知道第一手消息。;-)

不过我也不愿意拖的太久,所以周五下班后就请朋友将做好的DCG上传了,同时还上传了在上一次提到的读取XML文件的示例。下载地址分别为

http://www.swallow.net.cn/dcg/dcg.zip
http://www.swallow.net.cn/dcg/ConstructClassForXml.zip

在这里对朋友Laser的感谢,多谢为我提供空间^_^ 为了让读者在下载后能够马上运用起DCG,我觉得有必要在这里稍微描述一下动态模板的语法,其实很简单,规则如下:

  1. <%@ header params="value" %>,这种形式的tag是模板的headers,模板所支持的所有headers如下:

    1. <%@ Template Language="language" [Name="templateName"] [Version="templateVersion"] %>,模板必须有这么一个header,有了这个header,一个模板才算是正确的模板,它的Language属性指定了模板中动态代码使用的是什么语言。此header只能出现一次。

    2. <%@ Assembly Location="assemblyLocation" %>,这个header指出模板要引用哪个程序集,注意被引用的程序集必须在模板的根目录下,并且在指定引用时不能有任何路径信息,如:<% Assembly Location="MyAssembly.dll" %>。这个tag可以多次出现。

    3. <%@ Import Namespace="namespace" [As="alias"] %>,就像C#中的using namespace一样,这个header导入模板中的动态代码要用到的命名空间。可以多次出现。

    4. <%@ Parameter Name="paramName" DataType="paramDataType" %>,这是个比较关键的header,它指定模板为了运作需要哪些参数,这些参数又分别是什么类型的。可以多次出现。

  2. <% code block %>,在这种tag中的代码为动态代码,这种动态代码并不返回任何值。

  3. <%= code block %>,在这种tag中的代码为动态代码,这种动态代码要求返回值。

  4. <%# function block %>,这种tag定义了一个模板方法,这种模板方法可以在模板的任何其他地方调用。

  5. <%-- comment block --%>,位于这种tag之内的所有文字都被视为注释,在解析时将会被忽略掉。

  6. DCG内置类,这个内置类可在任何地方调用,目前它含有两个成员,如下:

    1. FileInfo,一个System.IO.FileInfo类型的属性,含有关于模板物理文件的信息,如CreationTime等。

    2. CallTemplate方法,这个方法有两个参数,C#中签名如下,string CallTemplate(string templatePath, object[] paramValues),第一个参数要求被调用的模板路径,第二个参数要求被调用模板所需要的参数对象。另外关于这个嵌套模板的使用,读者要注意避免循环调用现象,否则会出现死循环。

好了,这就是基本的模板语法了,比较简单,尤其是用过CodeSmith或ASP的人就更容易上手了。至于OCD的语法嘛,实际上90%与模板的语法一样,只是稍有不同,不同之处如下:

  1. header中的Template被改为OCD以明确他们之间的不同。

  2. header中Parameter被改为Object以表达在模板中是接受一个参数,而在OCD中是制造一个对象的区别。

  3. 模板中的动态代码是放在<%  %>中,而OCD却不用,因为OCD中的代码全部都是动态的,但OCD中的方法还是用模板同样的语法,即<%# %>。

好了,除了这些不同外,它们完全一样。懂得了模板与OCD的语法后,你就能下载DCG v1.3.4.0并马上开始使用了。DCG文件包包括以下文件:

  1. DCG.dll,DCG本身。
  2. Clean.exe,需与DCG放在一起使用,用来清理临时文件,如果不和DCG在同一个目录下,那么运行所产生的临时文件就是个问题了,所以必须重视哦。
  3. dcgconsole.exe,以前所提到的Console Application,使用方法请见dcgconsole本身(在提示行下输入dcgconsole不加参数)。

如果你想将直接引用DCG.dll在你的项目中使用的话,那么调用模板的C#示例方法如下:

// 获得模板管理器。
ITemplateManager templateManager = TemplateManager.Instance;
// 通过模板管理器获得模板。
ITemplate myFirstTemplate = templateManager.GetTemplate(ReadTemplateContent(templateLocation), templateLocation);

// 准备模板所需要的参数对象。
string templateNameParam = "123";
// 调用模板返回结果。
string result = myFirstTemplate.GenerateCode(new object[] {templateNameParam});

正如你所见,在你的代码中调用模板其实很简单,不过注意这里我没有加任何try catch代码,实际运用中模板经常会因为各种原因出错,比如模板定义不对,模板中的动态代码有编译错误、模板中的动态代码有运行错误等等,所以DCG中还给出了一系列的异常类型来提供对异常的分类与处理,关于这个读者可以看看DCG.dll的每个类型,我想看一下就足够理解它们的了,至于详细文档,我会慢慢给出的,毕竟我的时间也不是很多。

最后我希望读者帮忙给出意见,无论是你觉得DCG好还是坏,都希望读者能对DCG给出反馈,以便我对它的加强,谢谢,并祝您编码愉快!

posted on 2005-04-04 10:05:00 by cavingdeep  评论(0) 阅读(1323)

 

我可一直以来都没有歇着哦,这次DCG终于推出了超强功能性版本《1.3.4.0》,新增的超强功能使DCG更加完善了。^_^

具体改动如下:

  • OCD(Object Construction Document),对象构造文件,通过传入一个OCD可以在程序中动态的生成一个对象,配合动态模板可以不用改变源代码就得到不同的输出结果。OCD主要就是用来为动态模板提供参数的对象的。

  • 嵌套模板,在动态模板中简单方便的调用其他的动态模板,这就是嵌套模板。在新的版本中,DCG加入了内置类DCG,而调用嵌套模板的方法就定义在DCG这个内置类中。

  • DCG内置类,这个内置类有一些静态属性与方法可供模板调用,在这个内置类中的成员是与模板本身紧密相关的一些对象,比如DCG.FileInfo属性就含有模板本身的路径等信息可供模板调用。

  • Bug Fix,修正了关于程序集引用的bug。

除了DCG本身的功能增强外,我还写了一个dcgconsole,一个专门用来使用动态模板以及OCD输出结果的命令行工具,它可以通过传入XML配置文件的方式支持批处理,另外也可以在使用者自己的批处理文件中调用,同时对模板的错误信息有详细的描述(或纪录)。

以上为所有的DCG改动,接下来我打算要做的一些扩展如下:

  1. 一个Windows Application(dcgwin),支持与dcgconsole同样的功能,这在一般情况下要比dcgconsole好用。

  2. 动态模板的编写离开编辑器还是比较困难的,所以要做一个动态模板的编辑器,支持检查编译时错误,并考虑加入调试功能。

在推出DCG v1.3.4.0的同时我还写了一套相对复杂的DCG应用示例,希望用此展示DCG的非凡功能并帮助大家快速的了解并掌握DCG。实际场景如下,在制作dcgconsole的过程中,需要读入一个XML格式的配置文件以用来批处理模板,那么dcgconsole就需要有读取XML的功能,这在.NET中用XmlDocument等相关类可以做到,但是这种处理方式不直观,处理用到的代码很难读懂,不方便维护。那么可不可以将XML文件的格式做成强格式呢,就像强DataSet一样?答案当然是肯定的,我就是这么做的。强类型完全需要代码生成器这一类的工具才能做好,要不手工写会累死人的,这里就是DCG介入的地方,我用dcgconsole本身借助DCG生成了读取我定义格式的XML文件,然后再导入到dcgconsole中用来接受XML批处理文件^_^ 使用很简单,只要传给模板一个XML文件的路径它就能帮你生成好读取这个XML结构的一套强类型的类,详细代码我会在下一次Blog中给出。

好了,这次DCG就介绍到这里了,慢慢我会整理出一套文档供大家参考的。

posted on 2005-04-04 10:03:00 by cavingdeep  评论(0) 阅读(1597)

 

几乎DCG的每一次升级都是为了带给最终用户更方便的使用,这一次也不例外。那么这次DCG又带来了什么创新呢?让我们来看看,DCG的变动如下:

  1. 添加了变动保留功能。代码生成器的优点是省时省力功能强,所以我们选择它,但代码生成器却也不是没有缺点的,它的缺点也很大,那就是每次输出都会将上一次输出后的变动冲掉,这使我们使用者很伤脑筋,也正是这一点让我们不太敢使用代码生成器生成第二遍。为了克服以上原因,作者再次升级DCG,扩展了它的功能,通过在输出文件保留信息的方式使之能再次生成时保留修改。示例如下:

    <%@ Template Language="vb" %>
    <%@ Parameter Name="ns" DataType="String" %>
    <%@ Parameter Name="oldFilePath" DataType="String" %>
    <% DCG.ReservedSpace.BeginSymbol = "/*+*/" %>
    <% DCG.ReservedSpace.EndSymbol = "/*-*/" %>
    <% DCG.ReservedSpace.OldFilePath = oldFilePath %>
    <% DCG.ReservedSpace.GenerateDefaultValue = (DCG.ReservedSpace.OldFilePath Is Nothing) %>
    namespace <%=ns%> {
       public <%-sealed-%> class Base {
          public void DoSomething() {
    <%-
    //TODO
             <%"algo"%%>
    -%>
          }
       }
    }

    通过DCG.ReservedSpace来设置变动保留功能,保留空间在模板中的语法为<%-reserved space-%>,其中reserved space为默认值,也就是,当DCG.ReservedSpace.GenerateDefaultValue为真时会输出默认值,当其为假时会输出空白或者上一次的修改。

  2. %>字符串在标签中的输出。在以前的版本中,想在一个标签中(<% %> <%= %>等)输出%>不是一件容易的事,因为它代表着一个标签的结束,但现在通过在标签中用%%>来代表的%>的方式就可以输出%>了。同样在标签外想输出<%要用<%%来代替。

以上为所有的DCG版本1.4.1.1的变动,完全向下兼容。

posted on 2005-04-04 10:02:00 by cavingdeep  评论(0) 阅读(1508)

 
2005年03月28日

因为最近关注AOP的人越来越多,所以就阅读了一些关于AOP方面的文章,一篇个人认为比较好的文章是我在CSDN上看到的转载的一篇文章,转载者没有注明出处是哪里,所以我只好将转载网址贴在这里了《AOP及其Java实现机制》。如果您还没有听说过AOP或者还不怎么了解,欢迎首先阅读转载文章然后再继续这篇Blog。

由于目前我对Java的了解不是非常丰富,所以就不对Java方面的AOP作出评论了,但就.NET而言,我觉得AOP在.NET中的实现还是有一定的不理想因素的。

AOP思想

AOP的基本思想是将不同方面(Aspect)的代码分别开来写,达到逻辑清晰,可复用的目的。与OOP不同,AOP的核心是关注点(Concern),它将程序分为核心关注点(业务)与横切关注点(可能有纠结的功能,例如日志、事务等)。AOP并不与OOP冲突,OOP是AOP中实现业务的手段,我们主要用OOP来做核心关注点与横切关注点的实现,然后用AOP将这两种关注点合并产出最终程序。

AOP的实现

目前,AOP会通过织入器(Weaver)将不同方面的代码混合(织入),织入的方式多种多样,一般来说,只要是能够将两段毫不相干的代码混合在一起,那么就可以叫做织入了。不过,目前大多数产品的做法是采用反射、动态代理、元数据等方法来作为AOP的织入方法。我在下面要讲的,就是反驳大多数采用动态代理来作为AOP织入机制的产品,也就是我前面说到的AOP目前来说,至少在.NET中,还是不太理想的。

Aspect#

在.NET下也有不少的AOP框架,其中比较有名的当数Aspect#,但是我在对它进行了一定的使用测试后,发现了至少对我来说,它丝毫没有用处。它的实现机制就是大名鼎鼎的动态代理与反射。对于还不熟悉动态代理的朋友,它说白了就是“代理”模式的动态形态,可以动态的生成某个类的代理。至于代理模式,就是GoF中著名的23个设计模式之一的代理模式,还不明白的朋友赶快翻阅一下《Java与模式》吧!^_^

AOP中的代理模式

你也许会问,我到底对动态代理有什么不满呢?没有,我对动态代理没什么不满,如果使用正确的话,动态代理还是很有价值的。不过,要想作为AOP的万能织入器的其中一种,动态代理就不是那么可靠了。熟悉代理模式的朋友一定都知道代理模式是靠继承(或对“基接口”的实现)实现代理的,那么如果一个类不可以被继承呢?Bingo,这时代理模式就不成立了,也就是说,如果你用Aspect#对一个sealed类进行Mixin的话(混合以便动态增添新功能)那么它就会失败,因为无法创建动态代理。这到底是怎么回事呢?让我们来看看代理模式(在这里更准确的说应该是装饰模式)是如何做到织入新代码的:

public class A { public A() {} } public interface ILog { void Log(); } public class ProxyForA : A, ILog { public ProxyForA() {} void ILog.Log() { //...... } } A a = new ProxyForA(); ILog log = (ILog) a; log.Log();

就像你看到的上面的代码,动态代理会动态的生成这个Proxy类,并且通过一个Wrapper来将它返回,作为一个A看待,这时看不到ProxyForA存在的你可能会认为真的很神奇,将A的行为扩展了,但当A是sealed类时利用动态代理的弊端就暴露了,因为A是不可以被继承的。这也从另一面证实了一个OO设计原则的重要性:DIP(Dependence Inversion Principle),这个原则基本上告诉我们要以抽象概念做设计,不是以具体,这样,我们就永远都有可能使用代理模式了。关于OO设计原则,我在CSDN上的Blog有一篇总结它们的文章,感兴趣的朋友可以参阅一下^_^

那么现在让我们来看看AOP中的另一个织入手段,拦截器(Interceptor)。拦截器可以根据一个或多个切入点找到要切入的代码部分然后将横切方面(日志、事务等)代码织入,这个织入手段同Mixin一样,本身没有什么问题,问题出在在Aspect#中也是通过代理来实现织入的,这样就又带来了一个新的问题,什么问题呢?虽然目前.NET下AOP框架产品对于切入点的支持不是很广泛,但这也只是迟早的事,问题还是没有的,真正的问题就出在使用代理模式上面,让我们来看看如何通过代理模式在已有的类成员中实现织入:

public class A { public virtual void Do() { //.... } } public class ProxyForA : A { private IInterceptor _interceptor = new LogInterceptor(); public ProxyForA() { _interceptor.Instance = this; } public override void Do() { bool proceed = _interceptor.Intercept(); if (proceed) { base.Do(); } } }

使用动态代理实现切入点的织入就是以以上方式实现的,可以很清楚的看到,这只在A类中的Do方法为virtual时才可以实现,如果某个要切入的成员不是virtual,那么织入就会失败。

总结

以上所谈到的两个使用动态代理的缺陷都是很严重的,如果非要改动代码来适应动态代理的话就会破坏面向对象的设计,严重的甚至可以带来安全隐患。

正因为上述原因,我觉得Aspect#不是一个很理想的产品。个人觉得一种较好的AOP织入实现方式是代码在编译前的替换,这样可以避免所有限制与问题,但缺点是非常难实现。目前我觉得使用AOP框架倒不如自己的好设计,如果设计的得体,那么同样可以后期通过代理、装饰等模式对系统的行为进行快速方便的扩展。同时我也很期待新的技术的出现来使AOP成为现实!

posted on 2005-03-28 17:17:00 by cavingdeep  评论(13) 阅读(6634)

 
2005年03月22日

前段时间经常听说很多关于Subversion的好话,所以我决定也是时候真正体验一下了(我一直都是关注Subversion的哦)。Subversion相对CVS来讲有以下几点优势:

  1. 增加了元数据(Meta-data)的版本控制,Subversion有很多设置都是通过元数据实现的。
  2. 增加了文件夹的版本控制,没有文件夹的版本控制是CVS中一个很大的不足,这点在Subversion中得到了解决。
  3. 支持文件、文件夹的重命名与移动,好处多多,方便多多,不用再描述了吧。
  4. 真正的原子提交(Truly Atomic Commits),提交作为一个事务,如果某一受控对象提交失败,那么其他提交的对象也不会有效。这个功能真是一个字:好!同时支持不同仓库(Repository)中受控对象的同时提交。
  5. 一些性能上的优化,如分支(Branching)与标签(Tagging)会采用文件夹的受控方式达到优化。

Subversion在使用习惯上与CVS最大的不同之处在我看来就是它的修订号(Revision)的定义了。Subversion中修订号不是针对某个特定的受控对象的,而是针对整个仓库的,你在Subversion中不会说“文件A的第4个修订”,而是“仓库修订4中的文件A”,每一次提交都会导致仓库修订号的增加。更多关于Subversion的介绍请看它自带的文档,这里就不叙述了。

如果你决定使用Subversion,那么你很可能需要下面几个软件(免费开源):

  1. Subversion服务器与客户端(http://subversion.tigris.org),官方版本,我采用的版本是1.1.3。Windows下建议下载安装文件包,因为安装更全面,而且会自动配置环境。
  2. TortoiseSVN(http://tortoisesvn.tigris.org),官方版本,Windows下有两个,一个是常规版本,一个是为VS.NET 2003及更低版本Web项目不支持.svn文件夹的bug所做的非常规版本。非常规版本数据不能与其他版本通用,所以不建议使用,更多信息请看Subversion的相关文档。
  3. SVNService(http://dark.clansoft.dk/~mbn/svnservice/),可选。可以安装一个Windows服务来运行svnserve服务器(Subversion的独立服务器。Subversion可以使用两种服务器,Apache作为服务器或者它自带的svnserve)。

依次安装后就可以使用Subversion了。与CVS同样,Subversion(SVN)分为客户端与服务端,首先你可以用TortoiseSVN建一个仓库(Repository),注意这个操作是在服务端,然后你要选择一种连接到仓库的方法(在客户端),有以下五种:

  1. file:///ReposPath/RepoPath,以文件的形式直接访问,最简单的连接方法,不需要服务器,权限由操作系统来控制,如果后台仓库你采用了FSFS(文本)格式的话那么还可以在局域网共享中使用。
  2. http://host/Repos/Repo,如果你采用了Apache作为服务器的话就可以以这种形式访问,权限由Apache设置。
  3. https://host/Repos/Repo,与上一个一样,同时采用了HTTPS加密通信。
  4. svn://host/Repos/Repo,如果你采用了svnserve作为服务器的话那么就可以采用这种形式访问,它是一种基于TCP/IP的访问方式。权限由仓库的配置文件设置。
  5. svn+ssh://Repos/Repo,与上一个一样,同时采用了SSH的加密通信。

有了连接的URL后就可以用TortoiseSVN连接到仓库了,然后你就可以开始真正使用Subversion了。其他的我就不说了,有兴趣可以自己看看文档,推荐首先阅读TortoiseSVN的文档,Subversion的文档复杂度有些高,不宜初学者!

posted on 2005-03-22 15:16:00 by cavingdeep  评论(20) 阅读(13668)

 
2005年03月10日

XP作为一种还算年轻的软件研发的方法论目前应该可以说开始普及了。作为一个软件研发人员,我非常赞同XP理念,XP的理念中充满了使项目成功的关键思想,而这些思想不仅仅是技术上的,而是很大一部分是管理与沟通方面的。XP集成了许多最佳实践,而这些串连后的最佳实践使整个项目又变的有趣起来,这其中也包括了XP开发人员特有的积极向上的态度与责任心。这里我想向大家描述一下我个人的XP实践感受……

下面我分别写一下我对XP中其中12种最佳实践的感受:

  1. 现场客户(On-Site Customer)。客户的需求不是一成不变的,往往在需求收集阶段事情总是很抽象,就连客户自己都不知道他们具体要什么,他们只是提供一个轮廓,要我们双方共同去构造理想中的产品。但是我们,包括客户都不可能预测说我们现在定下来的需求就是真正的一成不变的需求,总是有千万个理由会促使需求的变动,也就是说需求的变动是必不可免的,它不是单纯的可以人为控制的。那现在的问题是,但需求变动时,我们有能力迎接(适应)它吗?现在XP提出了几种最佳实践来迎接需求的变动,而其中一种既是On-Site Customer,通过客户在整个项目中的不断参与我们可以保证我们所作的一切都真正是客户想要的,而我们不会浪费时间与精力在不需要的功能上。这是按时交货,交好货的一种关键做法。注意这个最佳实践要求的并不是客户每天8小时的参与项目,而是但我们需要客户解答某些需求疑问时,客户能及时的提出意见,给出反馈!
  2. 小发行版(Small releases)。上面提到了需要客户能及时给出反馈,那么及时得到反馈的一种好的做法就是提供给客户软件的预览版,毕竟没有实物客户很难提出他们对我们正在做的项目的看法。XP要求小发行版正是这个用意,通过快速的小的迭代开发得到很多的小版本,然后将这些小版本拿给用户体验,收集反馈,再根据用户的反馈继续下一个小版本的迭代开发,这是一个良性循环,保证了项目不走歪路,保证了客户对我们正在做什么没有偏见,同时也保障了项目的质量,因为直接参与测试的同时也有我们的客户。
  3. 简单的设计(Simple Design)。上面讲的是从需求的角度出发的,现在让我们来看看开发流程方面。我们都知道设计是好的,而事物总是保持越简单越好,那么为什么不选择简单的设计呢?通过简单的设计我们可以快速的理解与开发。但是但靠简单的设计往往不是很有作用,很快我们就发现以往的设计已经不再有弹性,很快就不能满足我们的需要了。这里就是体现XP精华概念的时候了,XP中有极限一词,也就是说不管是做什么,都要做到极限。简单的设计也不例外,同样也要不断的迭代,通过在简单的设计上重构出另一种简单的设计来不断改变设计与设计质量,会让你在不知不觉间作出与超级设计大师一样优质的设计。(当然,如果你真的很糟糕,那么即使XP再好我想也帮不了你了)
  4. 规划策略(Planning Game)。既然简单的设计有助于快速的进展那么不断的规划也一定会帮助到项目的方方面面。很难说架构师做好的架构就一定不会在后期改变,既然需求都很有可能有大改变,那么架构与整体项目规划也不是一成不变的。这是XP的核心,XP告诉你如何迎接变化,如何在快速变化的今天作出最优质的产品,真正满足客户的需求。
  5. 系统隐喻(System Metaphor)。说实话,我也是刚刚开始真正了解XP,所以对这个实践还不太了解,所以这里就不谈了,免得败坏XP的名声!^_^
  6. 重构(Refactoring)。我想很少有人在这个年代没有听到过重构吧?上面所讲的很多方面都是基于这个思想的。既然设计是好的,那么我们能不能快速的、小步的迭代设计呢?能不能通过不断的改善已有的代码来使它们更健壮呢?目前的项目大多数都会在维护一段时间后变的僵硬,代码越来越难以维护,有时不得不写一些自己都认为很难看的代码来维护新增或修改的功能,而正是这样的动作导致了下一次维护时的困难,使代码陷于了恶性循环,使的项目的维护成本越来越高,最后不得不放弃维护而重新开发。XP提出了迭代的重构方案,它使我们每一次都先重构再在重构好的代码的基础上再做修改,这使得我们每次都不用花太多的精力去完成对代码的维护而又与此同时提高了代码的简易度、健壮度。
  7. 结对编程(Pair?Programming)。既然对设计、对代码的评审是好的,那么为什么不随时评审呢?通过评审,我们可以用多个人的脑袋想问题,寻找解决方案,无疑这要比只用一个脑袋要好的多,毕竟人多力量大嘛,曾经也有教育家做过试验,证明了在群组讨论学习的环境下学生学到的知识更深刻,理解的更透彻,思想也更活跃。软件开发又何尝不是如此呢?我不完全赞成“结对”,但是我赞成有问题就问,不管什么样的问题,不管什么样的同事,只要你有疑问,那么你就可以向大家提出来,看看每个人的想法,这会使你最终得到一个当前最好的解决方案。
  8. 集体所有权(Collective Ownership)。在上个实践中其实已经提到了这个集体所有权的概念,既然你有问题就可以问,别人又都参与你给你他们认为的可能性答案,那么是不是反过来但别人有问题的时候你也要主动参与提出自己的看法呢?这种把多个大脑运作在一起的模式就是XP中集体所有权的概念了,在这里没有个人的殊荣,只有团体与团体的共同目标。注意这里就是XP的特点了,XP是以人为本,XP坚信人与人之间的作用要比单纯的依靠技术要更重要的多。毕竟只有最后依靠到人心上,项目才可能获得最后的成功。而作为一名XP开发人员也意味着你的个人休养与风格。
  9. 编码规范(Code Standards)。代码在XP中被看作一项非常重要的在开发人员之间沟通的工具。毕竟软件反映了开发者的心智,开发人员的思想变为一行行代码被写在程序中,这是一门艺术。但艺术也要有人去欣赏,如果每个人都看不懂其他人的艺术,那么怎么进行沟通呢?集体所有权让我们每个人都可以去改进其他人的代码,那么如果没有统一的编码规范将不是一件容易的事了。
  10. 一周40小时(40-Hour Week)。XP不主张加班加点,这只能是当有某些环节出问题的前兆。XP在每次快速的迭代中都很明确下一次迭代要做什么,XP有很高的可控性。
  11. 测试(Testing)。测试是XP的核心部分,是XP重要的环节,也正是处于这种原因,我将两种测试放在了最后面介绍,不是因为它们不重要,而相反是因为它们太重要了!测试其根本目的是为了保证质量,告诉我们所作的一切没有白做,它的确可行。XP中的自动化测试(常指简单有效的单元测试)是非常关键的,它有多重目的。其中一个很重要的目的就是给予我们开发人员信心,试想每次系统变动后运行自动化测试时看到一排路灯亮着是那么畅快的一件事啊!XP主张先测试后编码,每次的测试用例都能够反映出最确切的需求,测试亦是设计的一部分。其中的奥妙真的是只有真正用过才能体会得到。
  12. 持续集成(Continuous Integration)。这是另一种保证质量的测试手段,通过反复快速的集成我们可以及早发现软件中的隐患,同时持续集成也可以提供给我们多个小版本(Small Releases)以便客户的反馈。这个概念比较像微软提出的每日编译,只是XP并没有规定必须每日执行一次,XP反而主张每几个小时甚至每几分钟集成一次,总之就是当有较大变动时就及时集成给出反馈,这一点也能够充分的体现出XP极限编程中的极限来!

好了,以上是我个人在对XP有了一定充分的了解后所得出的结论,供给大家参考,我个人及其欣赏XP勇于创新的概念以及其在项目中的实际运用。虽然Kent Beck不主张将XP中的最佳实践剪裁掉使用(他主张将XP就像其概念一样统统发挥到极限,每一样都用的淋漓尽致),但是我个人还是同意每个组织根据其特性考虑去掉或消弱某部分实践来真正将XP运用起来,而不是只停留在大家都评论却没人敢去做的现象中。

posted on 2005-03-10 14:47:00 by cavingdeep  评论(30) 阅读(7337)

 
2005年02月17日

通常如果使用VS.NET的话会理所当然的使用VSS作为版本管理系统,但是VSS并不像其他版本管理系统那样强大,另外它也是要花钱的。相对CVS是Java上受众较广的一个版本管理系统,免费,功能强大而结构简单。不过不是因为是免费的所以稳定性不好,如果选择一个较稳定的版本的话CVS还是非常可靠的。如果要对比CVS与VSS的话那么我想你一定会更喜欢CVS的。这里就不作比较了,不是本文的目的,本文的目的是告诉你如何在.NET项目下使用CVS管理版本。

第一步就是下载相关软件,以下是需要的软件,全部免费并且开源:

  1. CVSNT(http://www.cvsnt.com/),CVS是一个真正的C/S版本管理系统,所以有服务器和客户端的说法。CVSNT是CVS服务器中的佼佼者,要比官方网站的好的多,而且支持多个平台,是目前使用最广泛的CVS(包括客户端)。记住一定要挑一个稳定的版本哦,一般相对旧一些的稳定版本才是真正的稳定版本。

  2. TortoiseCVS(http://sourceforge.net/projects/tortoisecvs),CVS在Windows下的客户端,这个东东做的非常好,与Windows Explorer紧密集成,CVS这么复杂的系统使用这个客户端就简单多了。

    本来还有一个产品叫CVS SCC Proxy插件,可以使微软的多个IDE拥有直接访问CVS的功能,但是这个插件不是免费的(虽然也不是很贵),而且目前还有许多功能上的不足以及bug,所以我没有打算采用它。不过插件的集成效果倒是非常的好,在VS.NET中与操作VSS没什么两样,除了CVS本身的功能要比VSS多一些除外。

  3. WinMerge(http://sourceforge.net/projects/winmerge),一个比对与合并的软件,TotoiseCVS可以用它来做版本比较与合并操作。

依次安装,安装好后就可以使用CVS做版本管理了。CVS并不是一个非常直观的软件,使用它还是需要一段过程的,一般相关软件的文档中都有教材告诉你如何使用CVS,这里就不叙述了。以下是TortoiseCVS在使用中的图片。

posted on 2005-02-17 09:44:00 by cavingdeep  评论(11) 阅读(7636)

 
2005年02月16日

Hi All,

我是Cavingdeep,也就是从前的Kefroth,可能有人认识我,没错,我改名字了。^_^博客堂专注于技术,希望不仅仅是.NET,因为我还有很多其他方面的技术可以分享哦!我的主Blog在CSDN上,欢迎交流!

posted on 2005-02-16 08:33:00 by cavingdeep  评论(12) 阅读(2690)

 

Powered by: Joycode.MVC引擎 0.5.1.0