代码改变世界

您选择简单的组合,还是完整的集成?

2009-10-28 01:09  Jeffrey Zhao  阅读(17577)  评论(39编辑  收藏  举报

其实这是两种文化,很多人说前者属于Unix文化,后者属于Windows文化。前者好比是一系列分散的小工具,它们互相配合完成任务,其典型代表是Unix Shell。而后者好比是一个完整的工具箱,包含了完成某件特定工作所需的几乎全部功能,其典型代表是我们再熟悉不过的Visual Studio。当然,既然是文化,就能够互相借鉴和采纳,因此我们在Unix环境中也可以使用如Eclipse和IntelliJ IDEA,而Windows环境中也有cygwin和Powershell。

可能一下子说起这个有点过于直接了,那么我们先来看一下小例子。我们.NET开发人员一定知道,一个项目在VS里编译好以后会出现obj目录和bin目录,其中包含了编译过程中的输出和编译结果。假如我们想要把整个解决方案打包给别人的话,我们一般要把这两个目录删除,它们的体积还是很可观的。那么,你会怎么做?是一个一个进入项目目录里删除,还是用某种方便的做法?这个问法可能有些愚蠢,因为我相信大部分朋友在经历多次“手动删除”之后,都会厌烦,都会设法有什么自动化的方式。不过此时其实也有两种做法。

例如,有人开发了这么一个简单的项目:TreeTrim。这个小工具有个功能,便是在资源管理器的右键菜单中增加“清理”的功能,这样我们便可以方便地删除obj和bin目录。不过,我们也可以选择另一种方式,例如“命令行”。Windows传统的命令行功能比较弱,和Unix Shell相比简直天上地下。于是有人把Unix Shell移植到Windows平台上,是为cygwin。微软在两年前也推出了PowerShell,虽然从“已有功能”上讲远不如cygwin完整,但PowerShell毕竟是为Windows量身定制的,可能更容易被Windows程序员接受(如命令的alias,文件分隔符,.NET支持等等)。而PowerShell的关键还在于“Shell”,一个命令(cmdlet)之间通信的“工作平台”,我们需要的功能基本上都可以通过命令之间的扩展或组合表达出来。

例如在PowerShell中,我们如果要列出当前目录(及子目录)下所有的bin和obj文件夹,可以这样写:

Get-ChildItem -include obj,bin -recurse 

那么,我们又该如何将列出的目录删除呢?可以这样:

Get-ChildItem -include obj,bin -recurse | ForEach-Object { Remove-Item -path $_.FullName -recurse }

我们通过“管道”,也就是“|”符号组成一个命令。管道的作用是将前一个命令的输出,作为后一个命令的输入。例如上面的例子,Get-ChildItem命令得到的所有目录,会作为参数传递给ForEach-Object命令,后者再为每一项(即目录)执行一个RemoveItem命令进行删除(大括号中的$_即表示当前那一项)。看上去这个命令很复杂,输入很麻烦,不过好在PowerShell的命令行有自动补全功能,而且其实几乎每个命令都有一个或多个别名(alias),或者说是“简写”形式。例如上面这行命令也可以这样写:

dir -i obj,bin -r | % { del -path $_.FullName -r }

效果也是完全一样的。

其实我本来没打算谈这些,因为无论是PowerShell的使用,还是“文化”都已经被许多人嚼烂了。我今天写这个东西的原因是,我想要做的一件事情正好落入了这样的“俗套”,于是不禁“多想”了一些。这个事情便是所谓的“项目模板”。

Visual Studio很强大,支持自定义项目模板或元素模板,打个zip包丢到指定目录里就行了。使用项目模板创建项目,便可以创建出预定义好的许多内容,例如文件,程序集的引用等等。在使用模板创建项目或元素的时候,我们还可以给出一个“向导”,例如创建ASP.NET MVC项目的时候,会提示您是否创建配套的单元测试项目。之后,对项目的Views目录点击鼠标右键还会出现“Create View”选项,选择后还会有新的向导窗口,让您输入更多的配置等等。这些配置项会用来替换模板中某些文件内容里的占位符,于是生成的结果就会有所不同了。总之,ASP.NET MVC秉承了微软一贯的风格,除了没有把项目直接帮你做完,已经给了我们许许多多的帮助。于是我们很多程序员就像被父母宠坏的孩子,只想着衣来伸手饭来张口。一旦遇到问题,就开始嚷嚷微软提供的东西怎么那么差,问题那么多,又没有技术含量——也就好像不懂事的小孩遇到困难就去责怪父母没有把一切都安排好。

我不是这样的“孩子”,但有了微软这样的榜样,我也总是想着如何做好一个“父母”。比如,我最近在准备TechEd的讲座示例,也想着MvcPatch项目的使用方式,于是我也想像ASP.NET MVC那样创建一个模板。目标很简单:让用户输入基础的程序集和命名空间,然后由此确定每个项目的相关属性。但是我不会啊,我从来没有做过这个东西。记得我好像在05还是06年的MSDN Magazine里看到过相关的文章,但从来没有亲手做过任何一个模板。于是我打开了文档,琳琅满目,最后……发现这真不是一件容易的事情,又要设计文件内容,又要编写窗体。可能装了某些插件之后可以方便,但是我也真不想把时间花在这个东西上面,因为这对我来说又有什么长进呢?

不过后来我忽然想到,其实我要的功能完全只是个“项目文件”加上“占位符替换”而已,这个工作完全也可以由PowerShell命令完成:

Get-ChildItem -path src -recurse | Where-Object { $_.mode -like '*a*' } | ForEach-Object { (Get-Content $_.FullName) | ForEach-Object { $_ -replace "ProjectAssemblyNameBase", "RealWorldMvc" -replace "ProjectNamespaceBase", "TechEd.RealWorldMvc" } | Set-Content $_.FullName }

不过这样的完整写法很冗余,适合写在脚本中。如果只是单行命令,不追求长期的可读性,也可以使用它的等价简写形式:

dir -p src -r | ? { $_.mode -like '*a*' } | % { (gc $_.FullName) | % { $_ -replace "ProjectAssemblyNameBase", "RealWorldMvc" -replace "ProjectNamespaceBase", "TechEd.RealWorldMvc" } | sc $_.FullName }

就此,任务也就完成了。可能我只需要再准备的脚本,提示用户输入一些字符即可。在这样的场景下,使用复杂的项目模板的确会更加方便一些,对开发人员的支持也更好。但是从性价比角度来说,这种基于PowerShell解决方案难道不是更直接一些吗?

本来还想补充一些话,最后打算还是独立开篇吧。