在公司的例会上,你的老板给你布置了一个新任务。这个任务听起来一点也不难,你所要做的一切就是为你公司的网站建立一个“简单”的基于Microsoft .NET的内容管理系统(CMS)。当你离开会议厅时,你可能刚刚开始觉得蹊跷:为什么没有人试图得到这个任务,你甚至记起当宣布这个项目时,你的几个同事都躲了起来,这究竟是怎么回事?
你也许还不知道,这个新任务会逼着你必须学习许多新技术,如HTML、JavaScript,、ASP.NET、C#、SQL、XML、 ADO.NET、 .NET remoting、网络服务等。你还需要掌握几个概念,如n层(n-tier)体系结构、数据库开发、版本控制、工作流(workflow)、个人化(personalization)和安全等等。
无论你是否认同这一点,你无疑是得到一个好项目,因为在开发过程会逼得你学到很多东西。唯一的遗憾就是没有人会立即认识到你在完成这个“简单的”项目后变得多么博学了。
有一大堆文章谈到建立你那个“简单”的CMS(内容管理系统)所需要的内容。沿着这条路走下去,我们将探索与CMS开发有关的众多技术和概念。我们在开篇文章所提到的技术在会在我们的实现样本中一一出现。那让我们从基本体系结构(即三层和多层体系结构)来开始本系列文章吧。
CMS三层体系结构
我曾在开发CMS(内容管理系统)三层体系结构上受到好评,不过那已经是好久以前的事了。CMS三层体系结构与标准三层客户端/服务器体系结构是基本对应的。CMS三层体系结构没有什么难以理解的内容,都是一些常识性的东西。它的每一层对应着一个体系结构中必须的元素:交互(interaction)、操作(manipulation)以及存储。这三层是:
表示层——处理与用户的交互、交流。
事务逻辑(business logic)层——处理用户所需要的信息。
数据库层——存储系统所处理的所有数据。
图A中直观的表示了CMS三层体系结构。图A所示的层分别位于不同的机器上,实际上,多个层可以存在于同一台计算机中,但是将它们分布在多台计算机中可以更好的分配CMS系统的负荷。

图A
CMS三层体系结构
CMS n层体系结构
简单的说,CMS n层体系结构就是把CMS三层体系结构的各个层分解为多个层,如图B所示。把层进行分解的好处是使得各个层更好的协调工作从而提高了系统性能;同时这也使得系统分布在更多的计算机上,这样可以减少系统由于指定计算机耗时过多而造成的瓶颈,从而提高了系统负荷。

图B
CMS n层体系结构
表示层是什么?
尽管表示层并不见得比其它层更重要,但是它几乎得到了全部的荣耀——因为它是唯一的CMS用户可以看到的层。这个层负责CMS与用户的交互工作。
表示层实际上由两部分组成:即Web客户端和Web服务器。Web客户端驻留在用户计算机中,通常用来接受Web浏览器的表格(form)。Web服务器位于Web主机地址上,用来生成动态Web页面和组成CMS系统的表格。
Web客户端与Web服务器端通过“请求——回应”的方式来相互通信。Web客户端向Web服务器发出请求,Web服务器根据请求作出回应。
Web客户端使用的是HTTP的请求方式。如:
GET /index.html HTTP/1.0
User-Agent: Mozilla/4.0 (compatible; MSIE 5.0; Windows NT)
Host: www.contentmgr.com
Web servers respond using the HTTP response. For example:
HTTP/1.1 200 OK
Server: Microsoft-IIS/5.0
Date: Thu, 12 Jul 2002 19:19:52 GMT
Connection: Keep-Alive
Content-Length: 1270
Content-Type: text/html
Set-Cookie: ASPSESSIONIDQQQGQGDC=MOFPDBPCPNIBACIBDCIOFCCL; path=/
Cache-control: private
…HTML嵌入了Web服务器发出的回应,该回应用来指示浏览器显示什么内容;JavaScript用来实现客户端的基本功能。最近以来,其它技术,如Java applet和ActiveX组件开始流行了,不过Web服务器在最初发出的回应中,绝大多数还是使用HTML,这包括了服务器发出的用于通知客户端使用何种HTML以外的技术来接管后面的执行过程的回应。
事务逻辑层是什么?
重申一次,事务逻辑层的功能可以放到单个的服务器上(三层体系结构),也可以分布到多个服务器上(n层体系结构)。事务逻辑层的功能包括以下三个部分:
访问(获取和保存)数据库层的数据。
从表示层获取数据。
执行必要的运算并且/或者处理数据。
事务逻辑层从数据库层获取数据,并根据表示层的需要来对数据进行处理。事务逻辑层也可以获得表示层提供的数据,并根据数据库层的需要对其进行处理。
CMS事务逻辑层的许多逻辑与其它两个层的交接(interfacing)有关。在Microsoft.NET下,由于ADO.NET、.NET remoting和Web服务器的帮助,这种逻辑的复杂性大多都被降低了。有了.NET之后,该层的最复杂的逻辑就是用于处理事务逻辑而进行计算和处理数据任务了(用C#或者Managed C++)。
什么是数据库层
数据库层的名字告诉了我们它的任务是什么了;它用来处理CMS数据。一个不太引人注意的地方就是,它的数据存储和检索功能并不限制于数据库。它可以是单个或者一系列平面文件(flat file),可能是XML格式。不过,数据通常还是存在数据库中。数据库的类型并不重要,因为对绝大多数CMS系统来说Microsoft SQL Server 2000与Oracle以及Sybase同样优秀(至少,你在Windows环境下)。也就是说,微软公司已经为Microsoft SQL Server 2000优化了.NET接口(interface);这可能会给它一个小小的优势(edge)。不过其它数据库提供商也没有闲着,微软的这个优势很快就会消失。
如果你不偏好某种数据库,你最好按通用的方式编写代码,这样你可以把任何数据库嵌入到你的CMS中。谁知道将来会怎样?今年所选择的数据库,明年你可能就不想再用了,是不是?
数据库层通常有它所在的计算机加载和访问。在大型的CMS中,通常在另一台计算机上保存一个镜像拷贝,这样可以在主机发生故障时,可以用它来顶替。
数据库层的内部工作过程、以及数据库本身,对一般的编程者来说是一个迷。开发者在本层的主要任务就是建立数据库、创建并载入数据库纲要(schema),偶尔也需要生成报表,还有一点就是要常常备份数据库。
选择哪一种体系结构?
选择哪一种体系结构(三层或者n层)取决于以下因素:
CMS 需要实现的功能
数据的数量
并行工作的用户数量
预期的增长
基本上,如果你的CMS系统将一直保持较小的规模,最好选择三层体系结构。相反,如果你估计CMS系统会越来越大,n层体系结构是一个明智的选择。
由于服务器间的通信量不高,三层体系结构更容易实现。但是在.NET中,并不一定要这么做。由于三层体系容易构建,所以可以早日投入使用。这样,如果市场要求很急迫,三层体系结构也可以作为大的CMS系统的暂时解决方案;把三层体系结构的方案几乎“无痛苦”的移植到n层体系结构是可行的。不过,你要认识到,拥有大量用户的三层体系的CMS系统很可能会超负荷的。
小结
现在你应该基本理解这两种主要用于CMS的体系结构。同时,你也应该可以科学的评估你应该使用哪一种体系结构。
[资源]开源的.net的CMS系统
几个.Net开源的CMS、Portal系统 最近打算花些功夫研究.Net环境下的CMS、Portal系统,很多优秀的开源CMS、Portal系统,都是PHP开发的,比如Xoops、 Mambo、Drupal,比起Php在OpenSource中的群星璀璨来,.Net CMS、Portal开源项目有点暗淡。在sourceforge上找了一个下午,把一些比较有成熟、有特点的项目下载下来准备研究。
一、DotNetNuke DotNetNuke
是一个.Net平台下,最负盛名的CMS系统,爱好者们都称它为DNN,开发语言是VB.Net。
其相关资源有:
Sourceforge上的项目地址:http://sourceforge.net/projects/dnn/
官方网址:http://www.dotnetnuke.com/
有汉化版本,国内有不少的研究者,主要集中在:http://www.dnnchina.net/,那里提供有很多的学习教程和Skin。 DNN是一个比较成熟的CMS系统,提供有大量的插件(Feed、相册等),目前最高版本是4.0,在.Net2.0框架下运行。
DNN是VB.Net开发的,很多开发者并不习惯VB.Net的风格,所以DNN爱好者创建了个C#版本的项目SharpNuke.NET。
Sourceforge上的项目地址:http://sourceforge.net/projects/sharpnukenet
官方地址为:http://sharpnuke.net/
二、dBlog Sourceforge
介绍说dBlog是asp和ASP.NET混合开发的,实际上主要还是asp环境下运行的CMS系统,这是一个轻量级的系统,其实主要用于Blog的发布,而并非Portal。
Sourceforge上的项目地址:http://sourceforge.net/projects/dblog/
官方地址:http://www.dblog.it/
比较有特点的地方就是blog、podcast的相关功能的实现。
三、Rainbow Portal
一 个酷酷的名字–Rainbow,使用C#开发,这个系统是在MS iBuySpy的基础架构上强化而来的,目前的Rainbow2006和iBuySpy项目已经很不一样了,大大的超出了很多,比起DNN来, Rainbow也有不少的优点,它支持多种语言,可以定制主体风格,可以创建工作流等。
Sourceforge上的项目地址:http://sourceforge.net/projects/rainbowportal/
官方地址:http://www.rainbowportal.net/
Rainbow在国内也有相关的研究:http://rata.cnblogs.com/
我发现Rainbow2006的安装有些问题,他的数据库创建脚本不适应大字符集的环境,我在简体中文的系统打开脚本查看,一些Insert的配置参数是乱码。
四、OmniPortal OmniPortal
并不是一个直接的应用程序,实际上是一个Portal的框架内核,可以在它的基础上建立任何的Web应用系统。对于一个Web开发者,OmniPortal提供了优秀的二次开发基础类库,虽然目前OmniPortal还不是一个Release版本,但是非常值得关注。
Sourceforge上的项目地址:http://sourceforge.net/projects/omniportal/
官方地址:http://www.omniportal.net/
由于OmniPortal是一个基础框架,因此参考文档相当重要,可是官方网站不知道为什么总连不上去,Sourceforge上又没有相关的文档,让人非常遗憾。
五、Ludico 这个Portal、CMS系统也不是一个正式版本的,去年11月份才开始的项目,但是我发现它的架构非常优秀,采用NHibernate.Net作为系统框架,因此可以作为一个很好的学习对象,值得关注之。
Sourceforge上的项目地址:http://sourceforge.net/projects/ludico/
[代码]ASP MVC Framework视图显示控制器返回结果
Controller控制器代码,用于返回List
public void FlashGet(String browserAddr)
{
//创建models
FlashGet flashGet = new FlashGet();
//根据url是否为空判断定向到页面
ViewData["test"] = "url is:"+browserAddr;
if (browserAddr==null)
{
RenderView("HowTo");
}
else
{
List<String> Result=flashGet.GetSWFS(browserAddr);
//返回结果给list
ViewData["list"] = Result;
RenderView("SWFList");
}
}
View视图代码,遍历List
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" AutoEventWireup="true" CodeBehind="SWFList.aspx.cs" Inherits="FlashSaver.Views.Home.SWFList" %>
<%@ Register TagPrefix="m" Namespace="FlashSaver.App_Code" %>
<asp:Content ID="Content1" ContentPlaceHolderID="MainContentPlaceHolder" runat="server">
<%=ViewData["test"]%>
<%
foreach (String path in (ViewData["list"] as List<String>))
{
Response.Write(path+"<br/>");
}
%>
</asp:Content>
[代码]C#编写的搜索引擎
主要的代码分析如下:
///
///
private void GetHTML() {
if (_HTMLData != null) return;
Byte[] BinData;
try {
WebClient wc = new WebClient();
BinData = wc.DownloadData(_URL);
_HTMLData = Encoding.Default.GetString(BinData);
wc.Dispose();
}
catch (Exception) { Console.WriteLine(“Can not read this page!”); return; }
GetTitle();
GetMeta();
GetLink();
if (OnFinishAnalyze != null) { OnFinishAnalyze(this, new EventArgs()); }
Console.WriteLine(“Finish!”);
this.Dispose();
}
///
///
private void GetTitle() {
if (_Title != null) return;
//”(
Regex reg = new Regex(@”
try{
Match m = reg.Matches(_HTMLData)[0];
if (m.Success) _Title = m.Groups[1].Captures[0].ToString();
}
catch (Exception) { _Title = “”; }
}
///
///
private void GetLink() {
if (_ChildURLSet != null) return;
ArrayList urlset=new ArrayList();
//”]+>([^ <>]+)” //提取超链接的正则表达式
Regex reg = new Regex(“]+>([^<>]+?)“, RegexOptions.IgnoreCase);
MatchCollection mm;
try { mm = reg.Matches(_HTMLData); } catch (Exception) { return; }
urlset.Add(new QLinkURL(this._URL, “”, this._MetaWords,this._Title));
foreach (Match m in mm) {
urlset.Add(new QLinkURL(URLJoin(m.Groups[1].Captures[0].ToString()), m.Groups[2].Captures[0].ToString().Replace(” “,””), “”));
}
_ChildURLSet = (QLinkURL[])urlset.ToArray(System.Type.GetType(“QSplider.QLinkURL”));
}
private void GetMeta() {
if (_MetaWords != null) return;
//
Regex reg = new Regex(““, RegexOptions.IgnoreCase);
try{
Match m = reg.Matches(_HTMLData)[0];
if (m.Success) _MetaWords = m.Groups[1].Captures[0].ToString();
}catch(Exception){ _MetaWords = “”;}
}
public void Dispose() {
this._HTMLData = null;
this._ChildURLSet = null;
this._MetaWords = null;
this._Title = null;
}
public string URLJoin(string s2) {
s2=s2.Trim(_SplitChar);
if (s2.StartsWith(“http://”, true,null)) return s2;
if(s2.StartsWith(“/”)) s2.Substring(1,s2.Length-1);
if (_URL.LastIndexOf(“/”) > 9) _URL = _URL.Substring(0,_URL.LastIndexOf(“/”));
return (_URL + “/” + s2).Trim(_SplitChar);
}
主要工作的函数就是上面的几个了!这是一个爬虫的代码!
当然还有数据库的,还有web的!
下载地址如下:
http://59.70.157.222/QSearch.Splider.zip
http://59.70.157.222/QSearch.WebSite.zip
[教程]ASP.Net调试之三板斧
一、Config.web
用过ASP的人对它的调试应该是记忆深刻的。在整片整片的代码中找到那几个
出错的地方,难度可想而知。现在微软推出了ASP的更新换代产品ASP.NET。对于
ASP.NET的好处,我想很多网站都已经介绍了差不多的,不过对于ASP.NET的调试
讲得就不是很多了。所以,我就以我的一点个人经验写了这一篇文章。由于,我
也是接触ASP.Net不久,错漏之处在所难免,还请大家多多指正。好了,言归正传。
第一招:配置Config.web
一般,当我们写好的网页运行出错了,ASP.Net就会在页面上告诉我们程序有
错了,但究竟错在哪里,它是没有提示的。为了能让ASP.Net进一步提示我们出错
的信息。我们就有必要编辑Config.web中的配置信息。
可能还有很多刚刚接触ASP.Net的人不太了解Config.web这个文件。那我就顺便介绍一下。Config.web是ASP.Net的一个配置文件,它里面存放着关于ASP.Net的所有配置信息。当执行一个ASP.Net页面时,它会先到该页面所在的目录查找这个文件,如果没有找到,就往上一级目录找,一直到wwwroot目录。如果都没有,它就会调用X:\WINNT\Microsoft.NET\Framework\ v1.0.2204目录中的Config.web文件(X为系统目录)。所以,如果大家要想改变所有页面的配置,就应该改WINNT目录中的那个 Config.web。另外顺便说一下,当你打开Config.web后,你会发现这是一个XML结构的配置文件。
好了,介绍解说这么多了,回到我们的主题。现在你要做的第一步就是:打
开或者新建一个Config.web文件。我们分开来说:
如果是新建,你就需要用文本编辑器新建一个新的文档,然后往里面输入下
面的语句:
输完后选择另存为,输入文件名Config.web,将它保存到当前页面相同的文件夹中即可。如果是修改一个已经存在的Config.web文件,你只需要用文本编辑器打开它,然后在
二、<%@%>
上一次我们说到配置Config.web文件,让出错信息给出更多的提示。可光有
提示又有什么用呢?“最多就是知道错在哪里,可我还是不会改呀!”别急,别
急,看看咱们这篇文章叫什么来着,“三板斧”,那当然是一斧赛过一斧啦!上
次那招不够厉害,我们还有下面的那,所以别急啊,且听我慢慢道来。
第二招:Trace追踪
用过ASP的人应该都用过下面的语句吧:
Response.Write XXX
Response.End
虽然我不太喜欢ASP的编程方法,但是这种方便的调试手段还是很好的。换用
ASP.Net以后,我发现ASP.Net提供一种更强大的调试方法,它就是我们现在要说
的Trace。所谓Trace功能就是在网页的最前面加上一些标记,至于是什么标记呢?嘿嘿,我不能马上告诉你(不好,臭鸡蛋……哇!)。我是说,我要先介绍一点基础的知识啦!
我不知道大家对ASP.Net的页面标示了解多少,为了下面讲解的方便,我还是
概要的介绍一下吧!ASP.Net的页面标示指的是在每一个ASP.Net页面最上面,用
<%@和%>括起来的语句。它的功能是用来确定在处理ASP.Net文件的时候,需要系
统做一些什么特殊的设定。具体的语法如下:
<%@ directive attribute=value %>
其中:directive就是页面标示符;attribute是该标示符对应的一些属性。
注意:在属性之间需要空格,而在”=”之间不能有空格。
ASP.Net现在包含以下7种标识
@ Page,
@ Control,
@ Import,
@ Registe,
@ Assembly,
@ OutputCache,
@ Webservice
我们用得最多的就是@ Page标示,而现在我要讲的Trace功能,也要用到@ Page。(关于这七个标示的具体应用,我会在以后发贴讲述的。)好了,回到我们的主题。要用Trace功能,你必须在页面的最上面加上:<%@ Page Trace="true" %> 这句话。加好后,你就可以看看页面的运行情况了。运行该页面。你会发现在页面的下半部分出现了一大堆的数据。下面就来解释一下这些数据的含义:
Request Details:通过Request方式向浏览器所读取的数据;
Trace Information:事件发生或程序执行的过程信息;
Control Tree:网页所使用的控件及控件之间的阶层关系;
Cookies Collection:网页所使用的Cookie信息;
Headers Collection:浏览器的表头信息。
Server Variables:Server变量的数据信息。
有了这一大堆数据,我们的工作就好做多了,但且慢欢喜,Trace还提供了更强大的功能,请接着看下去。
除了让ASP.Net页面显示这一堆数据外,我们还可以将程序中用到的变量的
值实时的显示在Trace Information区段中,其方法是调用Trace.Warn或Trace.Write两个方法。他们的用法如下:
Trace.Warn(“Description”,Variables);
Trace.Write(“Description”,Variables);
我想你们一定会问,这两个有什么区别呢?回答是:在功能上,这两个是一模一样的,只是在显示上,Trace.Warn将会以红色字体表示。
三、 查错神器Debugger
要用好的兵器,没点准备是不行的,所以我们就先来做点准备运动。
1.Config.web的设定:还记得第一招中讲的吗?对了,还是这个文件,打开它,向里面加入这条语句
说明:由于在预设情况下,ASP.Net会以正常模式来运行页面,为了能让它以查错模式编译网页,我们就必须加入这句语句。
2.启动查错工具DbgUrt.exe:这个程序放在x:\Program Files\Microsoft.Net\FrameworkSDK\GuiDebug目录里,文件名是DbgUrt.exe
3.激活查错功能:
运行DbgUrt.exe程序
–〉选取菜单Debug/Processes
–〉在出现的Processes对话框中选中Show system processes和Show processes in all sessions
–〉在Available processes列表框的最下面找到xspwp.exe(如果没有,请运行一.aspx页面,然后按Refresh键。)
–〉选取该文件后,按Attach键
–〉在出现的对话框中选中Common Language Runtime,然后按OK,回到Processes对话框
–〉按Close键
–〉在程序的主菜单中选取File/Open/File,打开你想要检测的文件。
下面正式开始页面调试。
利用DbgUrt.exe打开欲调试的文件后,我们要做的第一件事情就是:设置断
点。是不是觉得和其它Windows应用程序的调试很像(什么很像?简直就是一模
一样吗!)加断点的方法很简单,先决定需要加断点的地方,然后将光标移动到
该行,按下F9或者在该行的最前面点击鼠标左键,该句前面就会出现一个问号的
标记,这就是断点!我们当然可以设定多个断点,只要你喜欢。你要是像取消一
个断点,只需在该行重复设置断点的动作即可。
设置好断点后,只要使用浏览器浏览被调试的页面,当程序运行到断点的位
置时,它就会自动弹出DbgUrt.exe,并且停在刚才的断点位置。这是,我们就可
以利用Command Window-Immediate窗口检查变量的值了。
当我们想继续执行当前页面时,我们有几种选择:
1、按F5键,运行到下一个断点,若没有断点,则运行完该页面;
2、按F11键,执行单步操作;
3、按F10键,同样是单步操作,但它会进入子程序(函数)中的语句。
[架构]超越SOA:动态业务应用的新企业应用框架
第一部分——即使拥有全部需求和最佳设计,你的架构仍然很可能失败,原因在于……
目前的管理学校,教育培养的是公司经营者。设计公司几乎没有引起重视……几乎从来没有任何人为了取得计划的增长和稳定性,有意识和有思想地设计一个组织。
相关厂商内容
Dev2Dev技术日:如何在SOA参考架构中使用Eclipse、Java、Flex和SOA?
相关赞助商

—— Jay W. Forrester,设计未来(1998)
介绍
在一篇名为《动态业务应用势在必行(The Dynamic Business Applications Imperative)》的论文中,Forrester的高级分析师John R. Rymer指出了当今应用的一个致命缺陷:
当今应用迫使人们去寻找一种将孤立的信息和功能组映射到他们任务和过程的方法,它们强迫IT人员花高额预算来跟踪不断变化的市场、策略、规章制度和业务模型。
在下一个5年内,IT的主要目标应该是发明新一代企业软件,适应业务和业务工作,同时能随业务演变而演变。
Forrester称这个新生代为动态业务应用, 强调了和业务过程及工作(为人而设计)的紧密配合,对业务变化的自适应(为变化而构建)。在这个阶段,动态业务应用的需求比创建它们所需的设计实践更清 晰。工具都是现成的:面向服务架构(SOA)、业务过程管理(BPM)和业务规则领域中的先驱——包括独立软件开发商(ISV)——已经开始向我们展示了 这种方法。现在就是开始这段旅程的时候。
在这篇由两部分组成的文章中,我们会从架构和方法论的角度,采用历史的观点来看待这些动态业务应用(DBA)的发展。我们的目标是获得一种能使应用容易适应业务变化和其他必要修改的构建方法。随着企业在21世纪关注灵活性,DBA是使业务和IT在未来几十年内成功的关键。

图 1. 灵活性和效率——21世纪企业的两个主要驱动力
动态性对我们意味着什么?
在软件工程领域,许多框架或产品都声称具有自适应性。在我们设法理解一个解决方案在适应变化方面究竟有多好之前,需要给系统是如何变化的——它们的动态性——下一个可靠的定义。
早期的面向对象方法论认识到[1]:为了使系统分析中立,它必须基于两类现实世界的需求:
- 现实世界的实体 —— 收集现实世界实体的信息和它们间的关系,有助于分析师开始以一种系统的、结构的、客观的观点来看待需求,而非一种技术的、主观的观点
- 现实世界的事件 —— 系统行为只由改变现实世界实体状态的事件的出现来驱动
在这样的背景下,对于每一个被分析的系统,我们总能识别一个或多个最重要的实体。每个实体都又包含3个关联元素:事件、状态和生命周期。每个事件代 表状态中的一个变化,所有普通实体状态的有序和代表了一个生命周期。但是,那些触发状态变化且是正常流程一部分的事件与那些触发状态变化但不是正常流程一 部分的事件之间有明显的差异。例如,当一个产品订单被提交之后,一组可能发生的事件包括付费处理和订单交付。当一个用户变更订单或当企业改变价格时,我们 不能认为这些动作是正常流程的一部分,因此它们与实体(如订单)的生命周期无关。核心实体实例的生命周期单独定义了在正常操作中系统最有可能处理的东西。 所有其他事件类型,如变化或中间步骤,被区别对待。
这个场景对很多工程师都不陌生:一个系统模型包含一个核心实体结构,该实体具有一组事件,这些事件组成了实体的生命周期。这个系统模型对分析师和设 计者都很清晰且易于理解。建模工具,如有限状态机、实体关系图、实体状态转换图和数据流图,为了帮助这种方法已经经过了快20年的完善。那些为复杂系统 (如空客380或F-22,后者是世界上最高级的战斗机)服务、拥有数十亿行代码的软件就是这样被编写出来的。使用对象流图(它是捕获事件和状态转换的基 础模型)虚拟化实体生命周期是这个模型的关键。在这个情况下,架构可以被认为是静态的,因为整个系统状态在时间轴上的任意一点都是确定的。

图 2. 事件模型、状态变化和生命周期是正常操作的核心
普通事件、状态、生命周期间的关系,以及从其他事件类型中分出的普通事件是理解所建议的动态操作框架的基础。正如James Martin 和James Odell很久以前在《面向对象分析设计》中所写的,分析师、设计师和实现者都应该使用同一系统模型。分析师使用数据流图思考,设计师使用结构图思考,程 序员使用Java和SQL思考。在数据流上下文中,分析师识别对象类型,并思考改变对象状态的事件。最终用户也理解这个相同的认识。他们也应该按照对象类 型、事件、对象状态的变化,以及触发和控制事件的业务规则进行思考。Martin和Odell强调了对象流图对系统设计师的重要性:“事件模式适合按照事 件、触发器、条件和操作来描述过程。但是这种方式不适合描述大型复杂过程。一个系统领域常常太大或太复杂,无法表示成事件和触发器。此外,可能只有一个高 级别的认识才是必要的。这对战略级别的规划尤其正确。在这样的情况下,一个对象流图是有用的。对象流图(OFD)与数据流图(DFD)类似,因为它们描述 了活动和其他活动间的接口。在DFD中,这个接口传递数据。在对象技术中,我们不再限于数据传递。相反,图应该表示从一个活动传递到另一个活动的任何类型 事物:不论它是报表、零部件、已完货物、设计、服务、硬件、软件——或数据。简而言之,OFD指的就是被产生的对象,以及产生和交换它们的活动。”
为了捕获与业务操作关联的信息流,业务分析师用价值流程图(Value Stream Mapping)对OO方法论进行了补充。价值流程图起源于丰田,与精益制造关系紧密。美国国家环境保护局将价值流程图定义为“用来认识那些产生产品或交 付服务的活动序列与信息流程的精益过程映射方法。”此处的关键词是“产品”和“服务”。它们显现了合适的信息流程在整个企业中扮演的统一角色。
把过程流图和价值流程图两个概念结合在一起后,它产生了一个完全代表整个企业经营范围、可被方便翻译成OO(图2)概念的框架基础。
但是,许多系统并不是静态的,它们变化无常的行为无法被一组关系和事件捕获。事实上,它们发生在不可知的[2]未来,传统用来捕获它们动态特点的工具不起作用。所有的业务应用和绝大多数现实世界的系统都归于此类。这些系统,基于它们和其他外部系统的交互方式,处理3类变化:
- 正常工作 —— 组成主实体或实体们生命周期的正常事件序列。通过每个事件——实体改变其状态——通常可以方便地定义一个过程。在正常工作行为内部,有些操作上下文元素不 期望被改变。例如,当一个客户订购一个产品时,在整个提取订单、准备订单和交付订单的过程中,诸如价格、组成和交付方式等是不期望被改变的。
- 内部变化 —— 如上所述,在整个核心实体生命周期内,某些上下文元素不期望被改变。在现实世界中,这并不总是真理,因为管理决策或其他因素会迫使上下文元素变化。我们称内部系统属性的变化是内部变化。
- 外 部或环境变化 —— 不管一个企业如何相信他们“拥有”一个客户,但是这个客户总有保留改变他或她意志的自由。一个系统不太可能与外部系统(如客户或供应商)总是有一个“固定 的”合同。然而,正常工作很可能围绕这个“固定的”规则集合进行设计。我们称系统外部引起的变化为外部变化。
要对现实世界系统建模,就必须处理好这所有3种变化类型。这种模型与静态架构模型相比,在管理复杂性上有了极大的提高。从正常工作的观点来看,内部 和外部系统完全独立于主信息流运行。内部和外部系统甚至有它们自己的操作——管理有其自身的决策周期,客户有其自己的操作环境——由各自的信息流描述。就 信息流而言,这3种系统分别运行在3个平行宇宙中。解决这3种非同步信息流的唯一办法就是为每个信息流实现一个独立全面的变更管理。这种复杂交互在图3 (动态操作图)中表示。
邮轮公司给客户提供的服务可以作为解释动态操作的例子。在订购时,一个客户会决定在游览期间他计划参与的服务。但是,不仅客户可能在游览前和游览中 改变主意,而且邮轮公司也可能会为了利用新机会或应对未知事件改变服务。按照当前方法设计的系统,大部分变更都以极高的运营成本人工处理。使用基于动态业 务应用架构原则设计出的系统,来自客户和内部管理决策的变更由两个与正常工作完全集成的变更管理子系统负责。这不仅能降低成本,而且还提高了服务质量,甚 至可以最优化运营来提高利润。因为是完全通用的,它还可以重用标准组件。最终结果对于参与的每个人、业务、IT和客户来说是多赢的。

图 3. 动态操作有3个维度——正常事件、内部控制和外部反馈
动态业务应用的目标之一就是使设计和软件实现变得简单,以支持对所有业务来说司空见惯的动态系统。以我们的经验,与一个框架优先的工程学相结合是构建一个DBA的最有效的技术方法。
设计企业系统要求框架先行
构建动态业务应用说起来简单,做起来难。工程,包括软件工程,都采用了有百年历史的框架优先方法。设计一座桥梁、一架飞机或者甚至是一个软件应用都 使用相同的方法:一个设计团队先收集需求,然后使用一组定义良好的框架步骤来设计和构建系统。在设计桥梁和其他工程系统时,现有框架已被证明非常成功。但 在使用它来为业务系统开发软件时,却碰了壁。这两类系统之间有一个根本区别。在设计桥梁和飞机时,最终结果总是一个拥有静态架构的系统:当变更发生时,如 增加桥梁负重或飞机速度,整个系统可能就要重头重新设计。不幸的是,软件也常常使用一个静态架构进行设计:每当一个非预期的变更被引入到运营中时,很可能 所有事情都停了下来,使用包含新增功能的系统替代现有系统。因为业务运营在一个不断变化的环境中,而且比以往更依赖IT系统,这种停——走式的解决方案是 不可接受的。持续升级和在企业基础设施中集成系统的成本已经达到了无法支撑的地步。
依赖业务涉众作为我们全部需求的输入首先就是个问题。考虑到需求,目前还没有真正适合动态操作的“框架优先”软件设计方法。甚至卡内基梅隆软件工程 学院也表示架构是由场景驱动的,场景以涉众输入为基础被创建出来:“诱导一个软件密集型系统的业务目标是标准架构设计和分析方法的一个组成部分。业务目标 是驱动方法的引擎,通过将它们的实现作为场景……一个理想的设计方法或过程必须考虑众多涉众和众多影响。”
作为工程师,当我们收集系统需求时,我们怎能知道我们得到了正确的场景或是否遇上了正确的涉众?我们又如何能说明业务未来的变化,而这些变化通常连参与的涉众都不知道?
“企业经营者和企业设计师之间存在着根本区别。为了说明这点,考虑一下在一架飞机成功操作背后的两种最重要的人。一个是飞机设计师,另一个是这架飞 机的飞行员。设计师创造的飞机连平庸的飞行员都可成功驾驶。一般情况下,经理更像是飞行员而非一个设计师。一个经理管理一个组织,这和一个飞行员驾驶飞机 没什么两样。飞行员的成功依赖于那些创造了一架成功飞机的飞机设计师。另一方面,是谁来设计一家由管理者管理的公司呢?几乎从没有任何人有意识和有思想的 去设计一个组织,以取得有计划的增长和稳定性。在当前的管理学校中,教育培养的是企业的经营者,而如何设计企业几乎不受重视。”
在几年前完成的报告“设计未来”中,MIT的教授和系统动态之父Jay Forrester,指出这个问题是我们为企业设计系统的基本限制:
Forrester的观察进一步使当前的企业架构方法失效。在我们需要设计一个企业系统架构时,作为知识主要来源的涉众甚至是错误的分类。他们是企 业的“用户”,不是“设计师”。在设计飞机时,飞机设计师决不可能去询问飞行员或乘客关于飞机制造方面的事情。但是,在学校、工程或MBA课程中却没有企 业设计这门课。
那么回到企业架构,其中有一个根本的断档。企业架构的“用户”是企业涉众,很可能是熟悉企业动态性的企业毕业生,但是他们不熟悉工程方法。企业系统 的设计者和构建者——软件工程师——熟悉静态框架,但是对动态框架却很陌生。事实上,还没有说明业务动态的框架。根据企业架构框架,这是一个需要填补的缺 口。这个框架只描述企业是如何“被设计的”,但是也会定义开发路线图和主要组件,使用它们来构建企业的支持系统。

图 4. 企业架构的业务和工程方法
我们建议根本改变我们架构和设计信息系统的方式,以一种不要求业务涉众作为主要输入的方式。我们推荐利用一个以业务实体生命周期和事件模型为中心的框架作为架构的主要输入来源。业务场景只被用来微调一个已完架构,而不是作为主要驱动力。
这种框架优先方法从一开始就说明了业务动态,而不是事后诸葛亮。它暗示被设计的企业应用是动态适应的,而不是像由今天的方法论所实现的那样是静态适应的。这种方法成数量级的减小了开发和维护软件的成本,并砍掉了与改变和维护现有系统直接相关的超过70%的IT开销。

Fig 5. 基于框架的软件解决方案开发方法
在我们建议的方法中,业务场景只被作为一个已完架构的微调,而不是主要驱动力。我们将我们的直觉、经验和技巧输入到设计过程中越晚,产生导致巨大损失的错误的可能性就越少。由涉众输入引起的需求变更会在已经建好的现有解决方案框架中处理,减少了风险和延迟。
一份Standish报告研究表明,超过1千万美元的项目,成功率只有3%。行业咨询和哈佛商学院教授Andrew McAfee表示,部署如此高成本技术(如ERP、CRM、供应链管理、电子商务和其他企业应用)组织的成功率在25% ~ 70%之间。McAfee总结说:“这些面临的问题并非是单独的,它们都是同一事情的不同例子,基本上都是使用IT改变业务过程的结果。”。最近,这些百 分比有所提高,但是对于复杂系统IT仍缺乏清晰的路线。框架优先的方法能使业务变化集成到IT实现中,并提供了业务和技术之间更清晰的转换,可以将成功率 提高接近100%。建构复杂系统的时间由几个月或几年缩短至几天。
动态操作的服务器架构 —— 忘记SOA,迎接信息装配线
90年代早期,事件几乎是每本面向对象方法论书籍中的核心角色。随着象Windows这样基于GUI操作系统的出现,GUI开发平台依靠复杂事件模型来设计和构建单个应用。但是,在客户机-服务器环境中,服务器端的事件处理总是基于一个简单得多的模型。
当基于Web的技术(如J2EE和.NET)开始替代传统的客户机-服务器应用,客户机和服务器都经历了根本的转变。客户端的富OS事件模型由 Web浏览器和原始的脚本语言代替。在服务器端,事件处理由无状态、扁平的、静态架构所替代。其方式非常类似将网页传给Web浏览器的方式。
为了模拟现实世界的需求,需要一个有状态的、层次的、动态的和分布式的架构,设计必须严重依赖数据库来存储范围广泛的各种动态信息。但是根据其定 义,关系数据库非常适合存储不常变化的数据间关系。保存动态、分布式、层次的和有状态信息要求一个不同的基础设施,它不是以关系数据库为中心的。
基于Web的技术为支持现实世界系统引入了其他挑战。当J2EE应用服务器在一个分布式环境被使用时,使用Java作为编程语言的一个最大优势完全 没有了。Java虚拟机的垃圾回收从来没有被设计成能自动清除在多实例间交换的内存对象。在这种情况下,架构师和设计师必须编排整个对象生命周期,不考虑 编程语言的能力。甚至在数据保存在数据库中时,这种相同的数据清除问题也变得非常困难。
正如我们已经看到的,一旦我们能从正常工作中分离变化,架构就能只依赖事件和生命周期进行设计和实现。围绕生命周期进行系统设计已经经历了一个世纪——它被称为装配线。
在装配线于20世纪早期引入之前,制造业的工作方式多多少少和今天面向服务架构(SOA)处理信息的方式一样。每个对于SOA服务的调用一般都被视 为一个无状态调用。为了说明以前调用的历史,每个服务必须完全实现如何处理系统内部状态。一旦需求改变,几乎所有服务也需要以成本极高的方式重新编码来改 变它们各自的实现。

图 6. 服务器架构是以事件模型为基础的
我们建议使用以事件模型和生命周期为中心的信息架构作为业务需求和系统架构的双向翻译平台。事件模型在下一级被扩展成四个基本模型:状态、分布式、层次和动态。所有这5个模型可被基础需求和架构模型中的业务或技术人员方便地解释:
- 事件模型/生命周期—— 它是构建其他模型的基础核心。对业务用户来说,它是价值流,反映产品/服务生命周期和为客户创造价值的过程序列。对技术人员来说,同一事件序列反映了代表 业务实体对象的状态变化。最终结果是,事件模型扮演了解耦元素,大大简化了设计和实现。事件模型还扮演了另一个重要角色。因为其他四个模型是围绕事件模型 而构建的,它还扮演一个集成平台。事实上,事件模型是创造实现变更、层次和分布式组件的唯一办法。
- 状态模型—— 对业务用户来说,这个模型扮演了企业的面板,捕获当前经营的整体状况。对技术人员来说,它是系统的整体状态,它是全部生命周期实例的当前状态,以及它们的变化之和。
- 分布式模型 —— 对业务用户来说,这个模型捕获了其生命周期内控制产品/服务的各种组织。对技术人员来说,主实体只能基于分布式模型来控制。
- 层次模型—— 所有业务都有代表管理层级的层次结构。所有系统设计应该在架构上反映这种控制层次。正如销售VP不能给CEO下命令一样,低层级的组件不能给高层级的组件发送执行“命令”。
- 动态模型—— 继事件模型之后最重要的模型。业务用户使用它来捕获平时必须被处理的全部变更。如前所述,有两种变化类型:外部,如客户输入;内部,如管理决策。对技术人员来说,动态模型被翻译成一个匹配各种事件、生命周期和变化类型的插件架构。
这5个模型不仅可以被用在初始设计,对贯穿整个系统生命周期的需求变更亦有裨益。它们一起形成了自适应系统设计所缺失的框架步骤。这5个模型排除了用例作为企业架构的主输入的必要性。它们可以被翻译成一个清晰和全面的工程问题集合,与在桥梁或飞机设计中使用的方法类似。

图 7. 自适应架构是以5个模型为基础的信息架构结果
这5个模型定义了以信息变化和装配线为中心的动态业务应用通用架构。最重要的子系统是:静态模型、变更管理、虚拟装配对象和事件处理。在下一个细化层级,还有两个子系统:系统命令和控制与持久化。
这个架构还解决了在寻求一个适用于事务型工作流隐喻的通用解决方案过程中长期存在的问题,它是由Jim Gray[3]在 几十年前提出的。因为整个设计以单个事件的执行为中心,不仅可以实现“移动到下一个装配步骤”,而且也可实现“移动到前一个装配步骤”。实现需要根据用户 的输入决定前往哪个“方向”。通过将多个步骤“链接”在一起,可以使用相同的架构实现一个事务型工作流的“撤销(Undo)”操作。
动态业务应用的一个关键元素是事件处理。使用新的自适应系统信息理论,可以使用一个通用组件结构来“执行”每个事件。这个组件使用声明性编程来内嵌 业务逻辑、调用工作流引擎、调度器和业务规则引擎。这个实现不仅可以极大加速自适应系统开发,而且可以使后期改变非常容易处理,也减少了维护复杂集成的需 要。
我们建议围绕事件(操作)和事件生命周期创建一个供给控制、运营和环境的物理模型。生命周期控制器为离散事件管理装配信息。变更管理功能指导标准事件模型和个体事件内外部变更的执行。
结论
我们已经讨论了扁平、无状态、静态、客户端——服务器、基于Web的解决方案的演变方式所带来的IT架构和层次、有状态、动态、分布式业务的现实世 界之间的脱节。我们还讨论了传统工程方法为什么不能支撑能支持动态业务的自适应系统的开发。我们展示这两个问题的可能解决方案可以用一种新的模型驱动架构 方法来找到。
本文的第二部分将描述动态业务应用的可能架构,并给出一个案例研究,介绍我们概念的实际实现。
参考文献
[1] Yourdon Systems Method —— Model Driven Systems Development —— Yourdon Press, 1993
[2] Eric D. Beinhocker —— "The origin of Wealth", HBS Press Book,2006 —— 在他的新书“The origin of Wealth”中,麦肯锡公司高级顾问Eric D. Beinhocker声称,将经济视为一种静态、平衡的系统的传统观点正在经受一场彻底的反思,包括为数众多的原则。新的中心是:“复杂经济学”,其中经 济被视为一种高度动态的、不断演变、几乎无法预测的系统。这个摘录涉及在未来未知时公司如何来制定战略。
[3] Mark Whitehorn ,The Register, Interview with Jim Gray —— http://www.regdeveloper.co.uk/2006/05/30/jim_gray/
查看英文原文:Beyond SOA: A New Enterprise Architecture Framework for Dynamic Business Applications
[架构]什么是软件工厂
前言
Microsoft Patterns & Practices已经提供了不少“软件工厂(Software Factory)”,例如Smart Client Software Factory,Web Service Software Factory和Mobile Client Software Factory。而在CodePlex上也已经有了Microsoft P & P Team正在开发的下一代产品:Web Client Software Factory。这是一个非常有价值,非常值得关注的项目,目前正在以Weekly Drop的形式发布,按照计划将会在年底发布。在Terry Lee和我都曾经对它进行过介绍。
那么什么是“软件工厂”呢?它的作用又是什么呢?Jezz Santos在写了数篇有关这方面非常精彩的文章,他本人已经同意我将其文章进行翻译。我将陆续将它们翻译成中文,帮助大家和我自己理解一些概念。
原文信息
原文《FAQ – What is a Software Factory?》公布在Jezz Santos的博客上,版权归作者所有。
正文翻译
什么是“软件工厂”?这个问题经常能够引发下面的疑问:
如何给出软件工厂的定义?紧接着考虑下面的问题:我创建了一个命令行程序/一个指导性的包(Package)/一段脚本用于生成一些有用的组件 – 请您作出选择,它们也是软件工厂吗?
想用三言两语就给出清楚定义还真不是一件容易的事情。
背景
事实上这并不是个足够完整的问题,大多数人也正有这样的疑问,而且他们正在通过一些已经做好的东西或者重新去理解它们的区别,来设法搞清楚自己是否已经有了一个“工厂”或者“工厂”对他们来说还是个新事物。
解答
让我们先从“软件工厂”的官方定义开始谈起,然后我会尝试着使用更简单的词汇来解释它,并且使用一些示例来给出这些定义的衍生,希望能帮助您理解这些。
您能在这里找到一个官方而泛泛的定义,但是我想从Jack和Keth的那本有关软件工厂的书中所使用的那段短小些的定义开始。您也能够在我们的术语表中找到另一些定义。
“软件工厂是 使用一个基于软件工厂模式(Software Factory Schema)的软件工厂模版(Software Factory Template)来配置外部工具,过程和内容的一条产品线。通过适配(Adapting),装配(Assembling)和配置一些框架化的组件,能够 自动化地对一个典型架构的产品进行开发和维护变化的工作。”
呃……好吧(“咕嘟”——咽了一下口水), 这是一个学院派定义,我们来将它进行一点小小的延伸:首先,“软件工厂”的基本元素是一条使用一系列的工具(比如编辑器,代码模版,领域专用语言,命令行 工具等等)创建出来的“产品线”。这些工具会是用一个被称为“工厂模版”的东西来配置普遍适用的,能够扩展的开发工具(就像Visual Studio)。工厂模版实际上只是“工厂规范”的一个物理实现(例如,一个工厂模版包含了工具,指导和这些工具创建和配置一些东西的蓝本)。接着,则会 通过组装一些必须的组件,把这些工厂创建出来的不同产品的开发和维护过程进行自动化操作——理想情况下,这个过程会复用那些作为产品基础而存在的框架。
好吧,这里最关键的一点是产品线,工厂规范,它的工厂模版以及一个产品。在我看来,如果您有了所有这些,您就已经基本上接近于有了一个工厂了,因为剩下的玩意儿都是通过这些东西来得到的。
“框架”也是非常重要的东西,因为有了它, 您就已经在“为产品线上的产品定义变量”这个过程中迈出了一大步,大多数的工作它已经帮你完成了。请记住,您不应该为一个您还没有解决的方案创建一个工 厂,因此,创建一个工厂最基本的前提是:您已经有了对一些解决方案进行自动化操作的资本。
[当Visual Studio团队发布了一些能够帮助您自定义软件工厂的工具,那么微软对于这些术语的定义会变得更为具体,而且我猜想,如果要完全符合这个具体的工厂模 版,您只能使用被微软认可的软件工厂。但是就目前来说,您任何可以使用当前的工具和一些您自己的“创新”来创建符合上述定义的软件工厂。]
产品
让我们先从产品谈起,首先搞清楚一点:一个产品是一个特定的工厂创建的产物。
“一个产品是一个工厂生产线的最终产物”
千万不要将“生产线(production line)”和“产品线(product line)”混淆起来。
现在,一个产品并不需要真正的完成,结束,包装成一个像“Microsoft Word”这样的东西,虽然可能大多数人听到“产品”这个词的第一反应是这个。这个单词的含义只是描述一个工厂的产出。
例如,一个汽车引擎制造商生产出了汽车引擎,这就是它的“产品”,然后把引擎卖给其它的工厂,让它们将这些引擎组装进一辆汽车。一个汽车引擎依旧属于一个引擎工厂的“产品”。
所以,所有的工厂会建造一个有用的产品,这个产品可能会被组装进入一个更大的产品,当然也有可能不会。
工厂会为这个产品定义一个模型,这就是这个产品的“模式(schema)”,它包含了产品的各个部分和它们的变量。这个模型为用户提供了一个独立创建,配置或者维护产品各部分的方法。
工厂模式(Factory Schema)与工厂模版(Factory Template)
我们已经花了不少功夫定义了工厂模式(Factory Schema),所以我不打算在这里再完全重复一遍。
不过我们在这里这么说已经足够了:“工厂模式”是产品(和它的变量)以及它的各种配置的蓝图——也就是工厂产物的蓝图。它不只定义了工厂该建造那些东西,也包括建造这些东西方式,建造时所用的“零件”,以及在建造时所使用的工具。
顺便提一下,“工厂模版(Factory Template)”则是指工具、运行时、文档和框架等其它作为您的工厂的的组成部分一起发布的那些东西——例如一起打包在一个MSI安装文件中。
产品线
那么现在就谈一下产品线——这到底是什么?这可不是指“生产线”。
产品线,由工厂产物的变量组成。
好,为了真正对开发有用,工厂需要能够创建产品的多个实例(我们把其中的一个叫做一个“产品变量”)。更准确的说,一个产品“变量”就是每一个您生产出的实例,而不是产品的实际类型。一个工厂能够定义多种产品的“类型”(每一个对应了产品的一个可变的方面)。
因此,您的产品线定义了您工厂的解决方案领域。
[您能够在这里得到有关一个产品可变性以及其变量(是什么?应该如何使用?)更深入的讨论。]
那么,您的一个命令行程序/一个指导性的包(Package)/一段脚本是一个软件工厂吗?
[模式]软件工厂
Gunther Lenz是微软的一名架构师,Christoph Wienands是在西门子工作的一名软件工程师,两人担当起了一个项目,目标是评估以Microsoft Visual Studio 2005 Domain Specific Languages工具为基础建立的软件工厂(Software Factory)的优势。
虽然对于目前基于文本的软件开发工作来说,重用增加、抽象层次提高、自动化水平提高这些都是显而易见的好处,但还不足以向管理层及其他利益相关者证明先期投入的必要性。因此我们着手寻找定量的数据,以图清楚地在商业上确立这种范型。
该分析的目标是在框架及应用程序开发领域识别出自动化和重用的机会。为此作者开发了一个软件工厂模板(Software Factory Template):
软件工厂模板是在Software Factory Schema的基础上实现的。我们提供了多个T4模板——有工件生成、向导和DSL——全都包含在安装包里。这些模板给开发者提供了额外的工具和上下文敏感的自动化机制。
他们的项目分为两个部分:
经过初步研究,他们发现:
对于开发者来说,以前定义加上使用要输入320行的代码,而现在只需要输入几个字符串,然后决定几个选项就可以了。最终不但节省了时间,而且质量也提高了——因为只能在合理的选项中选择,而且在工件生成之前还会对输入进行验证。
总而言之,结论就是:
DSL开发成10倍地提高生产率,投资收益率超过100%。
对于指导包(guidance package),即使在最低投入的情况下也可以达到超过300%的投资收益率。
作者们计划在未来解决一些更复杂的问题:
- 如何将此方法扩展到更广阔的领域?
- 如何让工具适应更复杂的实现?
- 能够节省多少维护工作?
- 如何处理DSL元模型及模型的演进?
[服务]豆瓣的API服务使用指南
豆瓣 API 使用指南
豆瓣API是豆瓣为第三方开发人员提供的编程接口。利用豆瓣API,你可以在你的网站或程序中使用豆瓣的数据和功能。 目前的豆瓣API支持的功能包括:
- 搜索并查看书籍、电影、音乐信息
- 搜索并查看用户信息
- 查看用户收藏
- 添加、更新、删除用户收藏
- 查看评论
- 发布、修改、删除评论
在未来我们会支持更多的功能,例如查看、增删友邻.
本文档为需要快速了解豆瓣API的用户提供一个概览, 更为完整的豆瓣API信息请参阅 豆瓣API 参考手册. 现在可以使用Python 或者Javascript客户端进行开发,欢迎爱好者提供其他语言的客户端。
如果你编写了一个有趣的API应用,欢迎到API小组发布, 另外也欢迎到API小组参与豆瓣API的讨论,提出你对豆瓣API各种意见和建议。
Python客户端: http://code.google.c
Java客户端: http://code.google.c
ActionScript客户端: http://code.google.c
目录
API Key
为了保护豆瓣用户的数据;防止API被滥用或恶意使用,豆瓣要求每个API的使用者申请一个API Key, 而每个API Key唯一标识一个API使用者. 你可以在申请页面获得API Key, 在页面中填写必要信息后提交,你会得到你的API Key,例如
c4579586f41a90372f762cb65c78be5d
在之后的API使用过程中,你需要在请求中包括apikey参数,例如
http://api.douban.com/people/1345767?apikey={apikey}
注:如果只是想试验一下API,豆瓣也允许在不申请API Key的情况下进行API调用。不过在这种情况下,API调用被限制为每分钟请求不超过10次。使用API Key时,对访问的限制较为宽松,为每分钟40次,超过限制的话会被封禁(包括豆瓣主站的访问)。
快速入门
示例
下面通过一个简单的示例来演示豆瓣API的使用.
这个示例中展示了使用API获得ID为1220562的书的信息, 请求的url如下(注意将{yourapikey}替换为你的API Key)
http://api.douban.com/book/subject/1220562?apikey={yourkeyapi}
返回值为XML文档
<?xml version="1.0" encoding="UTF-8"?> <entry xmlns="http://www.w3.org/2005/Atom" xmlns:gd="http://schemas.google.com/g/2005" xmlns:opensearch="http://a9.com/-/spec/opensearchrss/1.0/" xmlns:db="http://www.douban.com/xmlns/"> <category scheme="http://www.douban.com/2007#kind" term="http://www.douban.com/2007#book" /> <db:tag count="20" name="片山恭一" /> <db:tag count="14" name="日本" /> <db:tag count="10" name="小说" /> <db:tag count="8" name="日本文学" /> <db:tag count="4" name="爱情" /> <title>满月之夜白鲸现</title> <author> <name>(日)片山恭一</name> </author> <content> 冷静地想一想吧!公寓的一个房间里只有两上人,连被褥都铺好了,理应是犯 错误的时候,我为什么却这么拘谨?一直胆小的毛病又犯了。事到如今,不要 说上床,就连接吻也还差得很远。这就是横亘在男人和女人之间的又深又暗的 河流吗? 如果有可能,我希望把手伸进她的心里,把她内心所有的东西都掏出来。她对 我究竟是一种什么感觉呢?我在她的心昊确实占有一席之地吗?抑或只是她生 命中的匆匆过客? </content> <link rel="self" href="http://api.douban.com/book/subject/1220562" /> <link rel="alternate" href="http://www.douban.com/subject/1220562/" /> <link rel="image" href="http://otho.douban.com/spic/s1747553.jpg" /> <db:attribute name="author">(日)片山恭一</db:attribute> <db:attribute name="isbn10">7543632608</db:attribute> <db:attribute name="isbn13">9787543632608</db:attribute> <db:attribute name="pages">180</db:attribute> <db:attribute name="tranlator">豫人</db:attribute> <db:attribute name="price">18.00</db:attribute> <db:attribute name="publisher">青岛出版社</db:attribute> <db:attribute name="binding">平装(无盘)</db:attribute> <db:attribute name="author-intro"> 片山恭一,1959年生于日本爱媛县,九州大学农学系农业经济学专业毕业。 学生时代通读了包括夏目漱石和大江健三郎在内的日本近现代文学全集, 同时读了笛卡尔、莱布尼茨到结构主义的欧洲近现代哲学。也读了马克思。 学士论文写的是马克思,硕士论文写的是恩格斯。二十二三岁开始创作小说。 代表作有《在世界中心呼唤爱》、《世界在你不知道的地方运转》、《气息》、 《别相信约翰·列侬》、《满月之夜白鲸现》、《空镜头》以及新作 《倘若我在彼岸》、《雨天的海豚们》等。 </db:attribute> <id>http://api.douban.com/book/subject/1220562</id> <gd:rating min="1" numRaters="39" average="3.69" max="5" /> </entry>
通过请求中包括alt参数,你可以改变返回值的格式。 目前支持的alt参数值包括:
- atom (默认)
- json
通过将alt设置为json来获得json格式的返回值:
{"category":{
"@scheme":"http://www.douban.com/2007#kind",
"@term":"http://www.douban.com/2007#book"},
"db:tag":[
{"@count":20,"@name":"片山恭一"},
{"@count":14,"@name":"日本"},
{"@count":10,"@name":"小说"},
{"@count":8,"@name":"日本文学"},
{"@count":4,"@name":"爱情"}
],
"title":{"$t":"满月之夜白鲸现"},
"author":[
{"name":{"$t":"(日)片山恭一"}}
],
"content":{"$t":"冷静地想一想吧!公寓的一个房间里只有两上人,连被褥都铺
好了,理应是犯错误的时候,我为什么却这么拘谨?一直胆小的毛病又犯了。事到
如今,不要说上床,就连接吻也还差得很远。这就是横亘在男人和女人之间的又深
又暗的河流吗?\n 如果有可能,我希望把手伸进她的心里,把她内心所有的东
西都掏出来。她对我究竟是一种什么感觉呢?我在她的心昊确实占有一席之地吗?
抑或只是她生命中的匆匆过客?"},
"link":[
{"@rel":"self","@href":"http://api.douban.com/book/subject/1220562"},
{"@rel":"alternate","@href":"http://www.douban.com/subject/1220562/"},
{"@rel":"image","@href":"http://otho.douban.com/spic/s1747553.jpg"}
],
"db:attribute":[
{"$t":"(日)片山恭一","@name":"author"},
{"$t":"7543632608","@name":"isbn10"},
{"$t":"9787543632608","@name":"isbn13"},
{"$t":"180","@name":"pages"},
{"$t":"豫人","@name":"tranlator"},
{"$t":"18.00","@name":"price"},
{"$t":"青岛出版社","@name":"publisher"},
{"$t":"平装(无盘)","@name":"binding"},
{"$t":"片山恭一,1959年生于日本爱媛县,九州大学农学系农业经济学专业
毕业。学生时代通读了包括夏目漱石和大江健三郎在内的日本近现代文学全集,
同时读了笛卡尔、莱布尼茨到结构主义的欧洲近现代哲学。也读了马克思。
学士论文写的是马克思,硕士论文写的是恩格斯。二十二三岁开始创作小说。
代表作有《在世界中心呼唤爱》、《世界在你不知道的地方运转》、《气息》、
《别相信约翰·列侬》、《满月之夜白鲸现》、《空镜头》以及新作《倘若我在
彼岸》、《雨天的海豚们》等。","@name":"author-intro"}
],
"id":{"$t":"http://api.douban.com/book/subject/1220562"},
"gd:rating":{"@min":1,"@numRaters":39,"@average":"3.69","@max":5}
}
JavaScript 支持
对于JavaScript开发人员,豆瓣API提供一种更简单的方式可以直接在HTML页面中使用API, 下面给出这种使用方式的简单示例。
首先你需要在HTML页面中如下script标签:
<script type="text/javascript" src="http://www.douban.com/js/api.js?v=2" />
之后你需要在JavaScript中设置好你的apikey:
DOUBAN.apikey = 'c4579586f41a90372f762cb65c78be5d'
而后你就可以调用豆瓣API,其中通过定义callback函数来操作返回的JSON数据:
DOUBAN.getMovie({
id:'2340927',
callback:function(movie){
var title = movie.title['$t'];
...
}
})
此外,豆瓣也提供了解析函数来帮助你更容易地使用JSON格式的返回值。使用豆瓣提供的解析函数,你需要在页面中添加如下的script标签:
<script type="text/javascript" src="http://www.douban.com/js/api-parser.js?v=1"></script>
接下来你就可以使用豆瓣提供的解析函数来处理返回值,例如:
var book = DOUBAN.parseSubject(result)
解析函数返回更容易使用的javascript对象,例如你可以这样得到书的封面图片:
book.link.image
下面HTML页面使用API获得ID为2340927电影的信息并展示在页面上(注意将{yourapikey}替换为你的API Key)
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML xmlns="http://www.w3.org/1999/xhtml">
<HEAD>
<TITLE></TITLE>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
<style>
body {padding:0;margin: 0;background: #FEFEFE;}
body,td,th { font: 12px Arial, Helvetica, sans-serif; line-height: 150%; }
span.title {
margin:5px;
}
</style>
<script type="text/javascript" src="http://www.douban.com/js/api.js?v=2"></script>
<script type="text/javascript" src="http://www.douban.com/js/api-parser.js?v=1"></script>
<BODY>
</BODY>
<script>
DOUBAN.apikey = {yourapikey}
DOUBAN.getMovie({
id:'2340927',
callback:function(re){
var subj = DOUBAN.parseSubject(re)
var tl = subj.title ? subj.title : "";
var author = subj.author ? subj.author : "";
var di = subj.attribute.director ? subj.attribute.director.join(' / ') : "";
var tmp = "<img src="+subj.link.image+" style='margin:10px;float:left'>";
tmp += "<div>Title : <a href="+subj.link.alternate+" target='_blank'>"+tl+"</a></div>";
if (subj.attribute.author) tmp += "<div>Authors : "+(subj.attribute.author.join(' / '))+"</div>";
if (subj.attribute.director) tmp += "<div>Director : "+(subj.attribute.director.join(' / '))+"</div>";
if (subj.attribute.cast) tmp += "<div>Casts : "+(subj.attribute.cast.join(' / '))+"</div>";
if (subj.attribute.aka) tmp += "<div>A.k.a : "+(subj.attribute.aka.join(' <br/> '))+"</div>";
if (subj.attribute.language) tmp += "<div>Language : "+(subj.attribute.language.join(' / '))+"</div>";
if (subj.attribute.country) tmp += "<div>Country : "+(subj.attribute.country.join(' / '))+"</div>";
if (subj.rating.average)
tmp +="<div>Rating: "+subj.rating.average+" / "+subj.rating.numRaters+decodeURI("%E4%BA%BA")+ "</div>"
tmp += "<p>"+(subj.summary ? subj.summary : "")+"</p>";
document.body.innerHTML = tmp;
}
})
</script>
</HTML>
注:实现上,豆瓣API使用 JSONP 方式来支持跨域调用API 因此你也可以使用自己熟悉的javascript库来调用JSONP风格的豆瓣API. 此时,你需要将alt设置为xd同时提供callback参数。
例如使用JQuery,前面的例子则可以写为
$.getJSON("http://api.douban.com/movie/subject/2340927?alt=xd&callback=?", function(movie){
var title = movie.title['$t'];
...
});
Mikel
