[Saas]SAAS(软件即服务)的时代即将来临吗?

mikel阅读(851)

在详细展开本文讨论之前,我们先来介绍两家公司:
1、37signals
http://www.37signals.com/
只要是RoR的fans没有不知道37signals的。这是一家位于芝加哥的创业型小公司,公司小到只有8个员工,但在业界享有盛誉。在芝加哥 办公室上班的有4个人,其余4个人分别在纽约,波特兰等城市soho办公。但是37signals公司基于web的小型商业软件产品的注册用户却超过了 100万。下图是该公司全部员工的合影(RoR作者DHH是左边稍靠后的那个人,貌似姿势挺猥琐):

37signals公司出版了一本名为《Getting Real》的书(中文名称《把握现实》),充分阐述了基于互联网web2.0商业创业的经验,可谓web2.0创业的圣经。
美国时代周刊记者采访了37signals公司,专门写了一篇稿件《Small Is Essential》,揭示了该公司的主要商业模式。
37signals公司主要提供基于互联网的小型商业软件租用服务:有提供项目管理和团队写作的BaseCamp(这也是导致RoR诞生的产 品);有小型客户关系管理系统Highrise;有即时团队在线交流系统Campfire;有信息组织,日历,工作安排软件Backpack等等。
用户在线注册37signals的系统,在线使用,每个月交纳12美元到149美元不等的月租费。37signals的在线软件租用服务对于小型商业公司具有很大的吸引力,功能好用,操作简单,价格便宜,无需维护,注册即可以使用。
2、salesforce
http://www.salesforce.com/
salesforce是业界赫赫有名的一家在线CRM厂商,其创始人来自前Oracle公司的高管。公司在2004年就上市了。 salesforce这两年处于一个飞速发展的阶段:2004年7月,Salesforce刚上市时,股票交易价为11美元。到2006年,它的股价到达 在40美元。成立7年多的Salesforce.com在全世界的2.48万个公司拥有超过50.1万个用户,并以每年80%的速度增长,客户满意度高达 97%。Salesforce在2006年二季度的收益从7190万美元增加到1.181亿美元左右。如今salesforce已经进入中国,提供在线 CRM系统。提供每人每年888元人民币软件租用服务费。
看完上面两家公司的介绍,你一定会说,他们做的不就是ASP(应用服务提供商)吗?是的,从某种程度上来说,他们就是ASP。其实ASP早就伴随第一次互联网浪潮的时候已经唱了一出狼来了的故事,但是现在ASP会卷土重来吗?让我们再来看看大公司的反映:
1、Google Apps
http://www.google.com/a/
不用多说了,Google Apps是google将其强大的互联网服务伸向企业应用领域的重要尝试之一。
2、Microsoft
Microsoft是典型的传统套装软件生产商,但是不意味着Microsoft不重视SAAS领域。附件是Microsoft的一系列关于SAAS文章当中的一篇。
Bill Gates今年访问中国之行,其中有一项重要的成果,就是和阿里巴巴达成的合作的协议。而这项合作的内容就是和阿里软件公司共同构建基于微软技术的SAAS平台战略。这项合作的意义可以说相当深远。
关于SAAS更多深入探讨,推荐大家看一下这篇文章:
http://www.donews.com/Content/200611/f4c23d1d6dc745f483df8e787ca0251d.shtm
SAAS的商业模式相比传统的套装软件有什么优势呢?对于软件供应商来说:
1、短开发周期,低成本,快速用户反馈
看看37signals就知道了,不再需要漫长的开发周期,可以尽快提供用户使用,搜集用户反馈,大大压缩软件开发成本,降低软件的市场风险,而且可以提高软件的市场需求适应程度的敏捷性。
2、相比卖套装软件来说,无需建设庞大的销售渠道,可以充分利用互联网长尾效应
这是一个伟大的进步,诞生于互联网的长尾效应真正要在企业应用领域发挥作用了。看看37signals和salesforce,数量庞大的中小企业购买他们的在线企业服务。这些中小企业在传统的软件营销渠道来说,都是处于被放弃的状态。
对于传统套装软件的销售来说,不管是Oracle,SAP还是RedHat,他们往往集中销售资源去攻克传统大客户,例如四大银行,电信,创造公 司超过一半的销售利润,另外通过合作伙伴建设销售渠道以分销的方式面向中小型客户。越是小型客户,越得不到重视,长尾末端的客户完全被放弃。
根据长尾效应我们知道,传统销售被放弃的那条长长的尾巴加起来其总市场价值等价于传统销售的市场价值。这也是Google赖以创造惊人利润的主要 原因。但是传统销售资源的有限性不允许他们对于尾巴用户投入资源。但是SAAS这种在线电子商务解除了资源限制。可以让长尾充分发挥了。
3、软件技术支持的成本大幅度降低
看看37signals,根本没有技术支持人员。互联网AJAX技术的兴起,使得基于浏览器的应用完全可以轻松处理复杂的企业应用操作。集中式的服务器使得软件在线升级几乎0成本。你还需要庞大的call center吗?你还需要庞大的技术工程师队伍吗?
对于客户来说,SAAS也带来了很多优势:
1、购买软件的风险大大降低了
你不用再担心花几十万购买的套装软件根本不合适,或者花几十万外包的项目开发出来的软件根本不符合需求。简单的注册流程之后,立刻开始使用,在试用期你就知道这个软件你应该不应该花钱购买了。
2、购买软件的成本大大降低了
不需要一次性付款,每个月按功能,按注册人数付费,就像分期付款一样,不会有浪费。如果你什么时候觉得软件不合适了,随时可以中止付费。
3、立即可用的软件
你不需要花很多时间去考察,去开发,去部署,当你需要,你就可以立刻使用,为你的公司争取了宝贵的时间。
但是SAAS没有一点问题吗?ASP是怎么倒下的?如果我们提供SAAS服务,那么会面临怎样的问题呢?
1、公司的信誉度的问题
如果你的公司明天就会倒闭的话,哪个客户敢用你的服务?如果你不是一个行业知名的大公司的话,很多客户不敢购买和使用你的服务,把自己公司业务押在你的身上。
2、安全和保密的问题
你怎么保证客户的资料不被外泄?你怎么保证你不会利用客户资料进行非法的勾当?客户为什么要信任你?
3、网络服务质量的问题
中国的电信运营商的网络安全保护水平实在令人担忧,如果你提供互联网服务,因为网络问题导致网站不能访问,网民最多诅咒几句,如果是你的客户把自己公司业务放在你的服务器上面,那么倒霉的就是客户了。
所以SAAS其实是很有高的门槛了,因为你必须有知名的品牌来让客户信任你,你必须有强大的资源来提供安全和可靠的服务。
salesforce是目前进入国内做SAAS服务比较早的一个,随后还有阿里软件。这里不得不提一句阿里软件,因为阿里软件有很大的优势:通过 阿里巴巴网站聚敛了几百万中小型商户;多年互联网经营建立的品牌信誉度;和Microsoft合作强强联合以求强大的技术支撑;全国部署的上万台服务器提 供了充分安全的网络质量和故障转移方案。可以说阿里软件想不成功都很困难,这里不得不佩服马云。当然除了阿里巴巴以外,国内的金蝶,用友,金算 盘,800CRM都盯着这个市场,我相信在未来的几年,SAAS会成为一股潮流。
作为技术人员的角度来看待SAAS,我觉得有两点值得一提:
1、互联网应用和企业应用的界限会越来越模糊
很多开发人员言必称超级复杂的企业应用,动辄分布式事务,消息中间件。确实,在传统的大客户大项目当中,这些超级复杂的企业应用还会长期存在。但 是不可否认的一个事实就是,企业应用的开发人员会越来越多被要求从事SAAS类型应用的开发当中来。在这个领域,不是超级复杂的企业应用游戏规则,而是互 联网游戏规则:快速开发,快速反馈,简单架构,小团队,随需应变。因此解决传统超级复杂问题的编程工具利器也许不那么有效了,在这个新的领域,需要的是轻 量级的超级快速编程工具利器。
2、你想做中国的37signals吗?
如果你可以开发出很酷很好用,而且市场很需要的在线软件产品,就去做吧,不论是面向小型企业,还是面向消费者个人群体,我都相信这种在线软件消费模式在未来会广阔的市场前景。

[JQuery]Jquery弹出层插件Thickbox使用心得

mikel阅读(864)

Jquery弹出层插件Thickbox使用心得

   前段时间在建设银行项目上用EXT完整做了个单页系统,太赶了,没有记录下任何东西,现在都忘了,怪可惜的。这次项目用JQuery做js的东西。主要用了个弹出层控件thickbox,自己也扩展和修改了一下。这里就记下来,也提供大家下载,希望对大家有用吧。

  thickbox官方网站(上面有例子和基本的使用方法):http://JQuery.com/demo/thickbox/

  就我使用过程中,thickbox常见问题:

  1。跨iframe的弹出层。

   症状:每次thickbox都只在frame中弹出,而不会整个屏幕覆盖

  原因和解决方法:

          thickbox使用tb_show()函数在body后面加入弹出层。可以使用window.top.tb_show()把弹出层加到页面上。我的tihickbox插件中修改如下:在tb_init()中把tb_show(t,a,g)替换如下

  if(a.indexOf('TB_iniframe'!= 1
     {  
          window.top.tb_show(t,a,g);  
     }  
     
else  
     {  
      tb_show(t,a,g);  
     }  

 这样只只要在原来的链接上加入TB_iniframe=true即可,如div.aspx?height=180&width=400&TB_iframe=true&TB_iniframe=true&modal=true

   2.thickbox只支持一层弹出,不可支持多层弹出。

       修改过的控件已经支持(不足:ie6下失效弹出层失效了,占时没解决,哈哈)

  3. 弹出层关闭后,文本框无法聚焦。

    症状:关闭弹出层后,原来页面上的文本框无法聚焦

    原因和解决方法:这个的原因不好说,很多人都认为是ie本身的bug。是由于iframe没有移除,即使移除了。内存上也么有清除造成的。 这也是我猜的。哈哈。解决方法是在tb_remove()中先手动移除iframe然后,在强制做垃圾回收,至少我是可以啦。哈哈。代码如下:

 1function tb_remove() {
 2    var seq=PopSeq();
 3     $("#TB_imageOff"+seq).unbind("click");
 4     $("#TB_closeWindowButton" + seq).unbind("click");
 5
 6     $("#TB_window" + seq).fadeOut("fast", function() {
 7         ///手动移除ifrmae,IE的一个bug
 8         $('#TB_iframeContent' + seq).remove();
 9         $('#TB_window' + seq + ',#TB_overlay' + seq + ',#TB_HideSelect' + seq).trigger("unload").unbind().remove();
10        ///自己调用垃圾回收,强制清楚iframe内存,解决文本框无法输入问题。
11         CollectGarbage();
12     }
);
13    if (typeof document.body.style.maxHeight == "undefined"{//if IE 6
14        $("body","html").css({height: "auto", width: "auto"});
15        $("html").css("overflow","");
16    }

17    document.onkeydown = "";
18    document.onkeyup = "";
19    return false;
20}

 

 4.在ASP.NET中如何动态设置需要的参数和关闭弹出层。

症状:thickbox提供的例子都是需要在input后a的class加thickbox,而且参数什么都是固定的。 而我们传递的参数一般需要动态。

解决方法,使用ASP.NET ajax,不多说了。直接看代码吧。

封装一个popup类,

 1public class Popup
 2{
 3    /// <summary>
 4    /// show the pop up div
 5    /// </summary>
 6    /// <param name="panel">container the button</param>
 7    /// <param name="url"></param>

 8    public static void ShowPopup(UpdatePanel panel, string url)
 9    {
10        ScriptManager.RegisterClientScriptBlock(panel, panel.GetType(), "ShowPopup""ShowPopup('" + url + "')"true);
11    }

12
13    /// <summary>
14    /// 
15    /// </summary>
16    /// <param name="panel"></param>
17    /// <param name="page">request page</param>

18    public static void ClosePopup(UpdatePanel panel)
19    {
20
21        string js = " self.parent.tb_remove();";
22
23       ScriptManager.RegisterClientScriptBlock(panel, panel.GetType(),"closepopup", js, true);
24    }

25}

 需要的js

1function ShowPopup(url) {
2    window.top.tb_show(null, url, false);
3
4}

 页面上例子

1///add按钮需要放在updatepanel里面   
2 protected void btnAdd_Click(object sender, EventArgs e)
3    {
4///自己组参数
5        string url = "aa.aspx?height=180&width=400&Type="+ddlType.SelectedItem.Value;
6        url += "&TB_iframe=true&TB_iniframe=true&modal=true";
7        Popup.ShowPopup(this.upButtons, url);
8    }

[MVC]融合思想:深入探索S#arp架构

mikel阅读(1003)

开发Web应用程序是痛苦的。无论是组织与测试AJAX脚本,还是在无状态环境下模拟状态,开发Web应用程序需要在计划和开发的各个阶段中,全神贯注,专心致志。Web开发人员还要面对开发中的诸多挑战,例如对象与关系的不匹配;在纷繁复杂的选项中选择最合适的工具提高开发效率;在项目中应用良好的架构,在保证代码具有可维护性的同时,不至于影响项目的交付。种种问题都使得开发形势变得越发严峻。

一切都在发展之中。不断涌现出的技术与技能虽然正在逐步解决这些开发中的难题,但没有任何一个可以单独扮演银弹的角色。但是通过对各种技术的权衡,精心挑选技术与技能,还是可以在不牺牲质量的前提下,大幅度地提高开发效率与可维护性。本文关注于Web开发的主流发展方向,通过使用S#arp架构,这是基于ASP.NET MVC的一个框架,荟萃了这些技术与技能的精华,从而为客户贡献价值。

赢得关键阶段的技术趋势

如果要用一个词来描述软件开发这个行业,毫无疑问,这个词语就是“变化”。比起那些历史久远的学科如土木工程学,我们这个行业不过是刚刚兴起,还处于萌芽状态。我们正在成长,而成长的烦恼就是行业自身经历的大量变化,而且看起来这样的变化还会持续一段时间。

这种变化可谓屡见不鲜。一个例子是项目管理方法学,由于其走向误区而带来的惨痛经历,它经历了大起大落,从一棵冉冉升起的“明星”,迅速衰落至默默无闻。另一个例子是有关技术的兴衰,长江后浪推前浪,新技术总是后来居上,取代旧的技术。以ASP.NET中的MVP(Model View Presenter)模式为例,该设计模式虽然为ASP.NET引入了更好的可测试性,但却增加了复杂程度。最近,微软引入了ASP.NET MVC用 来取代ASP.NET,它将之前ASP.NET实现的可测试性提升了一个新的台阶。由于要考虑.NET web开发的可测试性,MVP模式总是将控制器逻辑放在ASP.NET页面后面的代码(code behind)中,而ASP.NET MVC则抛弃了MVP的这种做法。这并不是说MVP隐含的原则到现在已经无效,而是随着这项技术的出现,通过使用适当的关注分离,可以更好的简化MVP实 现可测试性的目标。

因此,虽然软件行业仍然变化莫测,但技术发展的特定趋势与设计理念,仍然构成了开发与交付高质量可维护项目的基础。或许,这些理念的实现会随着时间 的推移而发生变化,但理念本身却是成功软件的坚实基础,能够持续地影响着软件开发。下面,我将简要地回顾这些设计理念,它们在关键阶段所获得的成功,已被 开发社区所广泛接受,因而对未来的软件开发产生了深远的影响。

抽象的基础功能

在不久之前,我还在为一个新对象编写CRUD功 能而被搞得焦头烂额。这项工作就像重新粉刷我的房子一样,费力而不讨好。充斥的大量重复代码,也成为了错误的多发地带。从编写存储过程与ADO.NET的 适配器,到测试片断的JavaScript验证代码,我发现我每天都将大把精力投入到了这些基础功能细节的实现上,以至于在我写完这些代码之后,巴不得赶 尽将它们抛诸脑外。

范式的转换在过去十年间已经发展成熟,其中关于基础功能的实现细节,属于最底层的工作,最好能够交给专门的工具来完成。面临的挑战是需要为这项工作找到合适的工具,既要允许软件忽略这些实现细节,又要保证基础功能可以使用。NHibernate是 这类工具的典范。它能够处理普通的.NET对象与关系数据库之间的持久化。采用这种方式,它就能够让对象自身完全忽略如何实现持久化,又能够解决对象与关 系之间的不匹配。而且,它不需要编写任何一行ADO.NET代码或者存储过程。NHibernate是一个非常棒的工具,更重要的是它实现了一个远大目 标,通过提供一个固定的解决方案,避免乏味而又琐碎的基础功能实现。要知道,这些实现在过去可是开发活动的重要组成部分。

随着时间的推移,软件行业引入了大量成熟的工具与技术,对这些乏味无趣的基础设施构建进行抽象,然后在开发完成后再进行设置。例如,随着NHibernate的逐渐成熟,那些附加的插件,例如Fluent NHibernate具有自动映射的能力,完全能够减轻管理数据访问的负担。这一现象印证了Douglas Hofstandter著作Gödel, Escher, Bach中提到的预言,即:采用合理的抽象是软件开发的发展之道,需大力提倡。

松散耦合

遗留软件系统最常见的毒瘤是紧耦合。(我在这儿使用的“遗留”一词,指的是那些其他开发人员强加给你的破烂软件,或者是你自己在很多年前开发的破旧 系统)紧耦合的例子多不胜数,例如两个对象之间存在双向依赖;具体依赖于服务的对象如数据访问对象;还有在单元测试中,如果依赖的服务断开或不可用,就无 法测试服务的行为。紧耦合会导致脆弱的代码,使代码难以测试。修改紧耦合的代码,会让开发人员视如畏途,不战而降,甚至逃之夭夭。毫无疑问,一个成功软件 的关键就是松散耦合。

维基百科将松散耦合解释为“两个或多个系统之间的弹性关系。”因此,松散耦合带来的好处就是你能够修改编程关系的任何一方,却不会影响另外一方。举例说明,在我看来,没有哪个设计模式能够比接口隔离,别名为依赖倒置(不要与依赖注入混为一谈)更能够说明松散耦合的思想了。该技术通常会用于将数据访问层从领域层中分离。

例如,在一个MVC应用程序中,控制器或者应用服务需 要与数据访问的仓储(repository)对象通信,以获取数据库中的项目个数。(本例中的仓储指的是“服务”)要满足这一需求,最简单的办法是让控制 器建立一个新的仓储对象的实例。换句话说,控制器创建了一个指向仓储对象的具体依赖;例如通过new关键字。遗憾的是,这种方法会导致紧耦合的诸多不良后 果:

  • 没有真实的数据库支持对仓储的查询,就很难对控制器进行单元测试。要求真实的数据库,会导致单元测试变得脆弱,一旦数据被前 一次运行的测试所修改,就会带来问题。在测试控制器逻辑时,我们应集中关注验证控制器的行为,而不是它所依赖的仓储对象能否成功地与数据库进行通信。此 外,测试一个“真实的服务”,例如在刚才提及的例子中,与真实数据库通信的仓储服务,会使单元测试的运行变得像老牛拉破车一般的缓慢;结果会让开发人员停 止运行单元测试,从而损害了代码的质量。
  • 如果不修改实例化服务的控制器,就难以替换服务(仓储服务)的实现细节。假如你希 望将仓储从ADO.NET改为支持Web Service,由于包含了一个与前者之间的具体依赖,如果不对实例化以及使用它的控制器进行大量修改,就无法轻易地将其替换为后者。在多数情形下,一旦 变化发生,就会导致霰弹式修改——这是说明这一问题的另一种坏味道。
  • 无法确定控制器实际拥有的服务依赖的个数。换句话说,如果控制器正在调用一些服务依赖的创建功能,则开发人员很难确定其逻辑边界,或职责范围。作为替代,控制器的依赖可以通过其构造函数传入,这样就能够使开发人员更容易理解控制器的整个职责范围。

替代方案是把服务依赖作为控制器构造函数的参数。这种做法带来的关键改善就是控制器只需要了解服务依赖的接口,而不是具体的实现。为了进一步说明,可以比较下列两段在ASP.NET MVC应用程序中的控制器代码。

如下的控制器直接创建了它的服务依赖CustomerRepository,也就相应拥有了一个具体的依赖:

public class CustomerController {

   public CustomerController() {}

   public ActionResult Index() {

      CustomerRepository customerRepository = new CustomerRepository();

      return View(customerRepository.GetAll());

   }

}

相反,如下的控制器则将服务依赖作为接口参数传递给它的构造函数。也就是它与服务依赖形成了松耦合关系。

public class CustomerController {

   public CustomerController(ICustomerRepository customerRepository) {

      this.customerRepository = customerRepository;

   }

   public ActionResult Index() {

      return View(customerRepository.GetAll());

   }

   private ICustomerRepository customerRepository;

}

对比紧耦合的缺陷,这种清晰的分离方式带来了诸多好处:

  • 领域层对于如何创建仓储对象以及它的实现细节一无所知,它只需调用公开暴露的接口。因此,它不需要修改控制器自身,就能很容易地更换数据访问的实现细节(例如从ADO.NET切换到Web Service)。这基于一个假定,两者实现的接口是相同的。
  • 依赖于接口而不是具体实现,在单元测试时更容易将仓储的测试替身(test double)对象注入。这就保证了单元测试能够快速运行,避免维护数据库中的测试数据,从而将测试的注意力放到控制器的行为上,而不是与数据库的集成。

这里并没有详细阐述依赖注入必须支持一个分离的接口和其它松耦合技术。更详细的讨论请参考文章依赖注入实现松耦合。(注意,在这篇文章中描述的“仿(mock)”对象实际上是“桩(stub)”对象。它与“测试替身(test double)”的术语来源于Martin Fowler的Mocks不是Stubs)。此外,将服务依赖重构到接口隔离一文详细地介绍了如何将设计转为接口隔离设计模式。

测试驱动开发

简而言之,测试驱动开发(TDD)能够有效交付高质量、可维护的软件,全面地简化设计。作为一项开发技术,测试驱动开发绝不会是昙花一现;它受到了越来越多的吹捧,是能够决定软件开发成败的关键,推动了我们这个行业逐渐走向成熟。

TDD隐含的基本思想是带着疑问开始软件开发,在开发过程中要不停地询问系统。例如,倘若你正在开发一个银行系统,可能需要询问系统,它有能力成功 处理客户的存款业务吗?关键之处在于要在实现行为细节之前提出问题。随之而来的好处就是在编写系统之前,它能够让开发者将注意力放到系统要求的行为上。

若要遵循测试驱动开发的指导原则,需按照如下步骤进行编码:

  1. 假设目标对象以及要求的行为已经存在,编写测试。
  2. 编译解决方案,此时会看到编译失败。
  3. 编写足够的代码使程序通过编译。
  4. 运行单元测试,测试失败。
  5. 编写足够的代码使得单元测试通过。
  6. 若有必要,进行重构

测试驱动开发虽然没有什么变化,但在日常的开发活动中对它的应用却还在发展之中。例如,测试驱动开发最近的发展方向是行为驱动开发;该方法试图弥补“代码编写的技术语言与商务运用中的领域语言之间”的鸿沟。换句话说,行为驱动开发将TDD与后面介绍的领域驱动设计(或者是与你喜欢的其它方法)结合了起来。

领域驱动设计

从近日的发展趋势来看,领域驱动设计(DDD)可 谓风光无限。该方法将软件开发的注意力放在了领域与领域逻辑上,而不是技术以及支持技术方案的关系数据库模型。和行为驱动设计一样,DDD提出了大量的技 术与模式,以改善客户与开发团队的协作关系,在描述语言上达成一致。理想情况下,客户应该能够理解DDD应用程序的领域层,而编码逻辑也能够完整地反映客 户的业务逻辑。

我试验了各种方法,得出的结论是:领域驱动设计是早期编程方法的一种自然进化,例如针对数据库数据进行模型驱动开发,其对应的模型可以视为应用程序的核心,要做的工作就是操作数据。(Castle ActiveRecordADO.NET实体框架都是非常稳定和优秀的模型驱动设计工具。)相反,在DDD中,数据库则被视为一种必要的基础设施细节,能够支持领域及相关逻辑。事实上,正所谓“万法皆空”,在DDD中本来就没有数据库。虽然,领域驱动设计需要数据库,但关键在于领域对于持久化机制(实现数据存储与获取的机制)应该是一无所知的。

而且,领域驱动设计不仅仅是将数据持久化从领域中分离出来。它的主要目的是让领域对象自身拥有领域行为。例如,我们不能分离出 CustomerAccountLogic类,并由它来决定CustomerAccount是否取决于付款日期,而是要求CustomerAccount 自身维护这一信息。采用这种方式,领域的行为是与模型自身是合二为一的。

以上介绍仅仅是运用DDD进行软件开发技术的冰山一角。若要了解领域驱动设计的更多信息,可以阅读文档领域驱动设计快速入门,它是Eric Evans经典著作《领域驱动设计》的简明摘要。

在S#arp架构中融入这些思想

每个项目都其独特的需求,也没有哪个框架能够尽善尽美,对于开发Web应用程序而言,这是机会与挑战共存的局面。但是在面临众多选择时,开发人员很 难做出判断,哪些工具和技术适合给定的项目,并让开发中通常遇到的挑战能够迎刃而解。例如,如果你正在寻觅一个.NET依赖注入工具——或者说控制反转容器——可以选择Spring.NET(它远远不只是提供IoC功能)、UnityCastle WindsorNinjectStructureMap,以及更多。这还只是对于IoC的选择!让事情变得更糟糕的是,我们需要明智地规划,恰如其分地在架构中权衡各种工具与技术,这是一项极具挑战的工作。

在.NET Web开发中,至少在当前还缺乏一个通用的架构与基础,可以在程序开发中对各种技术与技巧进行最优组合,根据已经被证实的实践来选择最近的技术,以及由开源社区开发的优质工具。S#arp架构正是基于这样的前提应运而生。开源的S#arp架构试图利用本文介绍的业已证明行之有效的实践,谨慎地选择工具以提高开发效率,保证系统高质高量,以及良好的可维护性。

S#arp架构使用的工具与技术如下所示:

选择的这些技术与工具表明,虽然软件开发没有奇妙的银弹,但选择一个稳定的开发实践,并结合适当的工具,就能带来巨大的价值。

领域驱动架构将它们串联在一起

我认为,被封装到S#arp架构中的关键思想,就是颠倒领域与数据访问层之间关系的一种技术。在通常的应用架构中,尤其是与微软推荐的架构紧密相关的,其依赖关系都是从表示层开始自上而下,依赖于业务层,最后依赖于数据层。虽然这是对实现细节的过度简化,但它通常会建议将数据层作为最底层,被其他层依赖;这正是根据设计进行模型驱动。

虽然模型驱动方式自有其优势,也适用于众多解决方案,但却不符合本文提出的领域驱动的目标。让领域直接依赖于数据层,会造成另一种弊端,就是引入领 域对象与数据访问代码之间的双向依赖。我的前任教授Lang博士说道,只有不断犯错误,最后才能成为这个领域的专家。我正在努力成为一名专家,我已经认识 到领域对象与数据访问代码之间存在的双向依赖,它们正是麻烦的渊薮。(这个深刻教训使我在成为专家的道路上能够登堂入室。)

那么,我们该如何冲破这重重迷雾,让设计清晰地呈现这些思想呢?解决之道就是明确地分离各种应用关注点,颠倒领域与数据访问层之间的关系,也就是在 领域层定义分离的接口,让数据访问层进行实现。采用这种方式,则应用程序的所有层都只依赖于数据访问(或其它外部服务)层的接口,从而保证对实现细节的一 无所知。这会让设计变得松散耦合,更易于进行单元测试,而在项目开发的维护阶段,系统会变得更加地稳定。

下图演示了S#arp架构所主张的体系架构,使用接口隔离模式反转了传统的领域与数据访问层之间的依赖关系。每个box表示一个单独的物理程序集,箭头则表示了依赖关系及其依赖方向。

ddd

注意,图中的数据层定义了仓储的具体实现细节,它依赖于核心的领域层。在核心层中,除了定义领域模型和逻辑,还定义了仓储接口,该接口可以被其他各 层所调用,例如应用服务层会与仓储通信,这就保证了它独立于其实现细节。某些建议认为表示层(在图上方的YourProject.Web)不应直接依赖于 领域层。作为替代,可以引入数据传输对象(DTO),从而在数据传递给视图展现时,更好地将领域层从表示层中分离。

怎么做?

我们工作的底线是,软件交付专家们必须及时交付解决方案,且要符合客户的需求,并具有高质量和可维护性。很多实践都经过了千锤百炼,所谓“前人栽 树,后人乘凉”,在个人的应用程序中,我们不必重新创造,只需求助于经过验证而又真实的设计模式,以及基本工具的实现,就能够解决开发中的常见问题,例如 数据持久化和单元测试。真正的挑战在于要合理地制定计划,事先做出权衡,既要提高开发效率,又不能扼杀我们的能力,企图以创新的方式去迎合不切实际的需求 是不可取的。我希望本文所描述的技术与工具,以及S#arp架构对它们的组合,足以说明,虽然条条道路都能够通罗马,然而前车之鉴,若要避免重蹈覆辙,就必须从一开始就要汲取经验教训,并要有足够的智慧来赢得一个稳定的开端。

了解更多信息

S#arp架构项目已经开发了接近一年的时间,它为快速开发稳定的领域驱动程序提供了简单但又强大的架构基础。你可以从http://code.google.com/p/sharp-architecture下载S#arp架构的RC版本。1.0的GA版则与ASP.NET MVC 1.0密切相关。S#arp架构的论坛欢迎大家踊跃发言,畅谈自己的体验。

关于作者

http://devlicio.us

若要提到编写优美的软件,那么Billy McCafferty可以说是经验老到,久经沙场,偏生他又一味追求编程的罗曼蒂克,以至于无可救药。Billy目前身兼两职,一方面他负责管理一家小规模的培训与咨询公司Codai(很快会推出新的网站),同时又在Parsons Brinckerhoff带领开发人员与架构师团队。在发布S#arp架构1.0版本之后,Billy的生活又将重回正规,在不久的ALT.NET以及其他开发大会上,你能够见到他的身影。

[Tools]MyEclipse 7.0 破解版下载

mikel阅读(586)

MyEclipse7.0 下载地址:downloads.myeclipseide.com/downloads/products/eworkbench/7.0M1/MyEclipse_7.0M1_E3.4.0_Installer.exe
http://downloads.myeclipseide.com/downloads/products/eworkbench/7.0M1/MyEclipse_7.0M1_E3.4.0_Installer.exe
1、建立java Project,任意取个名字就行。
2、建立一个名字为MyEclipseGen的类文件,内容见附件。
3、运行该代码,在控制台中会出现:
please input register name:
×××××(你的name)
即生成序列号:
4、进入myeclipse,输入Subscriber和Subscription code即可。
附件:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class MyEclipseGen {
private static final String LL = "Decompiling this copyrighted software is a violation of both your license agreement and the Digital Millenium Copyright Act of 1998 (http://www.loc.gov/copyright/legislation/dmca.pdf). Under section 1204 of the DMCA, penalties range up to a $500,000 fine or up to five years imprisonment for a first offense. Think about it; pay for a license, avoid prosecution, and feel better about yourself.";
public String getSerial(String userId, String licenseNum) {
java.util.Calendar cal = java.util.Calendar.getInstance();
cal.add(1, 3);
cal.add(6, -1);
java.text.NumberFormat nf = new java.text.DecimalFormat("000");
licenseNum = nf.format(Integer.valueOf(licenseNum));
String verTime = new StringBuilder("-").append(
new java.text.SimpleDateFormat("yyMMdd").format(cal.getTime()))
.append("0").toString();
String type = "YE3MP-";
String need = new StringBuilder(userId.substring(0, 1)).append(type)
.append("300").append(licenseNum).append(verTime).toString();
String dx = new StringBuilder(need).append(LL).append(userId)
.toString();
int suf = this.decode(dx);
String code = new StringBuilder(need).append(String.valueOf(suf))
.toString();
return this.change(code);
}
private int decode(String s) {
int i;
char[] ac;
int j;
int k;
i = 0;
ac = s.toCharArray();
j = 0;
k = ac.length;
while (j < k) {
i = (31 * i) + ac[j];
j++;
}
return Math.abs(i);
}
private String change(String s) {
byte[] abyte0;
char[] ac;
int i;
int k;
int j;
abyte0 = s.getBytes();
ac = new char[s.length()];
i = 0;
k = abyte0.length;
while (i < k) {
j = abyte0[i];
if ((j >= 48) && (j <= 57)) {
j = (((j – 48) + 5) % 10) + 48;
} else if ((j >= 65) && (j <= 90)) {
j = (((j – 65) + 13) % 26) + 65;
} else if ((j >= 97) && (j <= 122)) {
j = (((j – 97) + 13) % 26) + 97;
}
ac[i] = (char) j;
i++;
}
return String.valueOf(ac);
}
public MyEclipseGen() {
super();
}
public static void main(String[] args) {
try {
System.out.println("please input register name:");
BufferedReader reader = new BufferedReader(new InputStreamReader(
System.in));
String userId = null;
userId = reader.readLine();
MyEclipseGen myeclipsegen = new MyEclipseGen();
String res = myeclipsegen.getSerial(userId, "5");
System.out.println("Serial:" + res);
reader.readLine();
} catch (IOException ex) {
}
}
}
我运行后的结果:
please input register name:
sorc
Serial:fLR8ZC-855550-6654585354305964
注:private static final String LL = "Decompiling this copyrighted software is a violation of both your license agreement and the Digital Millenium Copyright Act of 1998 (http://www.loc.gov/copyright/legislation/dmca.pdf). Under section 1204 of the DMCA, penalties range up to a $500,000 fine or up to five years imprisonment for a first offense. Think about it; pay for a license, avoid prosecution, and feel better about yourself.";
此段代码常量不可变更 否则注册码无效

[MVC]在MVC下用XML实现breadcrumbs导航栏

mikel阅读(930)

转载:http://www.cnblogs.com/conan77/archive/2009/06/24/1509927.html

先看下样子image

像这种导航栏(breadcrumbs)在mvc下我们来实现他。我们采用XML来实现这个功能。

1.首先做个准备,我们编写rounting规则(顺便提一句,我们要用到rounting功能,所以规则必须写正确,不然出不来喔)

代码如下

 public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"inner",                                              // Route name
"resume/test/inner/{action}/{id}",                           // URL with parameters
new { controller = "inner", action = "Index", id = "" }  // Parameter defaults
);
routes.MapRoute(
"test",                                              // Route name
"resume/test/{action}/{id}",                           // URL with parameters
new { controller = "test", action = "Index", id = "" }  // Parameter defaults
);
routes.MapRoute(
"Default",                                              // Route name
"{controller}/{action}/{id}",                           // URL with parameters
new { controller = "Home", action = "Index", id = "" },
new { controller = "^(?!(test|inner)).*$", action = "^(?!test).*$" }
);
}

我们加了两个规则

/resume/test

和/resume/test/inner

2.编写用到的XML文件,注意是树形结构的

在models写个Navigator.xml

<?xml version="1.0" encoding="utf-8" ?>
<node Title="首页"  Description="潘峰的网站" Action="Index" Controller="Home">
<node Title="简历" Description="在线简历" Action="Index" Controller="Resume">
<node Title="Test" Description="Test" Action="Index" Controller="test">
<node Title="inner" Description="inner" Action="Index" Controller="inner">
</node>
</node>
</node>
</node> 

3.编写我们的类文件来实现Navigator

在models写个navigatorHelper.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Xml;
using System.Xml.Linq;
using System.Web.Routing;
using System.Web.Mvc;
using System.IO;
using System.Text;
namespace conansoft.Helpers
{
public static class MenuHelper
{
private static HttpServerUtilityBase Server = null;
private static HttpRequestBase Request = null;
private static UrlHelper Url = null;
private static RouteValueDictionary RouteDictionary = null;
public static string Navigator(this HtmlHelper helper)
{
Server = helper.ViewContext.RequestContext.HttpContext.Server;
Request = helper.ViewContext.RequestContext.HttpContext.Request;
Url = new UrlHelper(helper.ViewContext.RequestContext);
RouteDictionary = helper.ViewContext.RequestContext.RouteData.Values;
string xmlPath = Server.MapPath(Url.Content("~/Models/Navigator.xml"));
XDocument doc = XDocument.Load(xmlPath);
XElement node = FindNode(doc.Root);
StringBuilder sb = new StringBuilder();
Stack<XElement> s = new Stack<XElement>();
while (node != null)
{
s.Push(node);
node = node.Parent;
}
//输出breadcrumbs.可以自行修改使之符合你的要求
while (s.Count() != 0)
{
node = s.Pop();
if (UrlEqual(node))
{
sb.AppendLine(string.Format("<li title='{1}'>{0}</li>", node.Attribute("Title").Value, node.Attribute("Description").Value));
}
else
{
sb.AppendLine(string.Format("<li><a href='{1}' title='{2}'>{0}</a></li>", node.Attribute("Title").Value,
Url.Action(node.Attribute("Action").Value, node.Attribute("Controller").Value),
node.Attribute("Description").Value));
sb.AppendLine("<span style='color:Black;font-weight:bold;'> > </span>");
}
}
return sb.ToString();
}
/// <summary>
/// 查找当前节点
/// </summary>
/// <param name="e">当前节点</param>
/// <returns>找到返回,找不到为空</returns>
private static XElement FindNode(XElement e)
{
XElement result = e;
if (UrlEqual(e))
{
return e;
}
else
{
if (e.HasElements)
{
foreach (XElement ee in e.Elements())
{
result = FindNode(ee);
}
}
else
{
return null;
}
return result;
}
}
/// <summary>
/// Url是否相等
/// </summary>
/// <param name="e">节点</param>
private static bool UrlEqual(XElement e)
{
string url1 = Url.Action(e.Attribute("Action").Value, e.Attribute("Controller").Value).ToLower();
string url2 = Url.RouteUrl(RouteDictionary).ToLower();
return url1 == url2;
}
}
}

解释一下我们利用xml文件来实现breadcrumbs,并且我们用action和controller来判断是否为当前路径[UrlEqual]

在网页中加入

  <%=Html.Navigator() %>

好了效果如下image

我的网站

image

image

[API]淘宝开放平台重装上阵

mikel阅读(1018)

淘宝开放平台( Taobao Open Platform, TOP ) ,面向第三方的开放式电子商务服务基础服务框架,重装上阵。前一段时间提前接触了一点这个项目,真是个非常有想象空间的事情。

可以肯定的是,这是"大淘宝"战略的一个重要环节。从最初的 Taobao.com 一个站点,现在是一个平台,将来再到一个更大的商业生态系统

Taobao TOP 蓝图
(上图出处)

去年下半年淘宝有过一次尝试("淘园"项目),与上次的初步尝试截然不同的是,这次已经不再通过阿里软件这一层进行接入,从开发者使用角度上看,减 少了交互环节,更加直接方便。此外,可供使用的应用程序接口愈加丰富,更贴近用户使用习惯。随着开发者社区的成熟和开发者规模的扩大,淘宝提供平台化的支 持也是可以想见的事情。

对于所有的开放平台开发者来说,最关心的问题莫过于盈利模式。现在 TOP 关于盈利模式主要有两种形式:一是淘宝客佣金模式,再一个是淘宝插件分成模式。还是比较清晰的。就我个人而言,更倾向于前者的模式。也期待淘宝运营人员能够根据实际情况制定更加有利于开发者的策略,积极促进与开发者之间的互动。胜,在于人。

与其在一些 SNS 网站捣鼓那些游戏插件,还不如来开发电子商务第三方应用呢。你说呢?

[IIS]详解DNS安装及配置多个二级域名的二种方法(图文教程)

mikel阅读(1288)

最近为了调试程序,配置了本地的DNS服务器,我们要模拟网上的域名环境,发现还有很多的朋友不太了解,所以发一个图文教程简单的介绍下DNS正向区域设置的二级域名的两种方法,如果有什么不对的地方还望网友们指正批评!谢谢!不废话了,贴图先!

第一步:windows 2003中DNS的安装:

      

1.1这个步骤要花上一点点时间,大家耐心等下,不同的机器,速度可能不太一样!

1.2按图中操作后选反DNS服务器点击下一步

1.4到这里DNS服务器就安装好了,接下来的是一个向导添加一个"正向查找区域"了

1.5这张图主要说明一下是,区域名称就是我们要设置的域名!也就是不带主机头的后面那部分了!

1.6DNS安装的机器需要有一个固定的ip地址,不能自动获取,所以我们设置一下“网络连接”将我们的ip 地址输入进去,DNS服务器首选改成本机,这个地方会被强制使用本机的DNS服务器的,因为服务就装在这台机器上!

第二步:“DNS服务器”的配置

2.1下面配置我们刚才添加的"kfoo.com"这个域的“正向查找区域”,这一步,我们为这个域添加几个主机头用来测试!

下面是创建出来的主机头

2.3创建好了主机头,我们去cmd里面去确认下,会发现,指向已经成功!

第三步:IIS站点的配置

上面我们把“默认站点”端口号“80”的站点设置到了http://www.kfoo.com/

——————————————————————————————————————

上面这个要注意,站点的端口是“81”

——————————————————————————————————————

我们发现,这个地方这样设置了,但是只有http://www.kfoo.com/这个可以访问,而product.kfoo.com这个域名是没法访问的!为什么呢!?原来要加上product这个站点的“81”端口号!这样和我们预想的肯定不是一回事,那怎么办呢!?解决方法有三种!!(这里谢谢“木鱼”的指证,我之前把事情弄复杂了,这个地方那么就有三种方法)按木鱼说的最简单的方法就是,把product这个站点的端口修改下,也修改成"80"端口即可!

其它的两种方法还是放在这吧,下面选讲麻烦的,再讲简单的:

第一种方法:为网卡设置多ip绑定来解决上面的问题,操作如下图!(注:这里用多ip应该还可以解决多域名泛解析的情况!)

——————————————————————————————————————

——————————————————————————————————————

设置好了,但是我们的DNS也要相应的更改一下了!就是把之前的指向换成我们新加入的ip地址"192.168.0.253"

 

——————————————————————————————————————

IIS中"product"站点,我们也要作相应的更改,如图,我们要将站点的"81"端口设置到"80",ip分配到我们刚增加的"192.168.0.253",好了,OK!

第一种方法就是这样的,试下,是不是都可以访问了!接下来就看简单好用的第二种方法了!一张图说明问题!

第二种方法:看下图:

——————————————————————————————————————

——————————————————————————————————————

按第二种方法设置的话,设置好hosts文件之后,我们的DNS中设置的主机头都指向到一个ip就可以了!IIS中的设置也比较简单,所有的站点都用80的端口,主机头就按照hosts文件和DNS中设置的配置就可以了,

[Web]Web OS雏形—Opera Unite初体验 从 博客园新闻频道

mikel阅读(862)

     最近,Opera公司发布了自己的新产品Opera 10,它最大的特色是引入了一个新的技术Opera Unite。

      Opera Unite 一种在线协作技术,允许您使用电脑直接与他人共享您的数据 – 如文件、照片或者音乐。Opera Unite 技术基于 Opera 浏览器中简单而精密的服务器程序实现共享数据和服务等功能。

     使用 Opera Unite,您不再需要花费时间上传您需要共享的内容。直接共享自己硬盘上的内容,而不需要第三方共享服务器。

  Opera Unite 彻底的扩大了您的线上活动范围。Opera Unite 借力于当今先进的硬件和流畅的网络连接,赋予所有人定义网络未来面貌的能力。

  Opera Unite 服务

  Opera Unite 服务使您可以扩展您浏览器的功能以共享文件。这些服务范围广泛,从发布信息/聊天的社会性网络工具到文件、照片和音乐共享。用它可以快捷的与朋友共享内容 – 无需上传 – 而别人可以直接访问您的开放的那部分硬盘。
一、下载Opera Unite

下载地址:http://unite.opera.com/

      

二、配置个性化主页

进入浏览器,首先要注册自己的Opera账号

注册后,它就会让你输入个性化计算机名,也就相当于Opera中的个人主页

以我自己的为例:

登陆后,会出现一个侧边栏:

你可以看到Opera Unite目前已经提供了File Sharing(文件共享)、Fridge(留言板)、Media Player(媒体播放器)、Photo Sharing(照片共享)、The Lounge(聊天室)和Web Server(网络服务器)等六项功能。

以Fridge(留言板)为例,我们启动留言板服务,然后ADD Note:

马上就去考六级了,今天就先介绍到这,大家发现什么新的东西要来分享哦!

[MVC]一个基于Asp.Net MVC的权限方案

mikel阅读(885)

    最近这段时间博客园有几位同学在探讨通用的权限方案,偶闲来无事,也来凑凑热闹,下面简单说一下我的简单解决方案,基于AOP的。由于使用了ASP.NET MVC 开发,可能需要先对MVC有些了解,思路都是差不多的。

1.数据结构

Mad_Popedom为权限表,Control记录控制器名,Action记录动作名。
Mad_Role为角色表。

2.权限控制的实现
此处使用比较简单AOP方式,用MVC的Filter实现,代码如下

Code

解释一下,上面的代码就是在执行前,先获取登录用户可以运行的Controller-Action,然后和当前需要执行的Controller-Action比较,如存在,即通过,否则为没有权限执行。

3.为动作添加权限
为简单起见,对于Controller层我是独立出来一个类库的,好处是等会为角色添加权限的时候我们不需要手动输入,只要反射dll就可以了。

如图所示,凡需要权限控制的函数,只需要添加[SupportFilter]特性就可以了,当然这种方式只能控制到Action级。

4.为角色额添加权限
这个比较简单,只需要把角色和权限关联起来就可以了,这里我是用反射Controller层dll实现。
Web.config

Global.asax.cs

Madnet.Controllers.Test即为Controller层的dll

5.结束
上面4步即已完成基本的权限控制。可以在此基础上加上用户组,用户,菜单等管理,可实现”用户-角色-权限”的自由组合,一个简单的通用后台大概就是这样了。


—————————————————————————————-
文章发表于:http://www.madcn.net/?p=454