JGTM'2008 [MVP]

Thinking in WPF...
随笔 - 36, 评论 - 453, 引用 - 16

导航

标签

每月存档

最新留言

广告

【第1页/共3页,44条】
首页
前页
1
2006年10月29日

[DRAFT] Ease API Designing with .NET Interception Technology

There will be another series of articles discussing solving real problem (during our R&D process) with .NET's interception technology (specifically, RealProxy/TransparentProxy for intercepting method invocation for mocking API implementation).

With technology discussed in this installation, you will be assisted a lot during API designing phases, eliminating thousands lines of codes in effect to achieve the same runtime behavior (not counting performance) and that allowing you to incrementally implement your library.

Phase O: Initiative/Goals

1. get bored writing tedious fields, property getters n' setters, etc?

class X
{

   private int _a;

   public int A

   {

     get { return _a; }

     set { if (_a != value) _a = value; }

   }
}

Sure we could define interfaces, like this:

interface X
{
  int A { get; set; }
}

But without at least one concrete implementation, this code just cannot run and use.

2. Getting bored writing trival constructors merely to initialize various properties/fields?

class X
{

   public X() { _a = 0; }
   public X(int a) { _a = a; }
}

3. In an AJAX world, everything's changing incrementally. how to track object's changes and deal with only changed parts?

X x = new X();

DetectChanges(x); // -> nothing

x.A = 123;

DetectChanges(x); // -> x.A changed to 123

CommitChanges(x);

DetectChanges(x); // -> nothing

x.A = 123;

DetectChanges(x); // -> nothing!

x.A = 1234;

DetectChanges(x); // -> x.A changed to 1234

4. And to serialize such changes as XML/JSON, XmlSerializer is just no-use in such situation.

X x = new X(); // we want: <X />

x.A = 123; // we want: <X a="123" /> or X {a:123;}

x.B = 456; // we want: <X a="123" b="456" /> or X {a:123; b:456;}

Phase I: A Mock Factory

1. Simple Type Implementation

interface M

{

  int A { get; set; } 

}

void Test()

   M m = MockFactory.Create<M>();

   Debug.Assert(m!=null);

   m.A = 123;

   Debug.Assert(m.A==123);

}

2. Nested Type

interface N

{

  M B { get; set; }
}

void Test()

{

   N n = MockFactory.Create<N>();

   Debug.Assert(n!=null);

   M b = n.B;

   Debug.Assert(b!=null);

}

3. Improvement and Refactor

a) Get unassigned ValueType/ReferenceType?

b) DefaultValue?

Q. Constraint while set value? CannotBeNull(), Within(min, max), etc.

4. Implement of GetType(), ToString(), GetHashCode() and Equals()

5. Delegate inner method to external extension concrete class?

interface X

{

  void F();

}

class ExtensionX: X
{

   void F() { Debug.Assert(true); }  
}

void Test()

{

   X x = MockFactory.Create<X>();

   x.F(); // -> NotImplementedException

   MockFactory.RegisterExtenstionClass<X, ExtensionX>();

   x.F(); // -> Okay now

}

6. Refactor General-4 method to ExtensionBase:

class ExtensionBase
{

   Type GetType() {}
   string ToString() {}
   int GetHashCode() {}
   bool Equals() {}
}

How an extension instance could get "context" information about target instance?

Phase II: A Mocked Factory

1. Getting bored with MockFactory.Create<T>()? Let's define our own factory interface!

interface Factory

{

  M CreateM();
}

void Test()
{

   Factory factory = MockedFactory.Create<Factory>();

   Debug.Assert(factory!=null);

   M m = factory.CreateM();

   Debug.Assert(m!=null);
}

2. How about non-default constructor?

interface Factory
{

   M CreateM(int A);
}

void Test()
{

   Factory factory = MockedFactory.Create<Factory>();

   Debug.Assert(factory!=null);

   M m = factory.CreateM(123);

   Debug.Assert(m!=null);

   Debug.Assert(m.A==123);
}

Main challenge here is to locate property member for setting value when there is multiple interface inheritance. For instance,

interface X
{

  int A { get; set; }
}

interface Y: X
{

  int B { get; set; }
}

From typeof(Y) you can only get a member information (more specifically, PropertyInfo) for name "B" but not "A", because "A" is declared in interface X. So, you need to find a property info from a combination of Y and its interfaces implemented, in this case, X.

Q.

interface M

{

  int A { get; set; }
  int B { get; set; }
}

interface Factory
{

   M CreateM(int A);
   M CreateM(int B); // <-- conflict with CreateM(int A)
   M CreateM(int A, int B);
}

Phase III: Solve a Real Problem

1. Get changeset during object operation:

interface M

{

  int A { get; set; }

interface Factory
{

  M CreateM();
  M CreateM(int A);
}

void Test()

{

   Factory factory = MockedFactory.Create<Factory>();

   M m;

   // create "clean" instance

   m = factory.CreateM();

   PropertyInfo PropA = m.GetType().GetProperty("A");

   PropertyInfo PropN = m.GetType().GetProperty("N");

   Debug.Assert(ChangeDetector.HasChanged<M>(PropA)==false);

   m.A = 123;

   Debug.Assert(ChangeDetector.HasChanged<M>(PropA)==true);

   // create instance with

   m = factory.CreateM(123);

   Debug.Assert(ChangeDetector.HasChanged<M>(PropA)==true);

 }

posted on 2006-10-29 20:44:00 by jgtm2000  评论(0) 阅读(830)

 
2006年01月01日
新文章的骨架素材,请大家预览并提出宝贵建议(这是素材,还没有串接成文)。

文章的标题拟为:Thinking in PPP: DIP, Adapter and TDD

首先的问题是,DIP是什么?

依赖反转原则:高层模块不应该依赖底层模块,两者都应该依赖抽象;而抽象不应该依赖细节,反之细节应该依赖于抽象。

引用针对包的DIP原则:SDP+SAP 也即朝着稳定和抽象的方向进行依赖。

如果X->Y,那么我们需要评估Y的稳定性以确定该依赖的稳定程度:

class X
{
?public void F()
?{
? ?Y y = new Y();
? ?y.G();
?}
}

class Y
{
?public void G() {}
}


因为依赖者的稳定性不可能超过被依赖者的稳定性,如果被依赖者不太稳定,则依赖他的其他类也就不可能太稳定,这不是我们希望得到的结构。

如果被依赖者的稳定程度非常之高,则这个依赖也是可以接受的;否则,有必要提取一个相对稳定的抽象以改善这个依赖。

通过运用重构的提取接口技术,从Y提取出I,并让Y实现I——此时,Y通过realize关系静态依赖于I,其稳定程度不会高于I;

class Y: I
{
?void I.G() {}
}

interface I
{
?void G();
}

接下来,改变X以使之从直接依赖于Y变为通过I间接的依赖Y:

class X
{
?public void F()
?{
? ?I i = new Y();
? ?i.G();
?}
}

为什么说X是间接的依赖Y?因为如果没有Y,X连编译都不能通过

怎么办?通过使用运行时动态对象工厂来消除X对Y的静态依赖(这里图省事,我们直接使用.NET
Framework中的Activator来创建对象——实践中一般都是引入一个专用的组件工厂类):

class X
{
?public void F()
?{
? ?I i = (I)Activator.CreateInstance("Y, Demo");
? ?i.G();
?}
}

现在就算你把整个Y注释掉,程序的静态结构也是完整的——也就是说,我们已经消除了X和Y之间的静态依赖,降低了X和Y之间的耦合。

现在的结构是:X->I<-Y。被依赖的核心变成了I——再次评估此结构,我们发现X和Y的稳定性都不会高于I,因此如果I不稳定,一切都是徒劳。那么I到底稳定吗?要看I是如何而来。因为I是从原本不稳定的Y中提取的,如果抽象的合理,抽象出来的I确实代表了Y的稳定的本质,那么I就有一定的稳定性,那么该结构就相对合理了。可是如何能够保证I一定是合理的抽象呢?这就引出一个问题,I到底是属于谁的?

请问X->I<-Y到底是(X->I)<-Y,还是X->(I<-Y)呢?

事实上,对抽象的定义实际上是由对抽象的依赖者来确定的,该抽象的实现者只能去无条件的接受这个抽象,提供对这个抽象的实现——比较经典的说法也就是:接口是属于它的客户而不是具体实现的。

从包的层次讲,如果你需要分离X和Y到两个包中,那么你会把X和I放在一个包中呢,还是把Y和I放在一个包中?显然 ,X应该是和I在一个包中,也只有这样才能保证即便没有Y的存在X也是可以编译通过而独立存在的。此时,Y的包需要通过引用X(实际上只是I)所在的包即可为之提供一个具体的实现。

问题又来了,如果Y并不在我的控制内,也就是说即便我抽象出了I,Y也不可能实现这个接口(比如说Y是一个第三方组件)——这时候ADAPTER就是你的救星了。通过编写一个适配器A,使之对X提供I的实现,并利用Y来提供这个实现,我们可以达到这样的结构:X->I<-A->Y。如果要分离包,原理和前面是一样的。

很多的ORM工具,可以帮你由数据库结构导出一个对应的对象结构,然后你的应用程序就去依赖这个对象结构进行开发了。问题是,本来数据库结构的稳定性就不好,由这个不稳定的东西"直接"映射出一个静态结构,其稳定程度不可能高于它的源,因此你去依赖这样一个同样不稳定的对象结构,除了编程上面可能要略为方便一些以外,从结构的角度看简直就是一塌糊涂——因为这违反了DIP原则。

从大的架构来看,一个企业级应用程序是依赖于特定的数据库的(这里的特定有两层含意:一是提供数据库功能的服务器;而是数据库本身的数据结构和功能)。而大量的开发经验告诉我们,数据库是非常不稳定的环节之一。如果整个应用程序都依赖这个不稳定的数据库,则整个应用程序的稳定性就好不到哪儿去。怎么办?通过引入稳定的抽象反转这种依赖关系。我们将应用程序对数据的所有需求都封装到一个稳定抽象的接口中,然后令数据库去提供这个抽象接口的一个具体实现。可是数据库是一个外部软件实体,我们不能让它实现我们提供的抽象接口,于是我们写了一系列的所谓数据访问组件,里应外合,对应用程序提供一个满足其所指定的抽象接口的具体实现,而这里的实现是委托给后面的特定数据库来完成的。结构:(App->DAI)<-DAC->DB。

再来看应用程序使用WS的例子:VS.NET可以帮你从WSDL生成一个PROXY(在应用架构中也即Service
Agent,SA)。众所周知,每当WSDL变化的时候(你说了,不是说服务接口发布之后就不许变了吗?!那当然是比较理想了,然而现实就是这么无奈啊!),你需要Refresh
Web Reference,那么VS.NET就会把这些变化重新反映到为你生成的SA中,而你的应用程序可是直接依赖于这个SA的!因此,你不得不去更改你的客户代码。

应用本文所述原理,应用程序应该首先确定为实现自身功能所需要的抽象,然后让VS.NET从WSDL中创建一个PROXY出来。但是这个PROXY并不能以应用程序所定义的抽象来提供具体实现,于是我们可以写一个Service
Agent,让他来适配两边不一致的接口。

OOAD就是通过分析一个可以工作的动态逻辑(dynamic
logic,也可以称之为一个算法)所需的超越本质逻辑的软件特性(如可维护性、可扩展性、灵活性等等)通过重构将其转化为一个可以达到逻辑等价的动态逻辑的静态对象结构。

无论结构变得有多复杂,其运行时的动态逻辑不应该发生超乎本质的改变;

通过重构而带来的结构复杂性一般而言都会在不改变本质动态逻辑的前提下增加开销,因此软件设计人员的主要能力也是去正确的评估增加的开销和带来的利益的关系;

DIP带来很多好处

首先,它帮助上层模块满足了OCP(开闭原则),这样上层逻辑就能够避免受到具体实现细节变动的侵扰而保持更高的稳定性。

DIP在软件组件之间引入稳定的抽象从而降低模块之间的耦合,大大提高了软件组件的可测试性;

考虑DIP使得需求抽象先于实现细节而确定,有助于实现并行开发,有效的增强了团队的协作程度,提到了开发效率。

TDD帮助开发满足DIP要求的软件

因为TDD强调从客户的角度考虑代码的编写,这和DIP中由分析客户代码需求而提取其所依赖的核心抽象是一致的。

posted on 2006-01-01 01:04:00 by jgtm2000  评论(2) 阅读(1222)

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

posted on 2006-01-01 00:46:00 by jgtm2000  评论(28) 阅读(7474)

 
2005年05月20日

随着中国日渐成为世界最大的移动通讯与应用市场,一大批中小型移动增值应用软件商也迅速崛起,并奋力挖掘着也许是继互联网之后最大的商机。然而,移动应用市场并不简单,软件开发商所处的是一个十分复杂的产业生态环境。如何在这个环境中寻求商机,稳步发展,并在这个竞争激烈的巨大市场中站稳脚跟,是每个移动应用开发商必须要想清楚的事情。

让我们首先来看移动应用产业生态环境中的各个角色吧。

首先是移动网络运营商。通过运营商定制手机的现象我们可以看到,移动网络运营商正逐渐成为产业链的主导者,因此移动应用开发商首先要考虑移动网络运营商的根本利益。在移动市场刚刚形成的时期,用户主要以高端人群为主,体现在每用户平均收益(ARPU)上是比较乐观的。但是随着市场规模的日益扩大,手机终端设备的成本急剧下降,再加上人们对低廉实用的短信业务的认同,市场中涌入大量低端用户,这些用户极大的拉低了对运用商意义重大的ARPU值。因此运营商现在考虑的一件十分重要的事情,就是如何能够在这些低端用户身上挖掘更多的利润,这也正是对于运营商而言移动数据增值应用的意义所在。那么作为移动应用开发商,我们如何能够帮助运营商提供最终用户更好的数据增值业务,从而提升其ARPU值呢?

手机设备制造商在这种引导力的作用下,也必然会主动地考虑如何量身定制符合运营商思路的终端设备。彩屏手机的迅速普及就是为了迎合彩信这一无线增值应用,而智能手机(包括2.5G/3G的手机)作为未来最重要的数据增值业务的运行平台,也正逐渐成为引领市场潮流的重要力量。然而,随着智能手机软硬件平台的逐渐统一,各制造商的产品的差异性越来越小,因此增值应用软件就成为手机差异性优势的一个重要来源。那么作为移动应用开发商,我们如何为手机设备制造商提供更多的卖点,从而提升其差异性竞争优势呢?

手机平台开发商,大多也同时作为软件开发工具提供商,是提供手机设备制造商以及移动应用开发商一个统一、开放的软硬件应用开发与运行平台的厂商。他们考虑的当然是平台的普及性,越普及的平台就会产生越多的授权销售,而越好的开发工具将越能促进移动应用的诞生,反过来近一步巩固平台的市场占有地位。那么作为移动应用开发商,我们如何最大限度的利用软件开发工具提供商提供的平台应用开发工具,在适当的平台上开发出更好的应用,从而促进手机平台开发商的市场地位呢?

简单的分析之后,我们开始考虑how-to在这样一个复杂的、环环相扣的产业链中找到自己合适的位置,并逐渐融入到这个产业链中生根发芽发展。结合我们自身创业摸索的经验和一些心得体会,我们想可能主要有以下这样几点:

首先是积极地创新,这是非常重要的。单纯的模仿别人已有的应用模式是不够的,一定要能够深入挖掘最终用户的需求,想在用户前面、做在对手前面,成为市场的开拓者和引领者。这一点而言,并不是只有大公司才可以办得到。相反,倒是只有三、五个人,但又极具创造力的团队更容易做到这一点。我们有一个很实用、也很顺理成章的建议,那就是每一个从事移动应用产品设计与研发的人都应该自己首先拥有一台智能移动终端设备,这样子才能够随时随地的更有效的发现生活中、工作中、娱乐中的实际需求。

第二个体会就是,要注重积累和培养和产业伙伴之间有序、高效的沟通,利用价值链自身的利益驱动力借力打力,从而达到四两拨千斤的效果。很多像我们一样刚起步的移动应用软件开发创业公司可能都有这样的体会,那就是经常干使劲儿却不见效,就好像开车时候猛轰油门却不见车走——因为没有挂上档。和产业伙伴的沟通就是“挂档”的过程,通过找到彼此的利益结合点,按部就班的找到一档、二档乃至更高档的位置,从而实现顺利起步、逐渐加速的效果。

第三点,一定要选择正确的时机进入市场。俗话说:赶得早不如赶得巧。我们想到前面、做到前面,目的是更好的把握未来的市场机遇。但是,这并不是说要不顾一切的赶进度、拼市场,因为大多数中小应用开发商并不具备这样的实力来抵御大规模挺进市场的风险。比如说即将到来的3G大潮,我们现在为之做好一切准备,但并不意味着一定要就此下海——国家还没确定的事情,你着急也没有意义。我们能做的,就只有令万事俱备,这样就可只待东风了。

再一点,在市场机会初步形成后,可以在适当的时机寻求资本市场的支持,加速市场推进,从而为投资人和公司获得更大的收益。这就要求移动应用开发商能够具备资本市场的意识,将自身的核心价值通过资本市场转化为实在的市场价值,这也是一个很重要的手段。

总结一下,我们认为,对于移动应用领域的软件开发商而言,最为重要的一点,就是要在充分理解和研究产业价值链的基础上,利用自身的优势和想法,形成与产业共嬴的商业模型,和谐的、深入地融入产业的价值链中,并主动地成为产业发展的一分子。

posted on 2005-05-20 01:44:00 by jgtm2000  评论(1) 阅读(1388)

 
2005年01月01日

2004年对于我来讲,是一个既平凡又重要的一年。说平凡,是因为这样的人生道路已经走了十好几年,已经习以为常了;说重要,则是这一年来发生了许多意料之外但也情理之中的事情,而这些事情也许正在改变着我未来的道路。

2004年,是我编程序的第18个年头了——这在我身边大多数程序员眼里看来,已经是一个不可思议的跨度了。大概也是不怎么值得骄傲,就像谁也不好意思说自己“小学上了10年”一样,明摆着是屡战屡败、毕业不得啊!不过,也许微软公司就很喜欢像我这样执着的程序员吧,今年初我荣幸的被微软公司授予最有价值专家(MVP)荣誉。然而对我而言,这并不是什么大不了的事情(所谓情理之中)。真正能谈得上也许会改变我未来之路的还是——创业。

我的创业之梦做了已经不是一年两年了。我相信,早在互联网热潮席卷全球的时候,创业的理念就已经走进了每个有志向、有能力也有激情的年轻人的心中。而当时,虽然我没有任何创业资本可言,但我还是在职业与事业之间开始了曲折的摸索。在这期间,我涌出了五个创业计划(包括其中一款在海外成绩还算不错的共享软件),也学习了和创业相关的许多知识——虽然没有一个算得上真正成功,但是成功本也不是我既定的目标。按照做软件的说法——这些都叫原型,都是为了探索和积累。直到今年,也就是开始创业梦想后的第四个年头,我终于带着我的第五套创业计划和一帮激情兄弟正式扬帆启程了……我们的船名叫“无线视通”。而我,正是它的舵手——首席技术官(CTO)。

CTO是什么?CTO做什么?CTO怎么做?这是我在这几年来一直学习、思考和探索的问题。以我现在的理解比喻一下:CTO就像是船的舵手;CTO要在启航前确定船的目标并时刻保持船的航向;当然,在前方发现暗礁的时候也要灵活应变、化险为夷;光靠舵手而没有动力的船也是不可能前行的,因此CTO要集结更多人的力量、燃烧激情、共同奋斗抵达胜利的彼岸。然而这里面最重要、最根本的一点,还是CTO要兼具远见卓识和脚踏实地的双重作风。这就要靠所谓CTO的“第三只眼”了。

CTO的“第三只眼”既是可以高瞻远瞩的战略望远镜,也是可以明辨秋毫的战术显微镜。通过这只眼,CTO既要看清远景进而制定适宜的战略目标,也要因地制宜、审时度势的看清眼前的情况,并依此制定具体行动方案,最终达到既定战略目标——可见这只眼睛对于CTO而言事关重大,但这只眼睛也并非与生俱来。回想起来,这么多年我做程序员的经历的确让我的这“第三只眼”看得更远、更清楚。因为做一个程序员,一定要具备几项最基本、也是十分重要的能力。

首先是学习能力。程序员需要在实践中不断完成一项项具体的编程任务,比如说访问个网络啦、打印个图像啦、等等。谁也不可能在一开始就学会所有用得到或用不到的编程技术,因此程序员要有快速、持久的学习能力。这些能力可以让你在需要的时候获得你所需要的能力,而这些能力可以让你走得更远、也看得更远。试想,一个程序员,只是学会一些片断皮毛,怎么可能会有更宽广的见识,甚至为自己指引前进方向呢?方向一定是在不断的、广泛的学习过程中摸索、感触而来的。在这18年的程序员生涯中,我的学习能力让我能够积累足够的知识攻克一项又一项的难关,也让我把这些知识贯穿起来,将全局、远景看得越来越清楚。

接下来,程序员要有分析问题的能力。在编程的时候,为了解决同一个问题,经常会有多个选择摆在你的眼前——尤其是对于具有良好学习能力的人而言。这就面临一个选择的问题,这就要求程序员不断地去从各种各样的可能性中结合自己要完成的任务目标选择一条最佳的途径——长期的磨练增强了程序员分析问题的能力,而这个能力对于CTO的第三只眼而言,就是能够从无数的机会中选择出最佳战略方向的能力。战略是什么?战略就是要做什么、不能做什么。没有分析问题的能力,就极有可能犯下“做不该做的事情”、或“没做该做的事情”这样重大的战略错误。

但有了分析问题的能力还不够,程序员需要扎实的动手能力,也就是解决问题的能力。这可能也是我最突出的能力之一了。俗话说,做事情不能眼高手低,光有第三只眼高高在上、指引方向,而不能够把战略意图和战术思想实践,那么实在是没有太大意义。反思这些年我一路踏着键盘走过来,无论遇到什么问题,总是动手、动脑相结合,在实践中思考、在思考中实践,最终解决一个又一个问题。前面说过,CTO也不可能妄想一帆风顺走直线到达目标,如果真是这样的话也太幸运了。在实现战略目标的过程中,一定会有一个又一个现实的问题横在我们前进的道路上,等着我们去解决。CTO固然要有领导能力,但能有实际解决问题的能力则更是如虎添翼——毕竟到了关键时刻还是要亲自披挂上阵的嘛——尤其在创业阶段。

最后,但也是很重要的一点,我可能是一个比较爱与人沟通、喜欢与人交流的积极乐观型的程序员。这种沟通与交流的能力对于一个担任企业决策与执行核心的CTO而言,同样是至关重要的。对外,我要向合作伙伴与客户传达企业理念,这需要积极与平等的沟通;对内,我要把企业的战略设想与战术部署渗透给每一个成员,这需要广泛而深入的交流。想想看,作程序员的时候与老板沟通、与同事交流时候的经验和技巧也都派上了用处啊!

每当回想起自己过去写程序的激情时光,我总是暗自庆幸,庆幸自己能够在很小的年纪就对这样一个有趣味也有意义的职业如此热爱。我甚至没有想到,就是这样一股发自内心的热爱,让我一编就是18年。更让我庆幸的是,这18年的程序员生涯已经对我一生的发展起到了如此重要的作用,让我的路越走越宽、越走越远。而看着一些做过两、三年就开始寻求其他道路的程序员朋友,我感觉很遗憾——不过也许他们就是不喜欢吧,那么不喜欢就不要做,做也做不长、做不好的。一些做了四、五年的程序员也在抱怨了:累啊,没意思啊,没前途啊,也没钱赚啊……真的吗?如果你还热爱程序员这个工作,我建议你不妨从我们这些经历还算丰富的程序员这里得到一些启示和鼓舞,在你接下来的程序员生涯中有意识的去培养自己的学习能力、分析能力、动手能力以及沟通与交流的能力,相信大家都会有宽广深远的发展机会。

看看国外那些花白胡子的程序员吧,真是让人感慨和羡慕。虽然我现在有了全新的事业与角色,但程序员仍然是我最本质的角色,哪怕再过上十年、二十年……到那时候,花白胡子可能不太现实,但估计秃顶是没问题了。到那时候,我希望我已经站在CTO那第三只眼所锁定的胜利彼岸,自豪的向大家汇报:我,为自己曾经是、现在也仍旧是一名优秀的程序员而感到自豪!

(本文原载《程序员》杂志2004年第12期第88页,此处是未经修编的原文

posted on 2005-01-01 19:34:00 by jgtm2000  评论(4) 阅读(2621)

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

 

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

 

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

 

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

 

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

 

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

 

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

 

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

 

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

 

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

 

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

 

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

posted on 2004-12-30 04:43:00 by jgtm2000  评论(26) 阅读(10590)

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

 

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

 

Positive Thinking!

 

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

 

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

 

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

 

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

 

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

 

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

 

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

 

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

 

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

posted on 2004-09-10 03:40:00 by jgtm2000  评论(18) 阅读(6971)

 
2004年09月01日

刚刚上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 on 2004-09-01 15:03:00 by jgtm2000  评论(9) 阅读(6834)

 
2004年08月31日

各位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 on 2004-08-31 02:21:00 by jgtm2000  评论(9) 阅读(4857)

 
2004年08月29日

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

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

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

 

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

posted on 2004-08-29 04:31:00 by jgtm2000  评论(22) 阅读(3799)

 
2004年04月07日

记得以前思归曾指导我们凡事都该多求一种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 on 2004-04-07 21:18:00 by jgtm2000  评论(5) 阅读(7947)

 
2004年04月05日

周末熬夜终于搞定的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 on 2004-04-05 21:31:00 by jgtm2000  评论(18) 阅读(6511)

 
2004年04月04日
 Part III: Context-based Interception Towards a Mini AOP Framework

 

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

 

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

posted on 2004-04-04 03:12:00 by jgtm2000  评论(0) 阅读(5327)

 

在本文的上一篇中,我们利用.NET Remoting基础架构中的真实代理/透明代理技术实现了不针对具体类型、具体方法的通用方法调用拦截机制。由于技术内容太多,本来想一同写在上一篇中的最后一大块内容就新作一篇吧。在本篇文字中,我们介绍可以用于在.NET中实现基本AOP(面向方面编程)的更深入的技术,并结合该技术的优势和劣势提出了一个已经在我们设计的项目中应用的一个AOP框架原型思路。

 

在前一篇文字的结尾,我们提出了几点技术方案中感觉不爽的环节,其中群众呼声最大的就是觉得对象的构造不够intuitive:一方面需要引入FACTORY METHOD(如CreateInstance()),另一方面在该方法中的构造过程也不够simpleproxied real objectreal proxyGetTransparentProxy……),还有,由于我们是在构造proxied对象之后才将方法拦截代理(即真实代理)施加在真实对象上,因此我们自然无法拦截到对对象构造器的方法调用——这个不足对于很多功能实现场合都是致命的(比如说需要监视系统中各类应用对象的创建频率,我们就需要在构造器调用时进行相应计数逻辑;另外对对象构造参数的记录和分析也是很常用到的)。在本文中,我们就引入新的技术来一一解决这些问题吧!

 

首先,有没有更简单的办法来构造被代理的对象呢?有没有办法在构造对象之前就设置好消息拦截器呢?我们来考虑这行代码:

 

Calc calc = new Calc();

 

这可以算是最简单的创建对象的写法了吧!如果我们能够覆盖new指令的默认行为使之利用我们自己的FACTORY就好了——可惜,CLR/C#都不支持覆盖(overridenew操作符。看来此路不通……可是——我突然想到了在.NET Remoting中是可以用new操作符直接构造远程对象的(而远程对象不正是一个经过代理的对象吗)!只要CalcMarshalByRefObject类型且在Remoting中正确的配置为wellknown type的话,则当CLR遇到C#中的new指令(也即IL中的newobj指令)时,它将把对象激活的任务交给.NET Remoting来完成。比如这样:

 

public class Calc: MarshalByRefObject {…}

 

RemotingConfiguration.RegisterWellKnownClientType(typeof(Calc),
  "tcp://remoteServer:8080/calcService");

Calc calc = new Calc();

Assert.IsTrue(RemotingServices.IsTransparentProxy(calc));

 

你可以自己试试这种写法,看样子还真的有戏——只要我们能够实现一个和对象激活地址(URL)配套的channel在指定位置监听由Remoting发来的方法调用消息并处理就可以!只是……这不是有点儿添乱吗?

 

再想想有什么线索……System.EnterpriseServices里面的ServicedComponent!因为它是为了让.NET的对象可以使用COM+提供的组件服务,那么它也应该应用了透明代理技术。让我们来验证一下(记得Add Reference to System.EnterpriseServices assembly and use this namespace!):

 

public class Calc: ServicedComponent {…}

 

Calc calc = new Calc();

Assert.IsTrue(RemotingServices.IsTransparentProxy(calc));

Console.WriteLine(RemotingServices.GetRealProxy(calc).GetType());

 

当然,为了在一个assembly里面实现ServicedComponent,我们首先要给assembly一个strong name,原理就不多说,只要简单的为所在assembly施加这么一个自定义属性即可:

 

[assembly: System.Reflection.AssemblyKeyFile(@"C:\xxx.snk")]

 

其中这个C:\xxx.snk文件是事先用.NET SDKsn工具生成的(如:sn -k C:\xxx.snk)。编译执行,发现果然如此!此时新创建的calc实例确实是一个经过透明代理包装的对象,而且这个透明代理所对应的真实代理是System.EnterpriseServices里面的一个叫ServicedComponentProxy的类(注意看Console.WriteLine()方法的输出结果)。意思对了,可是感觉有点儿“过”,还是先来研究一下ServicedComponent到底有什么名堂吧——为什么一个类从它派生出来以后再new的时候就被套上了一个代理呢?

 

原来,ServicedComponent是从System.ContextBoundObject派生而来的,而这个类又是从MarshalByRefObject派生而来。看来,MarshalByRefObject不能满足我们的需要,而ServicedComponent又是为了一个特定的开发需求而设置的基类,那么其中夹着的这个ContextBoundObject就应该是一个提供某种公共特性的基类啦!赶快来印证一下,把Calc的基类变成这个ContextBoundObject

 

public class Calc: ContextBoundObject {…}

 

再运行前面的同样的测试代码……噻!就是它了!看来只要一个类是从ContextBoundObject派生而来(以后我们也用CBO来简称这类对象),那么当它被new的时候,就会被套上一个透明代理——而这个代理背后干活的真实代理类(再次注意看Console.WriteLine()输出的透明代理的真实代理的类型!)就是System.Runtime.Remoting.Proxies名称空间中的RemotingProxy(这是一个internal的类型,所以既没有文档也不能直接被使用——事实上,这个真实代理就是实现.NET Remoting所有核心机制与扩展机制的类!)。

 

关于CBO的详细用途和使用方法,还请读者自己查一下MSDN文档(趁有得看还是看看吧——再往后一些的内容可就想看也没有喽!),我这里只是大概讲解一下。ContextBoundObject,顾名思义,object that will be bound with a context。这里的所谓context是指一个逻辑上的执行环境,每一个AppDomain中都有一个或多个这样的context,就好比每一个托管进程都可以有一个或多个AppDomain一样。AppDomain中的第一个context也叫做default context,所有不是CBO的对象(也称为context-agile object)默认都是在这个context中创建和运行。而CBO对象都将创建并运行在自己所需要或认可的context中。跨越context的方法调用都是通过透明代理转发的,因为context所设立的执行环境是由若干个对象用法规则所保障的,只有通过代理机制才可以通过设置方法拦截器来对进出context的方法进行规则保障(这和COM/COM+中的概念是相似的,COM+的所有服务也都是通过类似的方法拦截代理来实现的——其实更准确的说应该是.NET CLR大量借鉴了COM+的设计与实现机制)。

 

执行线程所在的context可以通过System.Threading名称空间中的Thread.CurrentContext取得,它的类型是System.Runtime.Remoting.Contexts名称空间中的Context类,其中每个Context实例都有一个唯一的IDContextID)。前面说的default context也是这个Context类上面的一个静态属性(DefaultContext)。我们可以通过它来观察在一个线程上跨越多个对象时每个方法调用所发生在的逻辑context。比如这样(记得using System.Threading!):

 

public class Calc: ContextBoundObject
{
  public Calc()
  {
    Console.WriteLine("Calc(): " + Thread.CurrentContext);
  }

  public int Add(int x, int y)
  {
    Console.WriteLine("Add: " + Thread.CurrentContext);
    return x + y;
  }
}

 

public class Application
{
  public static void Main()
  {

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

    Assert.AreEqual(3, new Calc().Add(1, 2));
  }

}

 

你可以看到,对Calc实例的方法调用是发生在另一个context内的(作为对比,你可以把Calc声明为从MarshalByRefObjectObject再看看)。(最好找个开发环境动手实验一下——笔者四月一日注;)

 

除了ContextID之外,Context对象上的另一个重要概念是context property。在Context类中就有一个叫ContextProperties的属性,它的类型是一个IContextProperty[]。那么到底什么是context property呢?这可以是任何一个实现了IContextProperty接口的对象,这个对象可以为其相关的context提供一些特性(这里我把property称为“特性”,因为如果叫成“属性”有可能会和attribute混淆——而还真就有一个叫context attribute的东西!这个我们稍后就会遇到)。而IContextProperty接口中只定义了一个Name属性,还有几个方法,我们稍后再说。除了提供只读的数组之外,可以看到Context对象还提供了GetProperty()SetProperty()两个方法。如果感兴趣可以动手试试,你会发现一上来每个context就有一个叫LeaseLifeTimeServiceProperty的东东,而用它的名字去GetProperty()也能取得,但是一SetProperty()就会出错——就像对象的property一样,contextproperty虽然不是类型的一部分,但是也只能在context构造的时期形成并在context的生存期内一直存在。如何为context指定一些特性呢?我们需要用context attribute(在.NET中一般说到attribute都是指可以通过反射获取的attribute)来把所需的context property设置在context中,而因为context是为ContextBoundObject来提供合适的执行环境的,所以这些context attribute就自然是附着在CBO派生类型身上了,就像这样:

 

[SomeContextAttribute]
public class AnyContextBoundObject: ContextBoundObject {…}