RSS 2.0 Feed

Tuesday, January 03, 2006

2005年的最后一天,北京。一大早,雪下得好大——这该是2005年的最后一场雪吧,或许也是2005年第一场真正意义上的雪。晚上,夹在滚滚车流中,来到首体听齐秦黄金20年的演唱会。一首首熟悉的老歌唱起,我的脑中也随之浮现出历历往事——好一个黄金20年啊……
 
而2005年对于我和我们团队来说,则是非常具有戏剧性、充满转折点的一年。
 
自从2004年7月开始创业以来,身边的朋友们都十分关注我们项目的进展。创业之路确实崎岖,然而我们仍然坚信,只要坚守信念和理想、坚持到底就是胜利。事实证明:只要精神不滑坡,办法总比问题多。
 
2005年的新春佳节的前几天,团队的投资人之一突然变故,团队一夜之间竟连个“窝”都没有了,频临破碎。幸好canyue家有间两居室的新房,花了几百块钱简单装弄了一番,我们开始了一穷二白的第二阶段创业。如果说只是物质上的苦,对我们来说也真的不算什么,可屋漏偏逢连阴雨,sumtec退守观望,原本坚定的musicland也因为家里的压力不得已而退出——深深地,我被打击了一下。博文视点的周老师说过,有些人并不像看上去那么坚强,有些人却比看上去要坚强得多。是爷们儿就得坚强不是,谁让咱们认准要走创业这条荆棘路呢!
 
挺到五月份,终于迎来转机——不过也不是天上掉馅饼,而是需要权衡得失利弊,决定是否与农业部系统内的一家公司在深层面上战略合作。我带着兄弟几个把局势仔细分析了一番,考虑了若干种可能性,最后大家终于统一了思路——在坚守信念的大前提下,放弃一些可以放弃的利益,去争取一些需要争取的资源。还好,自此之后,事情一直如愿发展。我们用了大半年的时间来建设公司的团队,帮助公司打开了局面,也同时借助几大项目来运作共同的资源。就这样,我们双方在优势互补、资源共享的大前提下都得以和谐发展。
  • 这一年,我们和电视媒体相关的3G无线增值项目的理念被越来越多的业内人士所认同,而大环境、市场及相关政策也给我们留出了足够的成熟与完善的时间与空间,阿弥陀佛;
  • 这一年,我们依托农业部的项目研发了集影像识读(包括条码与二维码的识读以及图像拍摄)、智能卡读写、全球卫星定位以及无线数据传输等四大功能于一身的掌上电脑,为日后的移动应用拓展奠定了坚实的基础,也在这个过程中积淀了必要的人脉和网络;
  • 这一年,我们把创新精神和前沿技术用在国家大事上(禽流感监测、预警与防控指挥决策),确立了总投资约1.3亿的部级科技攻关项目和相关工程,同时拓展了我们的合作伙伴网络,为各位伙伴也带来了更多的机遇和收益;
  • 这一年,我们还为明年的互联网事业奠定了项目基础(有谁知道国内有多少Google Earth玩家?)……
总之,2005年对我和我们团队而言,都是充满曲折、变数、挑战和戏剧色彩的一年。而2006年已经到来,期望在这一年里大家都能脚踏实地,把计划中的事情一项项落实,惟有这样,再大的事情、再难的问题也终归会被解决,胜利总是属于内心坚定、敢想敢做的人们的!加油!
 
另外号一个外:不出意外的话,微软出版社的《代码大全》(第二版)将由电子工业出版社博文视点公司在春节前后发布上市,我作为第一译者有幸代表全书所有译者为本书作序。这是一本经久不衰的好书,它影响了十几年前的一批程序员,也必定会对今天广大的程序员有所帮助。真心期望每一名真正的程序员都读过这本书(无论是译本还是原版),并将书中的实用技术应用于每日的编程工作中,真正地体会到编程的艺术性和科学性。
 
最后,祝大家2006年顺利、快乐、梦想成真!旺旺旺~~  

posted @ | Feedback (28) |

Thursday, December 30, 2004

不管怎么说,智能手机是一个非常好的智能客户端应用平台(广义上说,并不是只有.NET可以实现智能客户端应用——只不过在目前它确实有一些优势——尤其是站在开发者的立场上来说)。就这一点,就足够让智能手机做很多令我们生活发生变化的事情了——当然,核心问题还是“应用”。

 

什么样的应用可能会改变我们的生活呢?比如说——移动电子商务应用(M-Commerce)。

 

表面上看,从电子商务到移动电子商务,无非是借助了智能移动终端的移动性和网络通讯能力而已。然而,移动电子商务在很多情形下对于传统商务的冲击,要比当年基于互联网的电子商务要猛烈得多——比如说购书。

 

昨天的北京现代商报的报头文章题为“过半购买数冲动消费”。文中是这样说的:我国的冲动型消费所占比重呈现逐年上升的趋势,随着人均可支配收入的大幅度提升,冲动型消费已经成为大众消费中的主流。

 

回想一下过去的一年中,有没有人因为冲动而购买过图书?你有没有向自己的朋友们推荐过什么自己钟情的图书?有没有过在不经意间随手翻开一本陌生的书而由此产生购买的欲望?然而,又有多少这样的欲望真正转化为了购物行为呢?

 

国内目前从事图书类电子商务业务的电子商务服务提供商已经达到六十余家,其中既有老牌的当当网卓越网贝塔斯曼,也有日益成熟的互动出版网China-Pub)、第二书店,当然还有不记名数的初出茅庐的小字辈。在非典期间,由于人们闭关自守、足不出户,很多网上书店都迎来了一阵销售高峰——然而,毕竟这只是非常时期的非常现象。回想一下你或者你身边的人,当购物冲动产生的时候,有多少次其实是当书在手中、而身边又没有互联网让你去享受电子商务的便利和优惠的时候呀!

 

而如果此时此刻,你手中的手机可以让你在几十秒之内把这种购书冲动转化为消费行为呢?如果你手中的手机可以让你以更好的用户体验享受这一过程呢?我相信,只要不会为此付出过多的代价,而又有足够好的用户体验,人们是会慢慢接受并习惯甚至依赖于这一全新购物形式的。

 

没错,移动电子商务是传统电子商务销售渠道和用户体验的延展,然而其移动性带来的体验变化将很有可能对传统商务形式带来远比当年电子商务出现时更大的冲击!试想一下,如果在所有的地面书店、朋友家中、公交车上,每当人们看到可心的图书产生购物冲动时,都会习惯性的掏出身上的智能手机查查网上能买到的最低价、看看别人写的书评、甚至就此把这本书放入购物车或者下单的话——也许以后传统书店都会打出“本书店谢绝使用智能手机购书”的提示呢!

 

由此可以看出,有些时候,一些技术的出现将可能对整个产业、人们的生活习惯等等产生意想不到的、甚至是颠覆性的影响和变化——就如鼠标的出现使得人们使用计算机的方式以及应用程序的交互设计完全变了个样,就如MP3这么一种音频压缩与回放技术的出现几乎颠覆基于磁带和光盘的随身听一样——说不定,在特定业务领域中,移动电子商务的出现也将改变很多事物。

 

那么,谁将是为市场带来这一体验的先行者呢?什么时候我们真正可以用自己手中的智能手机踏踏实实、舒舒服服的随时随地的购书呢?嘿,等着看吧,也许用不了个把月大家就会看到了!先问一句,如果你就有智能手机的话,你希望是哪一家先觉悟并行动起来把这个想法付诸现实呢?;)

 

P.S: 想先获得一些感性认识的朋友,可以在微软中国智能客户端的案例研究中找到我们公司(无线视通)为微软做的一个demo(仅是demo而已,真正的原型kaneboy见过,你可以来说说你的感受,呵呵),其中就有和本文内容相关的一个演示,感谢开心哈达。另一个概念演示也很有意思,代表了我们眼中所谓智能客户端应该具备的关键特性。

 

BTW: 本文标题借mvm的光,实属荣幸。;)

posted @ | Feedback (26) |

Saturday, October 16, 2004

昨天晚上我们的团队在饭后展开了一个小小的话题,这个话题原自我这些天来的一个设想。对于这个想法,我已经思考了一段时间,感觉应该拿出来供大家头脑风暴一下了,于是我向大家说明想法的来龙去脉,并希望大家调动思维,想想看这个创意是否有潜在价值——请注意,这件事情并没有提到议事日程,我仅是拿出来一个概念让大家集思广益,想想看这个东西的价值可能在那里,还是根本没有价值。

 

请问如果我希望有效率、有效果的达成这个目标,我们应该采用什么思考方法或工具呢?

 

Positive Thinking!

 

正面思考是什么呢?正面思考意味着只去想好的事情、对事情成功有益的因素,而不考虑任何会引起事情没有价值、无法实施、导致失败的因素。

 

你觉得这是考虑问题不全面?其实不然,因为人的思维是渐进式的,人不会在瞬间凝固对一件事物的认知。比如说,你的朋友跟你说他新买的手机多好多好,这时候你对他的手机会产生正面的看法(即产生概念),然而这并不意味着你就会头脑发热的也跑去买一部(即付诸实施)。因为当他给你介绍这款手机的时候,这只是一个和你利益暂无关系的信息;而当你要掏钱买这部手机的时候,这必然需要进一步考量是否有这个必要、是否合时宜——如果这部手机的价格高于你的预期很多倍,那么多少正面的信息也不足以让你掏钱去买它。也就是说,在不同的阶段,其实应该以不同的方法去考虑问题,这样才不会错失太多的机会。比如说:大卖场上经常在促销手机,宣讲各种各样好的功能特性,而你首先想到的是他在“吹牛”、又来骗钱、反正我已经有一个了跟我没关系了……然后你就扬长而去了——结果没想到这是一次拍卖,最终拍到这款手机的人才花了10块钱。(什么?10块钱还觉得被骗了?噢!原来买了个坏的!——见后文“实施前不做负面思考则会增加风险”:)

 

尤其对于产品研发阶段而言,正面思考绝对是一个有效的思维工具。它能让你在一种积极、愉悦的模式中发现埋藏在事物深层中的价值。我举个例子,任何两件事物,可能从来也没有过关系,你把它们放在一起,问问大家我们从中能够有什么机会。如果不是正面思考,可能有人上来就说出一大堆两者结合毫无价值或者困难重重的理由——这其实真的是很容易的一件事情,以至于人人都会(原因很简单,任何事物都是有利有弊,而人们可能更容易从自卫的角度出发去否定事物——包括否定自己、否定别人以及否定未来)。你觉得这样思考有益于发现机会吗?显然不会。退一步说,在确立机会之前就考虑那些负面因素根本就是毫无效率!可不是吗?既然这件事情还不是机会,肯定不会有人去做,那么把脑子用在考虑那些会引起失败的原因上面可不是没有任何意义也缺乏效率吗?

 

因此,我强调强调再强调,我们是创造新事物的团队,我们一定要使用“正面思考”这一强大的工具!尤其是团队的领袖和领导,更是要如此。试想,一个人刚刚提出一个新念头,你就提出一堆不可以、不太好的话,先不说直接打击思考者的积极性,就算别人想出来了正面的理由,谁还敢说呢?(因此在贯彻“正面思考”之前首先也要培养“平等交流”的意识,以便在极端情况下团队仍然能够积极的不受任何阻碍的思考……)况且如果是一个聪明人提出来的想法,他自然已经在正面思考之后做过负面的分析,当他仍觉得机会存在时才会拿到大家面前来探讨,这时候你也去提一些谁都能想得到的负面因素,首先不会表明你更聪明,反过来还会让提出想法的人感觉受到轻视。这种文化在团队中形成的话,对于开拓思想、发现机会而言就是一个灾难。

 

也许你已经猜到了,本来我是希望让大家对我的想法brainstorm一下,来共同挖掘一下由这个创意带来的潜在机会,结果变成了我来重新强化团队要首先“正面思考”的思维方法——也许有人会否认思维方法对解决问题的效果,然而我多年来的切身体会早就让我感受到了正面思考的价值,甚至于我偏执的认为团队中的负面思考者对整个团队都是有害无益的。为什么这么说?因为人的思维是容易受潜意识的左右的。试想某一件事情客观的讲有5个有利因素和5个不利因素。如果团队中的一个人首先提出来几个不利因素(尤其是持领导者地位的高层人士或其他有威望的人士),其他大部分人的潜意识就会受此影响以至于对有利因素更加的不敏感。反过来,如果上来就会有一两个有利的因素提出来,其他人的思维就会变得主动、活跃,因为大家会受价值的驱使而转向从好的角度思考问题。因此在领导团队的思考过程中,领导者能否积极的合时宜的正面思考对于整个团队而言都是意义重大的。

 

当然,我们做事情也不能只看有利因素而不看不利因素,问题的关键在于思考问题的阶段。我的原则是,在形成机会的阶段一定要“正面思考”,而在实施之前强调“负面思考”。如果用反了,肯定只会压抑对新机会、新思路的发现;而如果不重视实施前的“负面思考”,则可能导致更高的风险、把好事办砸。

 

总之,我是一个会善用positive thinking的人,这让我发现了比别人更多的机会;我也要求我的团队在适宜的时机保持positive thinking。这实在是一个强大的思维工具!

 

有兴趣的朋友,可以继续来看两篇不错的文字:The Power of Positive/Negative Thinking.

 

如果你也是一个positive thinker,不妨也来激发一下灵感,把一些毫不相关的事情放在一起,看看有什么机会?比如说,电视和博客?比如说,DIPAdapter?:)

posted @ | Feedback (18) |

Thursday, September 02, 2004

刚刚上MSDN Subscriber Downloads扫了一圈,发现已经有Visual Studio 2005 Beta 1 Refresh with Team System(TS)的下载了(3.39G Full DVD Image)。跟上次不一样的是,这次RSS却没有更新——不知道MSDN的人在搞什么搞啊。而且更恶劣的是,到现在这个大家伙还不能正确下载,FTM总是报internal error,我严重faint……

其实对这个Team System一直比较期待,因为我最近也在组建团队中,肯定是需要把团队开发的基础打好、环境建舒服了,才能最大程度的发挥人的价值。所以等下载回来体验一下,再回来向大家报告一下心得体会啥的。

这两天sumtec渐入佳境,思维明显开始活跃起来了。由于这个月这次迭代是要形成初步的架构设计(上个月花了一整个月来形成用户体验原型、而这之前的时间则都是在分析、挖掘用户需求),因此大家讨论的主要内容都是和OOAD、Patterns、Principles相关的,而我也十分关心Practices方面的东西。我一直和大家强调的一件事情,就是说光有OOAD/Patterns相关的理论知识实际上是远远不够的,一定要结合具体的practices才能够凑效,而这里面的大背景则是一系列软件开发所提炼出来的principles(如OCP、DIP、SRP、LSP等等)——这也就是为什么我十分推崇微软的Patterns & Practices项目以及Robert C. Martin所著的经典的Agile Software Development, Principles, Patterns, and Practices的原因之一。

另外,本着与大家交流、向大家学习的principles,如果您对我们的团队建设以及我们的项目开发感兴趣的话,不妨留言给我,我会尽可能的与大家交流——Yes, we're inspired people, and we shall be inspiring the community! Thanks! :-)

posted @ | Feedback (9) |

Tuesday, August 31, 2004

各位MSDN Subscribers,最新一期的MSDN Library October 2004 DVD/CD已经可以从MSDN Subscriber Downloads中下载了(其实早在26日我就从RSS中看到它了,可就是一直没有出现在TOC中——看来MSDN的信息化工作还有待加强:)。

这期DVD中除了收录了截至7月中旬的MSDN Library Online中的内容外,还有一贯精彩的.NET Shows、MSDN TV、MSDN Webcasts以及.NET Rocks等多媒体资源(CD版本的就只有遗憾啦——还好基本网上都可以下载到)。看得出,我是一直十分推崇保持应用最新版本的MSDN Library的,因为这是优秀的IT工作人的基本秘籍之一——随时保持知识更新!你可以不精通每一项新知识,但是你至少应该对业界的各种动态有所了解——因为没有一定的知识面,很难在技术领域有所突破。

对了,如果你对Robert C. Martin也就是Uncle Bob有所熟悉的话,本期收录的MSDN Webcasts中有他主讲的一次Patterns & Practices Live for MSDN Architecture,还在从事企业级架构设计与开发的朋友们不妨聆听一下大师的声音。:)

另外,对SQL Server 2000 Reporting Services感兴趣的朋友也不要错过那两集近三个小时的在线课程录像,内容十分翔实。

听说Visual Studio 2005 beta 1 Refresh with Team System也就快能够从MSDN Subscriber Downloads下载了,这倒是对我有些吸引力——这一块儿东西也该有点儿新气象了吧?!如果你也感兴趣,可以到MSDN Channel 9去搜索一下“team system”,有一个4 parts的video十分精彩——Late Night with the Burton Team。

Well, so much for today's late night blogging! Enjoy.

posted @ | Feedback (9) |

Sunday, August 29, 2004

20天的等待,我们团队终于迎来了千里迢迢从南京北上的sumtec同学——希望我们在一起会创造一段值得回忆的创业时光!

20周的阔别,我又回到了久违的博客堂——却不知道还有多少人记得我呢?:)

20年的卧薪尝胆,祝贺中国女排又一次让五星红旗飘扬在奥运五环的光芒下!

 

我代表我们的团队给博客堂的朋友们问好啦!希望随后团队成员会有精彩的内容与大家分享,与朋友们共同成长!:)

posted @ | Feedback (22) |

Wednesday, April 07, 2004

记得以前思归曾指导我们凡事都该多求一种alternative,看了ghj1976关于在.NET中利用XmlNamespaceManager来处理XPath名称空间引用的方法,又看了Zee关于直接在XPath过滤表达式中利用XPath内置节点函数来处理类似问题的另一种方法,那么我也来贡献一种非常有用的解决办法——利用MSXML中DOM的SelectionNamespaces二级属性(second-level properties)。

如果你还在基于ActiveScript脚本引擎或者COM的环境中操作XML的话,一般你都是使用Microsoft XML Parser(目前主要是用3.0或4.0)这套API,而且大部分时候你会与DOM打交道。在DOM对象上有很多的native properties,如validateOnLoad、async等等,这些属性通常会影响DOM运行时的表现。为了能够更方便的扩展一些新运行时的新属性,MSXML的DOM上提供了setProperty/getProperty的方法,用它们可以为DOM运行时提供基于key/value的额外属性,也即二级属性。

在MSXML 3.0中,为更方便的应用XPath等相关技术,DOM开始支持SelectionLanguage和SelectionNamespaces这两个二级属性。这个SelectNamespaces就是用来指定额外的名称空间映射的,如下:

dom.setProperty("SelectionNamespaces", "xmlns:x='urn:mine' xmlns:y='urn:yours'")
dom.selectSingleNode("/x:foo/y:bar")

则如果dom节点的XML内容是这样的:

<foo xmlns="urn:mine">
  <yours:bar xmlns:yours="urn:yours" />
</foo>

便可以正确的选取到中间一行的urn:yours名称空间中的bar元素(注意本例中XML默认名称空间及名称空间等价的概念)。

这个技术在功能上等价于使用.NET中的XmlNamespaceManager,只是在非.NET环境中(包括在开发InfoPath表单中使用的Microsoft Script脚本环境或者IE的脚本环境中)这还是非常正式的解决方法(效率也会比使用XPath过滤表达式要高效一些)。

希望在你使用MSXML的DOM对象遇到类似问题时有所帮助吧。

posted @ | Feedback (5) |

Monday, April 05, 2004

周末熬夜终于搞定的AOP尝鲜系列文章第三篇一出,就收到了很多朋友的反馈和邮件。首先感谢大家的关注!也衷心谢谢给我指出文章错误、文字疏忽的朋友们!

有一位细心朋友今早给我发来邮件,“批评”我又在简单问题上面犯了糊涂,他所指的是文章中出现的这个语句:

Console.WriteLine("Add: " + Thread.CurrentContext);

其中Thread.CurrentContext属性将返回当前线程所处的执行环境,类型是System.Runtime.Remoting.Contexts名称空间中的Context。一个字串常量怎么可以和一个对象直接相加呢!肯定是我忘了写ToString()了吧?他对此的评语也很有意思,“老兄以为在写JScript呐!”……嘿嘿!一针见血啊!

我回复他,感谢他的热心和仔细(再次:),顺便问了一句:我文中的代码你动手编译过吗?因为我清楚的记得像这样的代码我已经不是写一次两次了——偶的实践经验告诉我:这个语句是可以在Visual C# .NET中编译并正确运行的!不仅如此,我还经常写这样的语句:

string tempFilename = null;
tempFilename += Environment.TickCount + ".tmp"; // 啊?string += int?

或者:

string guidKey = "{" + Guid.NewGuid() + "}"; // 奋特!

再或者:

string logLine = "Log: " + DateTime.Now; // 咣当……

……就像读者所说:这是C#还是JScript啊!?实际上,我也没有在C#语言规范中找到能解释这个现象的理论依据(如果你知道的话希望能告诉我:)。所以我只能说这是Visual C# .NET编译器的一种编译器优化手段造成的side effect(将静态字串相加转化为String.Concat()调用)——不过事实证明,这种写法的效率比起String.Format()还要高(虽然没有人家灵活),甚至有时候比每一个用ToString()还要快!原因是什么?也很容易分析出来,留给聪明的你做个练习吧(可以把你的想法用反馈告诉我)!:)

最后还是按照惯例,做个练习——想想下面这个表达式中对quiz求值将是什么结果?

string quiz = null;
quiz += 1 + 2 + "3" + 4 + 5;

P.S: USE THIS TRICK AT YOUR OWN RISK! AND IT IS NOT RECOMMENDED AT ALL (THOUGH IT'S OFTEN USEFUL AND HANDY DURING DEBUGGING.)

posted @ | Feedback (18) | Filed Under [ .NET Stuffs ]

Sunday, April 04, 2004

 Part III: Context-based Interception Towards a Mini AOP Framework

 

摘要:在本文的上一篇中,我们利用.NET Remoting基础架构中的真实代理/透明代理技术实现了不针对具体类型、具体方法的通用方法调用拦截机制。在本篇文字中,我们介绍可以用于在.NET中实现基本AOP(面向方面编程)的更深入的基于ContextBoundObject机制的相关技术,并结合该技术的优势和劣势提出了一个已经在我们设计的项目中局部应用的一个AOP框架原型思路。

 

BTW: 由于工作调动等各方面的原因,偶的文章也像本年度偶最期待的产品Microsoft Visual Studio 2005一样频繁跳票……不过就在今晚,我终于完成了本系列文章最后一部分的写作。实际上,最近手头的工作已经离所写的内容越来越远,不过回头再鼓弄起这些技术,我脑中仍可激发出阵阵的奇思妙想……我想,这就是技术的魅力吧!希望您读过我的文章之后也能够同我一样被这些美妙的事物激发灵感,感悟个中快乐——只是,不要像我这样熬夜。:)

posted @ | Feedback (0) | Filed Under [ General ]

Wednesday, March 10, 2004

抛开技术细节不说,我今天很想记一种精神、一种态度,请来看看博客堂的“弟弟”博客园上面的一个话题:

关于.Text如何过滤恶意脚本的思考
小题大作:.Text中恶意脚本过滤的新方法

应该是受我文章的“误导”,dudu同学坚定不移的要用些(我已暗示多次并不合适的)新技术、新思路去解决一个很多中手、高手都不屑于解决的问题。他自己钻研文章,多次通过邮件向我提出自己的质疑,与我讨论相关的技术,最后终于成功的从完全不同的角度解决了问题,还发信来让我批评他写的程序……很多朋友问我如何可以成长为高手,这下我可找到教材了。:)

星星之火,可以燎原。一个社区的力量终归还是出自每个人的身上。所以我想,对外的广告是要这么打:Inspired community, inspiring people! 但对内的口号应该这样说:Inspired people, inspiring community!

posted @ | Feedback (9) | Filed Under [ General ]

继续前面的话题,上次给出了个不甚明确的大图画,结果引来众多高手指教,不亦乐乎!这次我想明确一下、具体化一下,就是下面这样一个命题(C#示意性代码):

如果有:

 

[TableMapping("Book")]
public class BookInfo
{
  public BookInfo(int bookID, string title, string publisher);

  public int BookID;
  public string Title;
  public string Publisher;
}

 

怎样将这段代码:

 

public BookInfo[] SelectByPublisher(string Publisher)
{
  SqlCommand command = new SqlCommand();
  command.CommandType = CommandType.Text;
  command.CommandText = "SELECT BookID, Title, Publisher FROM Book WHERE Publisher=@Publisher";

  command.Parameters.Add("@Publisher", Publisher);

  using (SqlConnection connection = new SqlConnection(connectionString))
  {
    command.Connection = connection;
    connection.Open();

    SqlDataReader reader = command.ExecuteReader();

    if (!reader.HasRows) return new BookInfo[0];

    ArrayList result = new ArrayList();
    while (reader.Read())
    {
      result.Add(new BookInfo(reader[0], reader[1], reader[2]));     
    }

    return (BookInfo[])result.ToArray(typeof(BookInfo));
  }
}

 

用这样的统一方式来轻巧的实现:

 

public BookInfo[] SelectByPublisher(string Publisher)
{
  return (BookInfo[])ElegantORM.Execute(Publisher);
}

 

显然,我们需要实现ElegantORM(暂且不论这个codename起的多烂啦:)这个helper。怎么实现最合理?它应该能够支持前文所述的几种常见数据访问组件接口方法的signature,且不仅依赖于使用custom attributes的映射元数据(即可扩展到支持外部映射配置文件等等)……

 

BTW: 在本文下我们不讨论三层结构相关的话题(不过欢迎你把这方面的见解继续发到我的前一个随笔中:),只针对这个具体命题的设计和实现,我很想听到大家的见解并分享自己的心得。:)

posted @ | Feedback (15) | Filed Under [ .NET Stuffs ]

Tuesday, March 02, 2004

前面用透明代理机制实现简单AOP的文章一发,很快收到很多朋友的反馈,其中就有一些朋友提出关于文中所述的透明代理等技术还能够用于什么应用场合的疑问。说实在的,本来我就是想写一些关于这些技术在实际项目中应用的随笔的,可是想到可能会有很多朋友对这些技术的基本原理还不熟悉,这才萌生了先写一系列文章作铺垫的念头(可惜由于工作比较忙的缘故,至今还欠一篇没有写完,还请大家见谅啦,我抓紧时间补上——这篇内容更精彩噢!呵呵)。

 

今天我们又一次把我前面这两篇文字中提到的技术应用在项目开发中,初步反馈还是非常positive的,这里我不妨把问题先说出来,请看过文章的朋友们好好想想,看看如何利用已经学到的知识来解决这个实际问题吧——稍后我会回来说说我的想法(当然如果大家看过偶的文章以后都能够想出这种解决方案的话那当然就最好了:),当然如果你有其他更好的办法,更是欢迎你通过发表评论和大家分享!

 

这个问题和单元测试(Unit Testing)有密切关系(虽然我们的项目还不能算是完全的TDD,但是充分、必要的单元测试还是不可或缺),这里我们还是以简单的例子来说明,比如这里有一个银行服务的接口:

 

interface IBankService
{
  void Withdraw(Account account, int amount);
  void Deposit(Account account, int amount);
  int QueryBalance(Account account);
}

 

这个接口的三个方法的作用很简单:取款、存款、查询余额。我们肯定要开发一个实现这个接口的具体业务操作类;与此同时,由于该服务接口对安全性的严格要求,项目组中专门负责安全规则的部门将以DECORATOR的方式针对该接口的每一个方法编写负责安全保障的修饰类(最终在运行时由组件工厂动态组合)。以这种设计方式,只要确定了服务接口,则两个类的开发就可以充分的并行进行了——真的是这样吗?我们写写看:

 

class BankServiceSecurityGuard: ComponentDecorator, IBankService
{
  void IBankService.Withdraw(Account account, int amount)
  {
    if ( … )
      throw new DailyAmountExceedsException( … );
    if ( … )
      throw new AccountBlockedException( … );
    if ( … )
      throw new PermissionNotGrantedException( … );
   

    ((IBankService)Decoratee).Withdraw(account, amount);   
  }
 
}

 

这个方法的实现就是一个把门儿的逻辑:结合方法调用参数使尽各种必要手段确保只有在符合所有安全规则的前提下才能够把调用转发到被修饰的(即decoratee)对象(也就是IBankService的核心实现类上)。也就是说,开发并测试这个安全保障修饰类的时候最终还是要依赖于一个能够工作的核心实现类,否则就算可以编译通过(还好这种依赖已经被接口隔离开了),你也无法让单元测试通过。我们能不能用什么办法来独立的测试这个修饰对象使之不依赖于一个正确工作的核心实现类呢?换句话说,有没有办法在没有可工作的核心实现类的情况下(这种情况常见于核心类开发周期较长的时候),让下面针对安全保障修饰类的单元测试代码正常起到单元测试的作用(一是驱动开发过程、二是形成软件规格说明、三是跟踪软件缺陷……)呢(我们先假设Account类是已经存在且经过充分测试的——其实对该类的隔离测试也是类似的道理)?

 

[TestFixture]
public class BankServiceSecurityTest
{
  private IBankService bankService;

  [SetUp]
  public void Initialization()
  {
    bankService = new BankServiceSecurityGuard(???);
  }

  [ExpectedException(typeof(DailyAmountExceedsException))]
  public void TestForExceedingDailyAmount()
  {
    AccountState state = AccountState.DailyAmountReached;
    Account account = new MockAccount(state);
    bankService.Withdraw(10);
  }

  public void TestForNormalWithdraw()
  {
    AccountState state = AccountState.Normal;
    int initialBalance = 1000;
    Account account = new MockAccount(state, initialBalance);
    try
    {
      bankService.Withdraw(10);
      Assert.IsTrue(coreBankService's been called for Withdraw(10));
    }
    catch(Exception ex)
    {
      Assert.Fail("this call should not throw exception");
    }   
  }
}

 

简单说,我们要测试两类情况:一类是应该抛出安全异常的情况(TestForExceedingDailyAmount),在这种情况下,我们需要确信在给定的前提下必将产生指定的后果(比如特定类型的安全异常);另一类则是不该抛出异常的情况喽,在这种情况下我们不光要确信没有安全异常抛出,还要进一步确保方法调用确实被正确的(包括传入参数的值)转发给内部的指定方法了(否则一是有可能放过人为的安全后门,也有可能漏过修饰类忘记或错误调用被修饰类的情况)!

 

我已经听见有人高呼“简单简单”啦……写个mock对象不就结了!好,现在请你来为系统中数以百计的安全修饰类以及其他功能修饰类编写一一对应的mock对象好啦(幸好我们的接口都不是很fat,每个里面平均也就不到10个方法吧:)!反正我可真是一个超懒的程序员啊……你能给个省事儿又简单的方案吗?

posted @ | Feedback (11) | Filed Under [ .NET Stuffs Agile/XP ]

Monday, February 23, 2004

在很多朋友的一再催促下,偶关于.NET中实现基本AOP编程的第二篇文章终于在上个周末出炉了。这里首先多谢在大周末帮我做review的朋友们,多谢你们的火眼金睛,群众的眼睛真的是够靓啊!!:)

 

在写这篇文章的时候,我从MS的e-CompanyStore新收到的微软无线桌面套装精英版给我添了不少乐趣,可也带来了不少烦恼。键盘的手感虽比不上我那台老的IBM金属键盘,却仍是舒适、便捷得很(尤其是那条皮质的腕托,还有左面的纵横滚轮——可惜不能按!);鼠标虽然不是想象中的皮革版(明明在买它的时候看到图上照片是皮的嘛!上当!),不过仍是十分的漂亮且舒服。不过要命的是,无线的键盘经常会“批发”多余的按键,让人防不胜防(我按一下DELETE键没准给我发出6个!十分钟至少一次的说——怀疑是不是买到了次品?别的用微软无线键盘的朋友有遇到这个问题吗?);另外F/Lock锁定键的默认状态是快捷功能而不是功能键,且没有直观显示,难道F键要下岗了吗?这一点实在有些自作聪明了(其实这些双态键不妨设计为机械式的锁定按键,用键位的高低来显示状态,既不费电,也很直观,而且可以保持用户的习惯——顶多就是内部走线复杂一些,可也对得起这价钱啊)。还有,F功能键从4键x3组变成了3键x4组,更是让我头疼不已(所以没有把它拿回家用——回家我就是开开Flight Simulator 2004,它的键位可是按照4个一组来设计的,所以基本上是没法用了——不过Visual Studio受影响也不小啊)!看来这款“新颖的”产品应该更适合于那些从来没有用过传统键盘布局的朋友吧——不然你且得适应一下呢(而且适应以后就更用不惯不是这样设计的键盘了,还真是微软的一贯风格嘿)。

 

嗯,总之呢,写文章还是挺辛苦的,不过只要能有几个朋友读过文章后有所收获的话,我也就心满意足了。当然,如果你觉得有什么不吐不快的意见和建议啥的,也可以通过博客堂随时与我交流(写评论或者用联系链接),多谢啦!:)

posted @ | Feedback (8) | Filed Under [ General ]

先来看这段NUnit测试代码,我们希望用反射机制在运行时访问一个对象的枚举类型的域或属性:

 

[TestFixture]
public class PaymentInfo
{
  public enum PaymentType
  {
    Cash, CreditCard, Check
  }

  public PaymentType Type;

  public void Test()
  {
    PaymentInfo payment = new PaymentInfo();
    payment.Type = PaymentType.Cash;

    System.Reflection.FieldInfo enumField = GetType().GetField("Type");

    int paymentTypeInt32;

    paymentTypeInt32 = (int)enumField.GetValue(payment);
    Assert.AreEqual((int)PaymentType.Cash, paymentTypeInt32);

    enumField.
SetValue(payment, paymentTypeInt32);
    Assert.AreEqual(PaymentType.Cash, payment.Type);
  }
}

 

实际上运行测试时发现在标红的这行上抛出一个异常:“对象类型无法转换为目标类型”。究其原因,原来是因为CLR的反射机制不允许枚举类型与整数类型之间隐式转换。不过C#编译器还是允许我们通过强制类型转换的语法来进行两者间的显式转换。

 

在这个测试中,使之通过的办法其实非常简单:把划线部分强制转换为枚举类型即可,如:(PaymentType)paymentTypeInt32。可问题是:在运行时如何动态转换类型呢?比如说我在写ElegantDAL的时候,需要将从数据库读出的一个类型为int的数值写入到要返回的对象的一个枚举型字段中,此时我只有fieldInfocolumnValueresultObject,然而写成fieldInfo.SetValue(resultObject, columnValue)就会出现前面提到的错误,可是我又只有一个运行时的Type信息(fieldInfo.FieldType),我又不能写成fieldInfo.SetValue(resultObject, (fieldInfo.FieldType)columnValue)……

 

只好将这种情况列为一个特例处理,而我们的救兵则是Enum.ToObject()方法——你知道有更好的方法解决这个问题吗?

 

BTW: 很多朋友写信来问偶承诺的下一篇文章什么时候才能搞定,其实我也不想拖,可最近工作确实比较紧张,而我的最佳写作时间又都在深更半夜(之前还要热身进入状态),所以迟迟不能结稿。目前其实已经写了好多(已经和第一篇长度差不多了),可是发现要写的内容还太多(也许是我写得太细了,因为我的目标不仅是讲how,更多的是whatwhy),所以现在决定把本篇文字再细分为两部分(上篇只讲TP/RP+MBR对象;把CBO相关的内容放到下篇再写),以便能够尽快让大家看到新的内容——嗯,争取周五release吧。至于还有朋友希望我能够深入写些关于ElegantDAL的设计与实现细节,这个留待稍后吧,正好我们将要整理的项目文档中也要有这一块内容(现在在我们这个企业级项目中用得还真挺好),到时候一起写啦(希望能够和canyue尽快完成第三版本的重写工作,目前使用的是第二版)。

posted @ | Feedback (5) | Filed Under [ .NET Stuffs ]

Saturday, February 07, 2004

 

昨天,我第一次在实际开发工作中体验到了结对开发的魔力(以前也就是两个人一起研究问题而已),那确实是一种令人神怡且有效的工作方式——至少it works extremely well for me!

 

在下午三四个小时的开发工作中,我和我的搭档一起解决了已经困扰了我很久的几个问题,并令人激动不已的在NUnit中点绿了单元测试中所有的测试项(就这几个问题我已经拖了半个月了还没能解决)——这意味着我可以放心的开始对代码进行重构(而不是重写)了(本来一直没有信心这样做,因为到处都有红灯,而每一个红灯都牵扯到很多的问题)……体会一:结对编程消除了最影响开发人员情绪的孤独感,进而重新迸发工作激情,大大提升工作效率和质量。

 

其他同事都下班以后,我们简直是进入了一种turbo的开发状态——我们先是一起总结了前面开发中最不爽的几个环节,并一致认为一定有好办法能够让开发变得更简单一些——于是我的灵感突然涌出,我告诉他“我将把让我们的开发变得繁琐的这些类都删掉,用一个custom proxy搞定所有的类”!“听上去不错,可是……有这么好的事吗?”“看我的——你就帮我瞧着那些绿灯吧!”当然,我在下手之前还是像唐僧一样把改造的思路说了几遍,当我确信搭档已经明白我的意图后,我开始动手了:三下五除二,我先把一个private成员提为internal(本来它只是被自己的一系列public gateway methods访问的,现在我要让它能被custom proxy使用),并马上写出了这个custom proxy,用它替代了以前那些繁琐的method gateway和那个接口实现类,最后,我到组件工厂中把实现接口的类换为由custom proxy构造的transparent proxy——NUnit.Run! Bingo! We got it! 体会二:两个人的智慧确实大于一个人,尤其当两个人从两个方向共同考虑同一个问题的时候——当然,彼此持续、默契的交流是必不可少的。

 

现在回头想想,这一下午的进展真的是匪夷所思,我又一次亲身体会到了结对编程的魔力和乐趣——当然,坐在我对面的项目经理估计也体验了他工作以来强度最大、持续时间最长的人造噪音。不过我想说的不是sorry,而是thanks!谢谢你给我们这样的机会,这是在其他团队中难以想象的事情。我相信你也将亲眼看到我曾向你描述的关于结对编程的种种好处。另外,thanks to my partner canyue!和你一起工作真的很开心,我在你谨慎的约束下变得更专注也更有效率,而你也能在看我开发的时候学到更多的东西。Cool!

 

如果你也有类似的敏捷开发经历,不妨写在评论中与我们分享——无论是成功的经历还是失败的经历,我想都会很有参考价值。其实关于敏捷方法论(尤其是其中最著名的极限编程技术),我也有很多实践经验和想法,希望以后有时间能够在这方面也写些心得体会吧——不过当务之急,还是先把腾苦不堪的颈椎疼痛消灭掉吧!现在开车都成问题了……

posted @ | Feedback (18) | Filed Under [ Agile/XP ]

Thursday, February 05, 2004

动态属性依然是类的一部分?——I really do NOT think so!
http://blog.joycode.com/microhelper/posts/12394.aspx#12408

不可否认attribute的强大 但它必须依赖程序而存在的事实就注定了它的脆弱?——What do you think?
http://blog.joycode.com/jgtm2000/posts/10064.aspx#10220

“具体情况、具体分析”——就算所有的技术都是死的,人还是活的……

posted @ | Feedback (35) | Filed Under [ General ]

Monday, February 02, 2004

摘要:本文通过一个高度简化的示例,逐步的揭示在面向对象设计与开发中面临的若干问题,并导出软件开发对AOP(即面向方面编程)思想的需求。这是文章的第一部分,主要利用面向对象编程技术与设计模式给出解决问题的一个思路,并分析该实现面临的问题,以此形成对面向方面编程技术的直观需求。

 

欢迎大家提出自己的观点、意见和建议,文章的下半部分正在写作中。谢谢!:)

 

http://blog.joycode.com/jgtm2000/posts/12103.aspx

posted @ | Feedback (1) |

Tuesday, January 13, 2004

I found this tip in an incidental and quick Rebuild when I suddenly notice the extra comma but compiler didn't report any error before I could be able to abort it! I thought it were a nice and kind bug in C# compiler but it's NOT, as shown in C# Language Specification 12.6 (array initializers), 14.1 (enum declarations), 17.2 (attribute specification)!

As in the following examples:

object[] x = new object[] {a, b, c,}

and:

[Obsolete, Flags,]
public
enum Y
{
  A,
  B,
  C,
}

which allows you to easily reorder the declaration list by using ^L...^V or Shift-Alt-T in VS.NET:

[Obsolete, Flags,]
public
enum Y
{
  A,
  C,
  B,
}

Isn't?this useful and flexible??笑脸 And finally, in keeping with?tradition of leaving you a quiz, does this work for method parameter declaration?

void Method(string x, int y,) {} // WORK OR NOT?

posted @ | Feedback (6) | Filed Under [ .NET Stuffs ]

在使用Visual Studio .NET 2003Visual C# .NET产品时发现一个问题(我用的是英文版本,在同事的中文版本中也存在同样的问题):SqlParameter.IsNullable属性无法在IntelliSense中正确列出(相反,其getter/setter方法却被错误的显示在成员提示中,即get_IsNullable和set_IsNullable)。大概看了一下IsNullable属性和其他属性成员的差别,看来是因为被打上了DesignOnlyAttribute的原因。然而即便如此,属性的两个hidden getter/setter也不应该显示出来啊(这么写也肯定编译不过去)。

 

同样的问题也存在于使用OdbcParameterOleDbParameter时,因此问题可能是双方面的:一是VC#.NETIntelliSenseDesignOnlyAttribute的支持似乎有问题(隐藏属性是对的,因为DesignOnly说明该属性只为设计时期所用,运行期代码中是不该引用的;然而不能因此就把getter/setter方法给列出来);另一方面,从IsNullable的文档来看,这也不应该是一个DesignOnly的属性,因此可能是.NET Framework的一处疏忽。

 

当然,也有可能是我有什么细节不知道的说,如果是这样请一定告诉我啊……

posted @ | Feedback (4) | Filed Under [ .NET Stuffs ]

mmkkblog上看到一个关于Object.GetType()的观点,想作些补充,和各位分享(原文请见http://v-instru.com/blog/posts/220.aspx)。

 

由于所有的类都隐式继承Object,并且获得Object.GetType(),不过由于派生类可以new一个新的GetType()遮蔽Object.GetType()比如:

public new Type GetType()

{

return typeof(System.String);?

}

这样的话如果instance想调用GetType()来获得精确类型的话得到将是System.String,而不是期望得到的,相反通过Type.GetType(string)这种形式可以绕过这个不确定因素,Type.GetType()是否应该是一种更好的习惯呢?

 

很显然,按照平时使用GetType()