BT

如何利用碎片时间提升技术认知与能力? 点击获取答案

案例研究:Lawson并购产品线的架构集成

| 作者 Barry Livingston 关注 0 他的粉丝 ,译者 罗小平 关注 0 他的粉丝 发布于 2007年7月3日. 估计阅读时间: 51 分钟 | 都知道硅谷人工智能做的好,你知道 硅谷的运维技术 也值得参考吗?QCon上海带你探索其中的奥义

内容提要

在并购(Mergers and Acquisitions,M&A,兼并与收购)时代,我们常常无法回避的一个问题是:如何在不影响用户体验的前提下完成产品线集成。2006年4月,Lawson软件公司(Lawson)与Intentia国际有限公司(Intentia)合并后,其研发团队就遇到这样的问题:新公司的两个旗舰产品线,即面向以产品为中心行业的M3(实现产品与资产的“Make”、“Move”和 “Maintain”)、面向服务行业的S3(实现“Staff”、“Source”和“Serve”的优质管理,让用户满意),如何形成无缝的、一致的用户体验,以提升用户生产力、满意度和信息集成能力。

在本次案例学习中,我们将研究Lawson对此问题的解决策略,并深入分析其中一些让人感兴趣的技术细节。

问题分析

合并之前,Lawson和Intentia都提供ERP解决方案,但目标用户的行业和区域不同。Lawson的产品线S3,主要针对美国服务行业,帮助用户实现对“Staff”、“Source”和“Serve”的 管理;S3包含的ERP功能很多,如企业绩效管理(Enterprise Performance Management)、业务流程管理(Business Process Management)、企业财务管理(Enterprise Financial Management)、供应链管理(Supply Chain Management)和人力资本管理(Human Capital Management)等。而Intentia的M3主要面向EMEA(欧洲、中东和非洲)和APAC(亚洲太平洋地区)的物流与制造行业,帮助客户实现对产品及各项资产的“Make”、“Move”和“Maintain”;其功能模块包括生产运作(Manufacturing Operations)、供应链执行与优化(Supply Chain Execution&Optimization)、客户销售与服务(Customer Sales and Service)、企业资产管理(Enterprise Asset Management)、财务管理与控制(Financial Management&Controlling)以及业务流程管理等。两家公司的合并意图非常明显——实现产品线、目标用户的行业和区域互补。

两家公司的系统在业务有共同点,但技术实现差异很大。Intentia的M3以服务端Java业务逻辑层为中心,用户接口(UI)部分多年来的变动很大。最早使用MicroFocus 公司的Cobol与C++混合实现客户端,然后演变到ASP/JSP瘦客户端模型,最后实现了基于瘦客户端的AJAX/JSP访问(也就是所谓Workplace Foundation)。Lawson的S3则是一个混用了Java和第四代语言(4GL)的解决方案,而且正在逐步向完全Java演进。两个公司采用了从Workplace Foundation式Web门户,到基于C#的可移动富客户端在内的大量不同客户端技术。

并购之后,Lawson需要找到能充分激发这两个旗舰产品线能量的客户端的构建策略。具体来说,该公司希望新的客户端能满足如下要求:

  • 能为Windows XP和Vista用户提供一致体验(它98%的用户都使用Windows操作系统)
  • 能为用户提供相同的应用启动和消息接收方法。
  • 实现新应用和新功能插件式添加。
  • 提供丰富的、高生产力的用户体验。
  • 实现代码库统一,保证服务端业务逻辑能为各种客户端共享。
  • 能和Java和非Java的业务逻辑层交互。
  • 保证用户现有和新开发的客户端能使用同样的服务端实例。

方案概述

为了保证正确选择新的客户端技术,Lawson决定寻求Frog Design欧洲公司(www.frogdesign.com)的帮助。两家公司最后得到的解决策略是:基于Microsoft新推出的WPF(Windows Presentation Foundation)和WCP(Windows Communication Foundation)——二者都是.NET3.0框架的组成部分——构建富客户端应用。主要有以下几点考虑:

  • 新产品与Windows Vista的发布时间一致,这就可在行业内为Lawson塑造领先采用新技术的良好形象。
  • Lawson 98%的用户都使用Windows平台,因此,新应用以.NET为平台,就是在支持用户投资的增值。
  • WPF很好实现了应用要素的逻辑分离,如图形设计与其他实现细节的分离。
  • WPF为窗口、控件、音频、视频、演示文档以及2D、3D图形处理等提供了统一基础平台。
  • WPF提供稳定的、不依赖于屏幕分辨率的UI渲染技术。
  • WCF提供了易扩展的通讯编程模型和框架。
  • WCF实现了应用逻辑与通讯部分的高度分离。使用WCF的客户端无需关心服务的具体位置——跨应用、跨进程以及是否在本地机上都没有关系。
  • WCF为复合型应用系统和SOA提供了申述式模型(declarative model)。
  • WCF终端在发布后是可配置的。

客户端

由上图可见,新的富客户端(“Lawson智能客户端”)主要设计为三个层次,另外还包括对用户数据与个人设置的考虑。最上面的是表现层(Presentation Layer),负责用户交互与宿主部件的生命周期管理。它本质上就是一块客户端“画布”(Canvas)。不同类型应用(M3、S3等)的部件都可安置到画布上,用户使用它们可启动任务和响应各种事件。当然,用户也可以通过给画布换肤改变整个客户端的视觉体验。

逻辑渲染层(Render Logic Layer)由众多负责各自应用内容渲染的插件式引擎构成。而这些内容,可在运行时通过元数据、用户数据和其他预设资源复合得到,从而确保为用户提供一致体验。

数据接口层(Data Access Layer)利用WPF实现UI组件对业务逻辑和数据组件的绑定。它支持多种能确保UI从用户数据、客户端配置和Session环境等获取数据的策略,而最为常见的就是使用WCF终端,通过Web Service建立与服务端组件的连接。

Microsoft SQL Server Compact Edition用于存储用户数据、用户行为统计数据和用户状态。用户完成漫游配置(roaming profile)后,用户数据将被复制到服务器,从而可实现对任何Lawson智能客户端设备漫游的支持。

服务端

如上图所示,Lawson和Intentia这些年来针对不同行业开发了大量服务端业务逻辑和功能。事实上,图示所有服务端组件都早已先于要开发的智能客户端,以各种方式应用到客户的工作中了。为了保护客户在财力和精力上的投资,降低向新技术迁移的难度,继续使用已经存在的服务端组件(包括Java和非Java的),对Lawson来说将是最好的选择。

为了加快新客户端原型的实现,他们利用Microsoft提供的Java转换工具完成了M3移动富客户端使用中JCA(J2EE Connector Architecture)/CCI(Common Client Interface)连接器向J#的转换。J#客户端连接器使用WCF实现与后端Web Service的交互。

Web Service框架可根据业务服务器提供的元数据生成WSDL(Web Services Definition Language)和Java连接器代码。Lawson开发人员重构了现有J2EE Workplace Foundation应用中的服务端组件,从而赋予了基于AJAX的Workplace瘦客户端和新的Lawson智能客户端使用同样业务逻辑和服务的能力。

深入研究之一:通过Web Service实现Java和.NET的集成

新的Lawson智能客户端充分利用了WPF和WCF提供的特性。如下图所示,在C#中使用XAML(Extensible Application Markup Language),可实现客户端部件与数据源的动态绑定。而数据源则通过代理对象与对应Web Service通讯。代理可使用Windows SDK中的元数据处理工具SvcUtil.exe生成,它负责C#与Web Service的通讯,并在二者之间现信息的格式转换。至于对Web Service的调用,自然是利用SOAP(Simple Object Access Protocol)在HTTP上实现了。

例1:智能客户端调用Web Service

© 2007 Lawson Software, Inc. All rights reserved. This document is for informational purposes only. Lawson Software makes nowarranties, express or implied, in this summary.

例2:客户端部件与数据源的异步绑定(XAML)

       ObjectType="{x:Type ds:CRS990DataSource}" />

ItemsSource={Binding Source=dataSourceCRS990,
Path=ResultCollection, IsAsync=True, Mode=OneWay} >
<... />

例3:C#数据源模拟对WebService代理的调用

Public ObservableCollection ResultCollection
{
get
{
InitBrowseCollection data = new InitBrowseCollection();
data.InitBrowseItem = GetCallContext();
try {
CRS990MIClient crs990 =
WSHelper.CreateClient();
InitBrowseResponseItem[] responseCollection = crs990.InitBrowse(
WSHelper.GetLWSCredentials(), data);
return FilterResponse(responseCollection);
}
}
}

例4:WebService代理中的方法样例

...
public InitBrowseResponseItem[] InitBrowse(headerType mws,
InitBrowseCollection initBrowse)
{
InitBrowseRequest inValue = new InitBrowseRequest();
inValue.mws = mws;
inValue.InitBrowse = initBrowse;
InitBrowseResponse retVal =
((SmartClient.Widgets.WS.CRS990MI)(this)).InitBrowse(inValue);
return retVal.InitBrowseResponse1;
}
...

M3服务端应用套件的核心是WSF(Web Service Framework)。这个用Java实现的框架,包含了负责提供Web Service创建、测试和热部署的设计工具。这些工具生成的代码,能够解析来自Web Service的XML请求,并生成包含了请求结果的XML返回,从而实现Web Service与业务系统API的交互。而每个后端API的部署位置和实现架构则可能各不相同。比如M3系统,其实是一个独立运行的服务端应用程序,需要通过TCP/Socket实现对它的访问。

在下图我们可以看到,框架负责生成将业务API封装为Web Service所需的全部连接信息和配置数据。它会用到很多工具,比如实现Java/XML绑定的Apache XMLBeans、解析和验证Web Service请求的StAX(Streaming API for XML)。另外,它还负责生成WSDL、Web Service的XML格式描述,以及用于调用业务服务的连接器代码。

Lawson的servlet应用服务器,使用了IBM WebSphere。而Web Service核心引擎,则是Apache Axis2;在此基础上,Web服务器还要负责运行时配置、连接池和安全管理等工作。Axis2引擎性能优异,且全面支持WS-I Basic Profile 和WS Security(负责消息加密)等标准。技术人员还通过类加载器的分离——每个Web Service的类,都由独立的类加载器负责加载——实现了对各个Web Service的隔离运行。

深入研究之二:UI渲染

Lawson先前的很多业务系统都使用XML实现客户端、服务端通讯。要想保证老客户端能继续使用,后端服务和应用的实现就不能改变。为此,Lawson想了不少办法。比如已有的瘦客户端(基于AJAX/JSP),可使用XSLT(Extensible Stylesheet Language Transformation)将XML格式的元数据和运行时数据转换为HTML。至于无法直接通过XSLT/HTML方式处理的数据,则需要祭出Javascript了。

而富户端应用程序,实现UI时也需要用到这些XML数据。因为已有的一些Java富客户端应用动态创建UI,Lawson新的C#智能客户端也沿用了这种模式。

在使用XML数据渲染UI前,需先用WYSIWYG编辑器完成业务系统中的页面面板。面板定义好后,设计工具将输出描述了面板上所有部件信息的元数据。这些元数据也包含了从服务端业务系统获得的部件数据绑定信息,并说明了与这些信息的交互方式。

例:用元数据描述UI面板

© 2007 Lawson Software, Inc. All rights reserved. This document is for informational purposes only. Lawson Software makes no warranties, express or implied, in this summary.

元数据描述UI面板的代码片段:

<Panel name="MMA001E0" rtype="DETAIL" modDateField="WMLMDT" regDateField="WMRGDT" changedByField="MMCHID">
<PanelHeader>MMS001/E</PanelHeader>
<PanelDescription langId="MM00101"/>
<Objects>
<GroupBox langId="MX_0235" justification="LEFT">
<Position left="1" top="1" width="73" height="1"/>
</GroupBox>
<Caption langId="WIT0115" tab="256">
<Position left="1" top="2" width="14" height="1"/>
</Caption>
<EntryField name="MMITNO" fieldHelp="ITNO" suppressLeadingZero="true" protected="IN41|!IN45" tab="270">
<Constraints maxLength="15" uppercase="UC"/>
<BrowsePosition top="4" left="18"/>
<Position left="15" top="2" width="16" height="1"/>
</EntryField>
<GroupBox langId="MX_0238" justification="LEFT">
<Position left="1" top="4" width="73" height="1"/>
</GroupBox>
<Caption langId="WNA0115" visible="!IN21" tab="1024">
<Position left="1" top="5" width="14" height="1"/>
</Caption>
<EntryField name="MMITDS" fieldHelp="ITDS" suppressLeadingZero="true" visible="!IN21"
protected="IN01|IN21|IN45&!IN41" tab="1038">
<Constraints maxLength="30"/>
<BrowsePosition top="5" left="18"/>
<Position left="15" top="5" width="31" height="1"/>
</EntryField>
<...>
<FunctionKeys value="001011111001000000000000">
<FunctionKey fKey="F3" langId="XF03000"/>
<FunctionKey fKey="F5" langId="XF05000"/>
<FunctionKey fKey="F6" langId="XF06000" visible="!IN41" reverse="!IN41&IN57"/>
<FunctionKey fKey="F7" langId="XF07000" visible="!(!IN45)"/>
<FunctionKey fKey="F8" langId="XF08000" visible="!(!IN45)"/>
<FunctionKey fKey="F9" langId="XF09000" visible="!IN41"/>
<FunctionKey fKey="F12" langId="XF12000"/>
</FunctionKeys>
</Objects>
<RecordFields length="634">
<RecordField name="WWCLIN" type="DECIMAL" pos="18" length="3" refField="CLIN"/>
<RecordField name="WWCPOS" type="DECIMAL" pos="21" length="3" refField="CPOS"/>
<RecordField name="MMITNO" pos="24" length="15" refFile="MITMAS" refField="MMITNO"/>
<...>
</RecordFields>
</Panel>

所有元数据文件都将被发布到Web应用服务器上。应用服务器再将将元数据与业务系统的运行时数据合并,最后生成复合的XML数据。这些XML数据包含了UI部件渲染的所需的全部信息,如部件在屏幕上的位置、数据呈现格式(比如小数位数目)和输入约束(如只能输入数字)等。AJAX/JSP的瘦客户端和新的Lawson智能客户端都要用到这类类型的XML数据。

复合了元数据和运行时数据的XML片段:

<Panel name="MMA001E0">
<Objs>
<FKeys val="001011001001000000000000">
<FKey val="F3">End</FKey>
<FKey val="F5">Refresh</FKey>
<FKey val="F6">Text</FKey>
<FKey val="F9">Field Audit</FKey>
<FKey val="F12">Cancel</FKey>
</FKeys>
<EFld tab="270" name="MMITNO" hlp="ITNO" acc="WD">
<Pos l="15" t="2" w="16" h="1"/>
<Constr maxL="15" type="CHAR" uc="UC"/>
B000007
</EFld>
<EFld tab="1038" name="MMITDS" hlp="ITDS" acc="WE">
<Pos l="15" t="5" w="31" h="1"/>
<Constr maxL="30" type="CHAR"/>
Air Filter, Fleetguard AF1811
</EFld>
<...>
<ChkBox name="MMECMA" hlp="ECMA" tab="4149" acc="WE">
<Pos l="54" t="17" w="3" h="1"/>
0
</ChkBox>
<...>
<CBox name="MMSTCD" hlp="STCD" tab="4366" acc="WE">
<Pos l="15" t="18" w="16" h="1"/>
<CBV val="0">0-No inv account</CBV>
<CBV val="1" sel="true">1-Inv accounting</CBV>
<CBV val="2">2-No, but planned</CBV>
<CBV val="3">3-No, but as func</CBV>
</CBox>
<...>
<GroupBox r="t">
<Pos l="1" t="1" w="73" h="1"/>
Panel Header
</GroupBox>
<GroupBox r="t">
<Pos l="1" t="4" w="73" h="1"/>
Basic Information
</GroupBox>
<...>
</Objs>
<PHead>MMS001/E</PHead>
<PDesc>Item. Open</PDesc>
</Panel>

Lawson智能客户端从Web服务器取得上述XML数据后,会使用内置解析组件执行解析,然后根据解析结果动态创建、定位、初始化并施加约束于所有UI部件,最后将它们放置到面板上。当然,解析组件在此过程中也会考虑用户和其他UI设置的信息。

如下例子实现标签的动态渲染:

private void ReadCaption(XMLNode n) {
string text = n.InnerText;
if (text.Length > 0) {
XMLAttribute a = n.Attributes["tip"];
string name = GetStringAttribute(n, "id");
string tooltip = a != null ? a.Value : null;
bool isFixed = n.Attributes["fixFnt"] != null;
bool isAdditionalInfo = n.Attributes["addInfo"] != null;
bool isEmphasized = n.Attributes["emp"] != null;
bool isColon = n.Attributes["cl"] != null;

CreateLabel(name, text, tooltip, isFixed, false, isAdditionalInfo, isEmphasized, isColon);
ReadPosition(n);
SetPosition();
SetWidth();

ReadAccess(n);
SetAccess();

AddElement();
}
}

protected void CreateLabel(string name, string text, string tooltip,
bool isFixed, bool wrap, bool isAdditionalInfo,
bool isEmphasized, bool isColon) {
Style s;
HorizontalAlignment hAlign = HorizontalAlignment.Left;

if (isAdditionalInfo) {
s = StyleManager.StyleAdditionalInfo;
} else if (isEmphasized) {
s = StyleManager.StyleEmphasized;
} else {
s = StyleManager.StyleLabel;
}

label = controlPool.Create(ControlPool.TypeLabel, s)
as Label;
currentElement = label;
label.Name = name;
CreateControlTag();
controlTag.AdditionalInfo = isAdditionalInfo;

if (wrap) {
TextBlock tb = new TextBlock();
tb.Text = text;
tb.TextWrapping = TextWrapping.Wrap;
label.Content = tb;
} else {
if (!isAdditionalInfo) {
if (isColon) {
// Only labels with colon can be right-aligned.
// The colon is only displayed for left align
if (UserSettings.Current.RightAlignLabels) {
hAlign = HorizontalAlignment.Right;
} else {
text += ":";
}
}
}
label.VerticalAlignment = VerticalAlignment.Center;
label.Content = text;
}

label.HorizontalAlignment = hAlign;

if (isFixed) {
label.FontFamily = StyleManager.FontFamilyFixed;
} else {
label.FontFamily = StyleManager.FontFamilyBaseUI;
}

label.ToolTip = tooltip;
}

深入研究之三:利用规则引擎提升系统稳定性

Lawson服务端的设计目标是支持多种平台。客户系统安装在各种硬件和操作系统上,数据也可能存储于各种RDBMS中。这些环境的异构性,将给系统的维护与监控带来巨大挑战。因此,系统无可避免会划分出很多层次,这就为性能下降和信息丢失埋下了伏笔。

为了提升系统的稳定性,Lawson开发了所谓Foundation Stabilizer。这是一个在支持和开发人员定义的通用约束与模式基础上的规则监控系统,它通过JNI(Java Native Interface)组件,实现对系统运行时性能和配置数据的收集,如底层操作系统(比如CPU使用)信息。一旦发现可疑情况,Stabilizer将向维护人员发出警报(可以是RSS、Email和HTML等形式),并自动实施适当的解决策略(如降低问题线程的优先级),以稳定系统运行。如下各类系统信息,构成了通过规则和模式匹配定位问题的基础:

  • 性能计数器
  • 属性文件的数据
  • Java程序中的参数和属性
  • 系统环境信息
  • 任务基本信息、当前状态和活跃度等
  • JVM状态及其资源消耗情况
  • 平台无关或依赖方面的约束信息

Example 3: A warning issued by the Foundation Stabilizer. © 2007 Lawson Software, Inc. All rights reserved. This document is for informational purposes only. Lawson Software makes no warranties, express or implied, in this summary.

规则引擎采用Java编写,其运行机制使用Prolog(译者注:Prolog是由事实(Fact)、规则(Rule)和查询(Query)构成的,这三个术语在下文需要用到)描述。如下代码片段中的stabilize()方法,将通过一个与业务系统运行在同一JVM中的Java线程定时执行。

 boolean stabilize() throws Exception {
KnowledgeBase kb = engine.getKnowledge();

engine.consult("mvx/res/Stabilizer.rules");
engine.consult("mvx/res/Constraints.rules");

engine.setQuery("cleanup");

try {
addSysInfo(kb);
} catch (IOException ex) {
KQLOG.EX("MvxStabilizer:Failed to collect system information", ex);
return false;
}

KQLOG.DT("Run Stabilizer");
boolean res = engine.setQuery("stabilize");

if (!res) {
KQLOG.E("Could not stabilize - rule engine failed");
return false;
}
KQLOG.DT("End Stabilizer");

Enumeration en = kb.elements();

while (en.hasMoreElements()) {
// Handle results from the rules engine
}

return true;
}
  • 此方法首先从规则引擎中取得已有知识库。知识库中包含了收集或计算得到的有关系统的事实项。事实的表述格式很简单:“操作 条件”或者“操作 条件1 条件2 …”。比如:"jobName" "IKDXLZ9A.corp.lawson.net:26100" 4676 "Looping";"jobCPU" "IKDXLZ9A.corp.lawson.net:26100" 4676 55。
  • 规则(本例中是两组)与引擎是紧密相连的。
  • 通过执行“cleanup”查询,可将陈旧信息从知识库中删除。
  • 可用系统当前信息更新知识库。
  • 执行“stabilize”查询后,规则将开始收集新的事实项。
  • 所有规则执行完毕后,就可以轮询整个知识库里的新事实并一一处理了。
...
rule(looping_nostat1):-
jobCPU(Addr, Id, CPU),
CPU>40,
jobChange(Addr, Id, Change),
(Change==0;Change>999),
not(loopingMem(Addr, Id, _)),
info(currentTime, Time),
assert(loopingMem(Addr, Id, Time)).

rule(looping_nostat2):-
loopingMem(Addr, Id, LTime),
jobCPU(Addr, Id, CPU),
CPU>40,
jobChange(Addr, Id, Change),
(Change==0;Change>999),
info(currentTime, Time),
Time-LTime>5,
jobType(Addr, Id, JobType),
jobName(Addr, Id, Name),
assert(warning(2, 4, 'Job may be looping', 'Job', JobType, Addr, Name, Id)).

rule(looping_nostat3):-
loopingMem(Addr, Id, LTime),
not(jobName(Addr, Id, _)),
retract(loopingMem(Addr, Id, LTime)).
...

上述三个规则,用于检测过度占用CPU资源的任务。

规则一,首先检测过度消耗CPU的任务,然后对其监控。jobCPU首先择定某任务(用Address、ID和CPU描述);若此任务的CPU占用率超过40%,且变动率指标为0或超过999,就将创建新的事实项(loopingMem),并设置Time值。

规则二,负责检测出被监控时间超过五分钟的任务。事实项loopingMem(由规则一设定的)在整个知识库中搜索这些任务。接下来执行的操作和规则一相同。最后,Time被更新,并增加了jobType和jobName。在M3业务引擎中,会使用这些事实项实现警告功能。

规则三,负责检测已经停止运行的任务,并将它们从监控列表中删除。loopingMem首先从知识库中搜索全部任务。如果在知识库中不能找到任务项,则将其从监控列表中清除。

总结

Lawson与Intentia在2006年的合并,迫使开发人员不得不面对一大难题——如何实现现有众多采用了不同技术的业务系统的集成。为了在公司的两大旗舰ERP产品线上提供持续一致的用户体验,客户端解决方案必须采用Java和4GL实现与业务层的交互。

在与Frog公司共同分析现有UI技术后,Lawson决定引入Microsoft的.NET3.0框架——即使用WPF实现UI组件与业务逻辑和数据的绑定,用WCF实现客户端与现有Web Service的交互(当然,部分系统必须做一定程度的重构)。如此一来,客户端应用的开发就变得高效了。新的Lawson智能客户端定义了插件式的客户端架构,从而大大降低了新应用和新功能的添加难度。通过对全公司代码库的统一,保证了服务端业务逻辑能为各种客户端共享,各系统的可维护性也提高了。

在研发智能客户端过程中,申述式编程语言应该为Lawson开发人员留下了深刻印象。说实话,申述式语言的学习成本相当高,不易使用,理解难度大。但一旦掌握了它,就可在研发生产力上获得巨大收益。就Lawson开发人员的经历来看,尽管初期遇到的困难很多,但最终在生产力上得到的回报是巨大的,付出的努力是值得的。

Lawson最初版本的智能客户端架构定位于利用强大而灵活的UI组件构建新型应用,并在提升用户生产力和满意度的同时,保证用户体验的持续和连贯。从用户目前的反馈来看,Lawson的努力没有白费。

查看英文原文:Case study: A new approach to integrating architectures post-merger at Lawson
译者简介:罗小平,上海某大型公司互联网中心技术总监,CSDN大版主,网络ID为lxpbuaa(桂枝香在故国晚秋),曾著有《Delphi精要》一书。个人博客为http://blog.csdn.net/lxpbuaa,现在CSDN主持翻译国外专家Herb Sutter的中文博客。他的Email和MSN为lxpbuaa AT 263.net

评价本文

专业度
风格

您好,朋友!

您需要 注册一个InfoQ账号 或者 才能进行评论。在您完成注册后还需要进行一些设置。

获得来自InfoQ的更多体验。

告诉我们您的想法

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我
社区评论

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

讨论

登陆InfoQ,与你最关心的话题互动。


找回密码....

Follow

关注你最喜爱的话题和作者

快速浏览网站内你所感兴趣话题的精选内容。

Like

内容自由定制

选择想要阅读的主题和喜爱的作者定制自己的新闻源。

Notifications

获取更新

设置通知机制以获取内容更新对您而言是否重要

BT