[转载]基于MVC框架+IOC+Rhino Mocks的一个简单项目介绍

mikel阅读(1218)

[转载]基于MVC框架+IOC+Rhino Mocks的一个简单项目介绍 – 漫步云端 – 博客园.

现在不管是企业还是科研机构,几乎所有的项目开发都是遵循一定的框架,将经过实践证明过的开发框架和开发模式借鉴使用无可厚非,但难免会遇到某些功能实现 或者基于某种考虑当前的开发框架无法达到这样的目的。这时我们就会考虑不同技术的融合。

我们现在正在开发的平台项目正是借鉴了这样的思 想,我们的平台项目首先整体的开发框架使用了AspNet MVC框架;其次数据访问层套用了CommunityServer的开发框架,其中融合了Provider模式和传统的三层架构;而在业务逻辑层处理中, 为了保证代码的可重用性以及可扩展性,我们引入了依赖注入(DI);最后,在单元测试模块中我们使用了Rhino Mocks作为我们的测试框架。

(一)AspNet MVC框架,所谓MVC其实就是分别代表三个单词Model、View和Controller。了解他们分别的含义,我们就从ASP.NET页面的处理机 制谈起。

一般来说,一个ASP.NET页面通常要处理一下事情:1. 因为最后展示的都是页面,所以我们要得到在页面上展示需要的数据,也就是Model。2. 在页面的Page_Load(页面加载)方法中为我们的页面控件绑定数据,涉及到这些业务逻辑的工作(即获取数据和绑定数据的工作)都是在 Conotroller中完成的。3. 也就是我们看到的.aspx页面,不同的是这些页面都是没有后台.cs代码类的。

接下来我们需 要明白在MVC中Web请求的处理流程,用户通过Web浏览器向服务器发送一条url请求,这里请求的url不再是xxx.aspx格式,而是http://HostName/ControllerName/ActionName/Parameters的 格式。这个请求被ASP.NET MVC的路由映射系统截获(路由映射可以在Global.asax中配置)。路由映射系统按照映射规则,解析出控制器名 ControllerName,Action名ActionName和各个参数Parameters,然后,寻找Controllers目录下的 ControllerNameController.cs这个控制器类,默认情况下,系统总是寻找Controllers目录下的“控制器 名+Controller”这么一个类,然后,找寻这个类下与ActionName同名的方法,找到后,将Parameters作为参数传给这个方法,而 后Action方法开始执行,完成后返回相应视图,默认情况下,会返回Views目录下与ControllerName同名的目录下的与 ActionName同名的aspx文件,并且将ViewData传递到视图。ViewData中一般包含了控制视图显示的控制量以及视图显示需要的数 据,如图1所示。

MVC0

(二)CommunityServer的开发框 架

1. 三层架构,即数据访问层、业务逻辑层和表现层。这样开发的系统简洁并且易于理解。

2. Provider模式,关于Provider模式我们已经非常熟悉,这是我们处理数据访问层逻辑和业务逻辑层逻辑经常会采用的方式,实现Provider 模式需要4各类:基础类(即对应数据库中相应表的类)、工具类(即在基础类基础上写的函数类)、接口类(即工具类中会调用接口类中的方法)、实现类(即实 现接口类中的方法)。例如下面的例子:User(基础类,对应数据库中的User表)–>Users(工具类,包含User的方法,比如 GetUser方法)–>UserDataProvider(接口类,该类中声明了GetUser方 法)–>SQLUserDataProvider(实现类,该类实现了UserDataProvider类中声明的GetUser方法)。

3. 需要注意的是构建三层架构使用Provider模式时一定要谨慎,鼓励大家自己尝试一下,会发现张口容易动手难。

(三)依赖注入。关 于依赖注入,大家可以看我的前一篇博客–也来谈谈依赖注入模式。 希望会对大家有帮助。本着测试先行的原则,引入依赖注入不仅使代码复用性加强,而且便于测试。在我们平台项目中,我们使用了Castle IoC。

(四)Rhino Mocks单元测试框架。在平台项目的测试中,由于开发框架的特殊性以及其中的三层架构用到了几种技术,所以为了测试全面,我们需要将测试分为几层来测 试。总体来看我们这个项目的大体构造流程如下所列:基础类–>工具类–>接口类–>实现类 –>controller–>View。因而我们在测试时将其分成工具类的测试(即函数的测试)以及Controller层业务逻辑的测 试两部分。需要注意的是这两部分的关注点是不一样的,在工具类的测试部分,我们关注的是函数实现业务逻辑的正确性,因此我们会忽略数据访问层而专注于测试 函数功能是否能够实现;在Controller层业务逻辑测试部分,我们关注的是在controller层处理的业务逻辑能否在view层返回给我们需要 的数据,因此我们关注的是controller类里面的业务逻辑实现,而我们会刻意忽略其中调用的函数的实现环节。

(五)最后附上一个 简单的实例说明:这个实例MySingleCase就是一个最简单的MVC框架+CommunityServer开发框架+Provider模 式+IOC+Rhino Mocks测试框架的应用,其模式和我们现在正在开发的平台项目一致。

其中 MysingleCase.Components中放置的是全局类以及公共类,包括CSCache.cs缓存类、CSConfiguration全局配置 类、DataProvider(Provider基类)、IoC(依赖翻转的公共操作类)、WindsorServiceLocator.cs(服务定位 器处理类)等。

MysingleCase.Models中放置的是项目中用到的用户自定义的基础类,包括基础类、工具类、接口类。其中 Service中放置的就是使用依赖注入时需要的接口类。

MysingleCase.Web项目中就是具体的MVC框架的使用。 Controllers文件夹下面是所有的Controller,对应于每一个Controller中的ActionResult的表示层都放在 Views文件夹下的相应路径下。而其中的IoC文件中的ContainerBuilder.cs类负责Controller以及需要使用依赖翻转的所有 类的注册。别忘记我们使用MVC的必要条件也就是Global.asax中的路由规则是必不可少的。

附:源代码

[转载]策略模式-4

mikel阅读(996)

[转载]策略模式-4 – 云飞龙行 – 博客园.

3.3  Context和Strategy的关系

在策略模式中,通常是上下文使用具体的策略实现对象,反过来,策略实现对象也可以从上下文获取所需要的数据,因此可以将上下文当参数传递给策略实现对象, 这种情况下上下文和策略实现对象是紧密耦合的。
在这种情况下,上下文封装着具体策略对象进行算法运算所需要的数据,具体策略对象通过回调上下文的方法来获取这些数据。
甚至在某些情况下,策略实现对象还可以回调上下文的方法来实现一定的功能,这种使用场景下,上下文变相充当了多个策略算法实现的公共接口,在上下文定义的 方法可以当做是所有或者是部分策略算法使用的公共功能。
但是请注意,由于所有的策略实现对象都实现同一个策略接口,传入同一个上下文,可能会造成传入的上下文数据的浪费,因为有的算法会使用这些数据,而有的算 法不会使用,但是上下文和策略对象之间交互的开销是存在的了。
还是通过例子来说明。
1:工资支付的实现 思路
考虑这样一个功能:工资支付方式的问题,很多企业的工资支付方式是很灵活的,可支付方式是比较多的,比如:人民币现金支付、美元现金支付、银行转账到工资 帐户、银行转账到工资卡;一些创业型的企业为了留住骨干员工,还可能有:工资转股权等等方式。总之一句话,工资支付方式很多。
随着公司的发展,会不断有新的工资支付方式出现,这就要求能方便的扩展;另外工资支付方式不是固定的,是由公司和员工协商确定的,也就是说可能不同的员工 采用的是不同的支付方式,甚至同一个员工,不同时间采用的支付方式也可能会不同,这就要求能很方便的切换具体的支付方式。
要实现这样的功能,策略模式是一个很好的选择。在实现这个功能的时候,不同的策略算法需要的数据是不一样,比如:现金支付就不需要银行帐号,而银行转账就 需要帐号。这就导致在设计策略接口中的方法时,不太好确定参数的个数,而且,就算现在把所有的参数都列上了,今后扩展呢?难道再来修改策略接口吗?如果这 样做,那无异于一场灾难,加入一个新策略,就需要修改接口,然后修改所有已有的实现,不疯掉才怪!那么到底如何实现,在今后扩展的时候才最方便呢?
解决方案之一,就是把上下文当做参数传递给策略对象,这样一来,如果要扩展新的策略实现,只需要扩展上下文就可以了,已有的实现不需要做任何的修改。
这样是不是能很好的实现功能,并具有很好的扩展性呢?还是通过代码示例来具体的看。假设先实现人民币现金支付和美元现金支付这两种支付方式,然后就进行使 用测试,然后再来添加银行转账到工资卡的支付方式,看看是不是能很容易的与已有的实现结合上。

2:实现代码示例
(1)先定义工资支付的策略接口,就是定义一个支付工资的方法,示例代码如下:

/**

* 支付工资的策略的接口,公司有多种支付工资的算法

* 比如:现金、银行卡、现金加股票、现金加期权、美元支付等等

*/

public interface PaymentStrategy {

/**

* 公司给某人真正支付工资

* @param ctx 支付工资的上下文,里面包含算法需要的数据

*/

public void pay(PaymentContext ctx);

}

(2)定义好了工资支付的策略接口,该来考虑如何实现这多种支付策略了。
为了演示的简单,这里先简单实现人民币现金支付和美元现金支付方式,当然并不真的去实现跟银行的交互,只是示意一下。
人民币现金支付的策略实现,示例代码如下:

/**

* 人民币现金支付

*/

public class RMBCash implements PaymentStrategy{

public void pay(PaymentContext ctx) {

System.out.println(“现在 给”+ctx.getUserName()

+”人民币现金支付”+ctx.getMoney()+”元”);

}

}

同样的实现美元现金支付的策略,示例代码如下:

/**

* 美元现金支付

*/

public class DollarCash implements PaymentStrategy{

public void pay(PaymentContext ctx) {

System.out.println(“现在 给”+ctx.getUserName()

+”美元现金支付”+ctx.getMoney()+”元”);

}

}

(3)该来看支付上下文的实现了,当然这个使用支付策略的上下文,是需要知道具体使用哪一个支付策略的,一般由客户端来确定具体使用哪一个具体的 策略,然后上下文负责去真正执行。因此,这个上下文需要持有一个支付策略,而且是由客户端来配置它。示例代码如下:

/**

* 支付工资的上下文,每个人的工资不同,支付方式也不同

*/

public class PaymentContext {

/**

* 应被支付工资的人员,简单点,用姓名来代替

*/

private String userName = null;

/**

* 应被支付的工资的金额

*/

private double money = 0.0;

/**

* 支付工资的方式策略的接口

*/

private PaymentStrategy strategy = null;

/**

* 构造方法,传入被支付工资的人员,应支付的金额和具体的支付策略

* @param userName 被支付工资的人员

* @param money 应支付的金额

* @param strategy 具体的支付策略

*/

public PaymentContext(String userName,double money,

PaymentStrategy strategy){

this.userName = userName;

this.money = money;

this.strategy = strategy;

}

public String getUserName() {

return userName;

}

public double getMoney() {

return money;

}

/**

* 立即支付工资

*/

public void payNow(){

//使用客户希望的支付策略来支付工资

this.strategy.pay(this);

}

}

(4)准备好了支付工资的各种策略,下面看看如何使用这些策略来真正支付工资,很简单,客户端是使用上下文来使用具体的策略的,而且是客户端来确定 具体的策略,就是客户端创建哪个策略,最终就运行哪一个策略,各个策略之间是可以动态切换的,示例代码如下:

public class Client {

public static void main(String[] args) {

//创建相应的支付策略

PaymentStrategy strategyRMB = new RMBCash();

PaymentStrategy strategyDollar = new DollarCash();

//准备小李的支付工资上下文

PaymentContext ctx1 =

new PaymentContext(“小李”,5000,strategyRMB);

//向小李支付工资

ctx1.payNow();

//切换一个人,给petter支付工资

PaymentContext ctx2 =

new PaymentContext(“Petter”,8000,strategyDollar);

ctx2.payNow();

}

}

运行一下,看看效果,运行结果如下:

现在给小李人民币现金支付5000.0元

现在给Petter美元现金支付8000.0元

3:扩展示例,实现方式一
经过上面的测试可以看出,通过使用策略模式,已经实现好了两种支付方式了。如果现在要增加一种支付方式,要求能支付到银行卡,该怎么扩展最简单呢?
应该新增加一种支付到银行卡的策略实现,然后通过继承来扩展支付上下文,在里面添加新的支付方式需要的新的数据,比如银行卡账户,然后在客户端使用新的上 下文和新的策略实现就可以了,这样已有的实现都不需要改变,完全遵循开-闭原则。
先看看扩展的支付上下文对象的实现,示例代码如下:

/**

* 扩展的支付上下文对象

*/

public class PaymentContext2 extends PaymentContext {

/**

* 银行帐号

*/

private String account = null;

/**

* 构造方法,传入被支付工资的人员,应支付的金额和具体的支付策略

* @param userName 被支付工资的人员

* @param money 应支付的金额

* @param account 支付到的银行帐号

* @param strategy 具体的支付策略

*/

public PaymentContext2(String userName,double money,

String account,PaymentStrategy strategy){

super(userName,money,strategy);

this.account = account;

}

public String getAccount() {

return account;

}

}

然后看看新的策略算法的实现,示例代码如下:

/**

* 支付到银行卡

*/

public class Card implements PaymentStrategy{

public void pay(PaymentContext ctx) {

//这个新的算法自己知道要使用扩展的支付上下文, 所以强制造型一下

PaymentContext2 ctx2 = (PaymentContext2)ctx;

System.out.println(“现 在给“+ctx2.getUserName()+”

+ctx2.getAccount()+”帐号支付了“+ctx2.getMoney()+”“);

//连接银行,进行转帐,就不去管了

}

}

最后看看客户端怎么使用这个新的策略呢?原有的代码不变,直接添加新的测试就可以了,示例代码如下:

public class Client {

public static void main(String[] args) {

//创建相应的支付策略

PaymentStrategy strategyRMB = new RMBCash();

PaymentStrategy strategyDollar = new DollarCash();

//准备小李的支付工资上下文

PaymentContext ctx1 =

new PaymentContext(“小李”,5000,strategyRMB);

//向小李支付工资

ctx1.payNow();

//切换一个人,给petter支付工资

PaymentContext ctx2 =

new PaymentContext(“Petter”,8000,strategyDollar);

ctx2.payNow();

//测试新添加的支付方式

PaymentStrategy strategyCard = new Card();

PaymentContext ctx3 = new PaymentContext2(

“小王”,9000,”010998877656″,strategyCard);

ctx3.payNow();

}

}

再次测试,体会一下,运行结果如下:

现在给小李人民币现金支付5000.0元

现在给Petter美元现金支付8000.0元

现在给小王的010998877656帐号支付了9000.0元

4:扩展示例,实现方式二
同样还是实现上面这个功能:现在要增加一种支付方式,要求能支付到银行卡。
(1)上面这种实现方式,是通过扩展上下文对象来准备新的算法需要的数 据。还有另外一种方式,那就是通过策略的构造方法来传入新算法需要的数据。这样实现的话,就不需要扩展上下文了,直接添加新的策略算法实现就好了。示例代 码如下:

/**

* 支付到银行卡

*/

public class Card2 implements PaymentStrategy{

/**

* 帐号信息

*/

private String account = “”;

/**

* 构造方法,传入帐号信息

* @param account 帐号信息

*/

public Card2(String account){

this.account = account;

}

public void pay(PaymentContext ctx) {

System.out.println(“现 在给“+ctx.getUserName()+”

+this.account+”帐号支付了“+ctx.getMoney()+”“);

//连接银行,进行转帐,就不去管了

}

}

(2)直接在客户端测试就可以了,测试示例代码如下:

public class Client {

public static void main(String[] args) {

//测试新添加的支付方式

PaymentStrategy strategyCard2 = new Card2(“010998877656”);

PaymentContext ctx4 =

new PaymentContext(“小张”,9000,strategyCard2);

ctx4.payNow();

}

}

运行看看,好好体会一下。
(3)现在有这么两种扩展的实现方式,到底使用哪一种呢?或者是哪种实现更好呢?下面来比较一下:
对 于扩展上下文的方式:这样实现,所有策略的实现风格更统一,策略需要的数据都统一从上下文来获取,这样在使用方法上也很统一;另外,在 上下文中添加新的数据,别的相应算法也可以用得上,可以视为公共的数据。但缺点也很明显,如果这些数据只有一个特定的算法来使用,那么这些数据有些浪费; 另外每次添加新的算法都去扩展上下文,容易形成复杂的上下文对象层次,也未见得有必要。
对于在策略算法的实现上 添加自己需要的数据的方式:这样实现,比较好想,实现简单。但是缺点也很明显,跟其它策略实现的风格不一致,其它策略都是从上下文中来 获取数据,而这个策略的实现一部分数据来自上下文,一部分数据来自自己,有些不统一;另外,这样一来,外部使用这些策略算法的时候也不一样了,不太好以一 个统一的方式来动态切换策略算法。
两种实现各有优劣,至于如何选择,那就具体问题,具体的分析了。
5:另一种策略模式调用顺序示意图
策略模式调用还有一种情况,就是把Context当做参数来传递给Strategy,也就是本例示范的这种方式,这个时候策略模式的调用顺序如图4所示:

图4  策略模式调用顺序示意图二

未完 待续……

[转载]asp.net mvc 2 DisplayTemplates 的使用

mikel阅读(1129)

[转载]asp.net mvc 2 DisplayTemplates 的使用 – 飞创cms – 博客园.

ASP.NET mvc 2 官方给的例子有些简单,主要介绍了新的功能。下面主要介绍下DisplayTemplates 给我们带来的实用的功能,可以自定义字段显示的方式,按类型分:String、Boolean、Decimal。按显示的方 式:EmailAddress、Html、Url、HiddenInput。还可以自定义字段的显示 如:DropDownList。可以扩充类型的显示 如:DateTime,只要和字段的类型相同都可以直接使用,而不用绑定。下班一个简单的MetaData的例子。它可以扩充数据模型,定义一些自定义的 内容。

1 [MetadataType(typeof(Article_MetaData))]
2 partial class Article
3 {
4
5 }
6 public class Article_MetaData
7 {
8
9 [ScaffoldColumn(false)]
10 public int Id { get; set;}
11 [DisplayName(标题)]
12 [Required]
13 [SearchFilter]
14 public string title { get; set; }
15
16 [Display( Name=“”,Order=12)]
17 [Required]
18 [SearchFilter]
19 [DisplayName(栏目)]
20 [DropDownList(Category, Id, Name)]
21 public int Cid { get; set; }
22 [DisplayName(模型)]
23 [ScaffoldColumn(false)]
24 public int ModeId { get; set; }
25 [DisplayName(排序)]
26 [Required]
27 public int OrderID { get; set; }
28 [DisplayName(时间)]
29 [Required]
30 public DateTime CreateTime { get; set; }
31
32 [DisplayName(内容)]
33 [DataType(DataType.Html)]
34 public string Cont { get; set; }
35 }

关于MetaData的详细内容可以参考msdn上的介绍。mvc 对MetaData内的部分内容支持不是太完善,有些内容还需要自己来扩展。如[Display()]就不能使用,如果使用的话,你必须自定义 ModelMetadataProviders。通过它,你可以实现很多功能。

DisplayTemplates 文件夹内的自定义控件只针对html.display() 使用。下边说下,我使用的自定义表格,先将表格用分头部,和主体内容两部分,分别是 header、Rows。

header.ascx代码

1 <%@ Control Language=C# Inherits=System.Web.Mvc.ViewUserControl %>
2 <script runat=server>
3 bool ShouldShow(ModelMetadata metadata) {
4 return metadata.ShowForDisplay
5 && metadata.ModelType != typeof(System.Data.EntityState)
6 && !metadata.IsComplexType
7 && !ViewData.TemplateInfo.Visited(metadata);
8 }
9 </script>
10 <% if (Model == null) { %>
11 <%= ViewData.ModelMetadata.NullDisplayText %>
12 <% } else if (ViewData.TemplateInfo.TemplateDepth > 1) { %>
13 <%= ViewData.ModelMetadata.SimpleDisplayText %>
14 <% } else { %>
15
16 <% foreach (var prop in ViewData.ModelMetadata.Properties.Where(pm => ShouldShow(pm))) { %>
17 <% if (prop.HideSurroundingHtml) { %>
18 <%= Html.Display(prop.PropertyName) %>
19 <% }
20 else if (prop.DataTypeName != Html && prop.DataTypeName != MultilineText && prop.DataTypeName != Text)
21 { %>
22 <% if (!String.IsNullOrEmpty(prop.GetDisplayName())) { %>
23 <th><%= prop.GetDisplayName() %></th>
24 <% } %>
25
26 <% } %>
27 <% } %>
28
29
30 <% } %>

rows.ascx 代码

代码

<%@ Control Language=C# Inherits=System.Web.Mvc.ViewUserControl %>
<script runat=server>
bool ShouldShow(ModelMetadata metadata)
{
return metadata.ShowForDisplay
&& metadata.ModelType != typeof(System.Data.EntityState)
&& !metadata.IsComplexType
&& !ViewData.TemplateInfo.Visited(metadata);
}
</script>
<% if (Model == null) { %>
<%= ViewData.ModelMetadata.NullDisplayText %>
<% } else if (ViewData.TemplateInfo.TemplateDepth > 1) { %>
<%= ViewData.ModelMetadata.SimpleDisplayText %>
<% } else { %>

<% foreach (var prop in ViewData.ModelMetadata.Properties.Where(pm => ShouldShow(pm))) {
%>
<% if (prop.HideSurroundingHtml) { %>
<%= Html.Display(prop.PropertyName) %>
<% } else if(prop.DataTypeName!=Html&&prop.DataTypeName!=MultilineText&&prop.DataTypeName!=Text) { %>

<td><%= Html.Display(prop.PropertyName) %></td>
<% } %>
<% } %>

<% } %>

调用表格使用的代码

代码

<%@ Page Title=“” Language=C# MasterPageFile=~/Views/Shared/Admin.Master Inherits=System.Web.Mvc.ViewPage<dynamic> %>
<%@ Import Namespace=mvc.Models %>
<asp:Content ID=Content1 ContentPlaceHolderID=TitleContent runat=server>
Index
</asp:Content>

<asp:Content ID=Content2 ContentPlaceHolderID=MainContent runat=server>
<div id=headImg>
<img src=/content/adminimages/01.gif alt=“” /><%:ViewData[NavTitle]%>
</div>
<div id=cont2>
<div class=form clearfix>

<form id=form1 method=get>
<div id=Search class=clearfix>
<%=Html.DisplayFor(m => ViewData[searchModel], tool,“”)%>

<li class=add>
<%: Html.ActionLink(添加, Add) %>
</li>
</div>
</form>
<table class=tb>
<% int i = 0; foreach (var art in Model)
{
i
++;
if (i == 1)
{
%>
<tr class=tbhead>
<%=Html.DisplayFor(m => art, header, di)%>

<th>操作</th>
</tr>
<%} %>
<tr>
<%=Html.DisplayFor(m => art, rows, di)%>
<td style=width:60px>
<%: Html.ActionLink(编辑, Edit, new { id = art.Id })%> <%: Html.ActionLink(删除, Delete, new { id = art.Id }, new { onclick = return confirm(‘你确定要删除 吗?’) })%>
</td>
</tr>
<%} %>
</table>

<div class=page>
<%=ViewData[page] %>
</div>
</div>
</div>
</asp:Content>

————————————————————————————
作者:王继坤
出处:http://www.wjk3.cn/
本文版权归作者和博客园共有,欢迎转 载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

[转载]AgileEAS.NET之敏捷并行开发方法

mikel阅读(1022)

[转载]AgileEAS.NET之敏捷并行开发方法 – 魏琼东 – 博客园.

首先声明,此并行开发非指并行计算,而是指实践软件工程所总结的一些心得和做法。

敏捷并行开发方法,是利用软件构件技术等技术,以及平台+插件化开发技术整合而出的一种快速开发模式;并行是指产品在生命周期内,项目管理过程、项目研发 过程和机构支撑过程“并行”开展,项目研发过程中各个阶段有限度“并行”开展。

并行开发

在传统的瀑布开发模式下,同一软件的多个阶段是顺序地被开发出来的,也就是说第一阶段全部开发结束后才开始第二阶段的开发;同样,等第二阶段全部开发结束 后第三阶段的开发工作才会启动。但这种串行开发模式在当今的市场环境下越来越行不通,因为所有的软件产品都面临越来越大的迅速上市的压力,唯一有效的解决 方案就是引入并行开发机制。在并行开发模式下,同一软件的多个开发阶段会同时进行开,如第一阶段的工作尚未完成,往往就会启动第二阶段的工作,甚至会同时 启动第三阶段的工作,从而有效缩短软件的上市周期。

相关技术

软件构件技术:所谓软件构件化,就是要让软件开发像机械制造工业一样,可以用各种标准和非标准的零件来进行组装。软件的构件化和集成技术的目标是:软件系 统可以由不同厂商提供的,用不同语言开发的,在不同硬件平台上实现的软件构件,方便地、动态地集成。这些构件要求能互操作,它们可以放在本地的计算机上, 也可以分布式地放置在网上异构环境下的不同结点上。

软件复用技术:软件复用就是将已有的软件成分用于构造新的软件系统。可以被复用的软件成分一般称作可复用构件,无论对可复用构件原封不动地使用还是作适当 的修改后再使用,只要是用来构造新软件,则都可称作复用。

平台+插件开发方法: 插件的本质在于不修改程序主体(平台)的情况下对软件功能进行扩展与加强,当插件的接口公开后,任何公司或个人都可以制作自己的插件来解决一些操作上的不 便或增加新的功能,也就是实现真正意义上的“即插即用”软件开发。平台+插件软件结构是将一个待开发的目标软件分为两部分,一部分为程序的主体或主框架, 可定义为平台,另一部分为功能扩展或补充模块,可定义为插件。

小结

敏捷并行开发方法的基础是基于构件(插件)技术支持的并行,涉及软件开发的分析、设计、实现和测试等过程, 一个完善的开发方法不单单是一个简单的理论基础,还需要相应的基础平台、项目管理工具、开发辅助工具才能构成一个完整的方法体系。

敏捷并行开发方法以AgileEAS.NET做为构件技术运行、管理平台,应用开发人员根据应用需要及AgileEAS.NET平台构件契约进行分析需 要、设计开发应用构件,使用EAS.NET构件管理工具对所开发的构件进行总装集成和管理。

[转载]AgileEAS.NET应用开发平台介绍

mikel阅读(1543)

[转载]AgileEAS.NET应用开发平台介绍 – 魏琼东 – 博客园.

介绍

AgileEAS.NET应用开发平台,简称EAS.NET,是基于敏捷并行开发思想以及.NET构件技术而开发的一个应用系统快速开发平台,用于帮助中 小型软件企业建立一条适合快速变化的开发团队,以达到节省开发成本、缩短开发时间,快速适应市场变化的目的,AgileEAS.NET应用开发平台包含基 础类库、资源管理平台、运行容器、开发辅助工具等四大部分,资源管理平台为敏捷并行开发提供了设计、实现、测试等开发过程的并行,基于 AgileEAS.NET平台的应用系统的各个业务功能子系统,在系统体系结构设计的过程中被设计成各个原子功能模块,各个子功能模块按照业务功能组织成 单独的程序集文件,各子系统开发完成后,由AgileEAS.NET资源管理平台进行统一的集成部署。

AgileEAS.NET应用开发平台也是为应用信息系统开发而提供的一组低层功能集合及开发支撑平台,应用信息系统的开发建立在此平台之上,采用构件 式、可复用开发,节省开发成本,加快开发速度,在软件开发上更好的作到多快省。

AgileEAS.NET平台的核心思想是包含两点,一是基于DotNET构件技术的插件式开发,二是基于敏捷并行开发方法以的构件并行,即应用系统的构 件(模块)同步并行开发,由平台进行总装集成。

起源

2004年我们做一个农业系统的项目,当时我们开发上没几个人,.NET应该刚刚应用于公司不久,以当时的技术力量根本不可能按期交付,所以我们想到了把 某部分模块外包给其他公司。

作为一个新的尝试和创新,我们在对项目进行了自顶向下的细分,把项目分为子系统,子系统分解子系统和模块,模块做为不可细分的业务功能,以模块为计量单位 进行分包。各模块相互独立的进行开发,开发完成后进行集成、测试。

为了适合这种创新,我们要求模块的开发要遵守一定的约定,即一种契约;我们还需要一个能集成、管理和调用这些模块的一套应用程序,即平台。这样就形成了一 种平台+模块(插件)架构。

为此,我们按此思路开发了EAS.NET的最初版本,包含模块的所遵守的接口和一套能够管理、调用模块的WinForm应用程序,当然,最初版本也提供了 基于账号、角色与模块之间的权限管理部分以极一部分基础函数库。

这就是AgileEAS.NET的最早起源,也是EAS.NET的支撑思想“并行开发”的基础。

结构

以上文字简单的对EAS.NET起源及理念做了一个简单的介绍,从以上文字我们知道,EAS.NET平台的核心理念涵盖两个方面,第一方面是基于一种软件 工程的实践,插件模块独立并行开发和总装集成的一种思路,第二方面则是利用.NET技术(反射调用)实现了这种思想。由此可知EAS.NET应该包含以下 四部分内容:第一、软件过程工程的支持,第二、插件标准与平台(运行容器),第三、插件的组织及管理,第四、支持插件快速开发的技术及工具。

image

上图是AgileEAS.NET平台的架构简图,也许看第一眼也不甚明白,我会在后面专门就AgileEAS.NET架构做详细解释,下面我把各部分简要 说明一下。

插件契约定义了一组插件开发的接口,实现这些接口的模块可以被运行容器调用,并可以被资源管理平台所管理。

基础类库是一组用于支持快速开发插件模块的基础组件库,包含IOC、ORM、AOP、UDA、Session等基础类库。

服务容器实现了一个服务插件运行容器,可以动态的调用服务插件,服务插件是实现了插件契约的企业服务组件。

分布式通信/传输服务基于消息技术实现了支持分布式开发所需要的数据通信支持,目前支持传输基础数据类型及ORM组件。

插件运行容器是一组能实现插件调用的一组应用程序,它能实现加载插件并依据权限执行插件的业务功能,目前实现了WinForm、SmartClient、 WebForm、ActiveXForm四件插件运行容器。

资源管理平台是基于插件运行容器的一组插件集合,业务功能包含集成、管理、组织插件、管理账户及角色以及基于插件/角色(账户)的权限机制,目前,资源管 理平台基于WinForm、SmartClient、ActiveXForm运行容器基于实现。

项目管理工具提供基于“并行开发”这种模式的项目管理工具。

辅助工作是一组用于支持快速开发插件模块的基础应用工具,包括插件调试环境、ORM设计器、代码生成器、报表系统等。

AgileEAS.NET自2004年起应用于诸多MIS项目,我也一直忙于生计,没有想去过推广,只是一直在慢慢的完善,很多时间很想写点东西,但一直 都没有去写,最近无事在家休息,就整理了一下这些东西,发到博客园和大家探讨和交流。

我会坚持把这东西写的能让大家明白,在以后的文章中,我会分别将各部分进行详细听介绍,在适当的时候提供给大家下载,也希望大家支持和拍砖。

http://www.agilelab.cn

[转载]领域驱动设计实践

mikel阅读(1266)

[转载]领域驱动设计实践 – 张逸:晴窗笔记 – 博客园.

领域驱动设计的关注重心是领域,尤其在面对复杂的领域逻辑时,它总能够帮助我们很好地分析领域。领域驱动设计的基础是领域建模。Eric认为需要和 领域专家良好地合作,从交谈中发现通用语言,找到领域的关键词。领域建模是迭代的过程,根据逐渐深入的领域知识来精化模型。不过,领域驱动设计并不排斥其 他的分析技术,例如分析模式,或者通过测试驱动开发来引导我们找到问题域的领域模型。

领域建模并非领域驱动设计所独有。在RUP中,领 域建模就是一个非常重要的环节。它是一种用例驱动的开发方法,通过获得的用例来帮助分析和设计人员寻找对象,以及对象之间的关系。根据我个人的经验,我喜 欢采用两种截然不同的方式来获得模型。一种是用例驱动,一种则是测试驱动。在得到初步的领域模型中,我会尝试利用领域驱动设计的思想为对象分类,找到实 体、值对象、聚合以及服务对象,并通过分析对象的生命周期,为不同类型的对象建立资源库和工厂对象。

本文将以一个读者耳熟能详的图书馆 管理系统作为我们要分析的领域,尝试讲解如何在项目开发中应用领域驱动设计。我将选择用例驱动的方式来获得我最初的领域模型。简单起见,我先给出分析领域 的用例以及用例图。

借书:读者携带要借书籍到借书处。图书馆工作人员首先扫描读者的借书卡,获得读者信息,然后扫描书籍的条形码。如果 借阅多本,则扫描多本书籍。扫描时,需要判断当前读者的类型,获得读者可借书籍数。如果借阅书籍超出,则提示。如果扫描失败,允许工作人员手工输入编号。 借阅的期限为1个月。

还书:读者携带要还书籍到还书处。图书馆工作人员扫描书籍的条形码,进行还书操作。如果借阅的书籍超期,则提示, 并计算出应收罚款的数额。如果扫描失败,允许工作人员手工输入编号。

我采用了摘要方式来描述用例。我喜欢这样一种简洁的方式,它实际上 等同于XP中的用户故事。在需求并不复杂的时候,或者在对文档要求并不严格的时候,都可以采用这种方式来编写用例。

以下是表达上述两个 用例的用例图展现:

clip_image002

可以首先利用名词/动词法找到模型中的领域对象。这种方 法虽然极度地简单与低级,然后在建立领域模型之初,是非常有效的手段。通过对用例的分析,大致可以获得如下对 象:Reader,Administrator,Book,Library Card以及Scanner。也许还有我们未曾发现的领域对象,这可以通过深入领域或与客户交谈来进一步获得。我们可以尝试着先获得一个最简单的领域模 型,如下所示。

clip_image004

我们发现Administrator对象是一个孤立的对 象,它与其他领域对象没有产生任何关系。至少在借书、还书用例中,我们并不需要管理这个对象,可以考虑删除它。模型中的Scanner对象非常特殊,表面 上它会对Book与LibraryCard进行操作,然而对于Scanner而言,它并不关心操作的是什么对象,而只需要扫描条形码,返回一个字符串。这 是一种行为的体现。在整个系统中,Scanner对象可以只拥有一个,没有属性和状态,仅提供扫描功能,或者说是服务,因此可以考虑将其定义为服务对象。

Reader与Book之间的关系非常直接,可是在引入LibraryCard之后,这个关系就显得有些尴尬了。仔细阅读用例,我们发现识别读 者的信息,是通过借书卡来获取的。无论是借书还是还书,都可以通过借书卡来获得读者当前借阅的书。此时,读者与书之间就不存在任何关系了,它已经进行了转 嫁。既然借书卡已经实现了对借书关系的管理,我们还有必要保留Reader对象吗?阅读用例,我们知道在扫描借书卡时,会获得读者的信息。虽然我们可以在 借书卡中保留这些信息,但根据单一职责原则(SRP),将其专门封装为一个对象仍有必要。

目前,借书卡仅仅维护了读者当前借阅的书籍, 那么,还需要维护借阅和返还的历史记录吗?从用例的描述来看,并没有这一功能。我们感到疑惑,因为保留历史记录是大多数系统所必备的。此时,客户的答案就 显得格外重要。“哦,是的,我们需要查看历史记录!”这是客户给我们的肯定答复。显然,查看历史记录属于另一个用例,它甚至可能属于另外一个上下文 (Context),例如关于“查询”的上下文。然而,这一信息的来源却来自于借阅与返回用例,我们应该将其识别出来。如果其他用例需要用到,我认为这个 对象是需要共享的。细化后的领域模型如下:

clip_image006

通过对扫描行为的分析,我认为Scanner提供的扫描 行为与领域无关,而是一种基础设施,因此我将其定义为基础设施层的服务。模型增加了FineCalculator对象,用以完成对超期读者的罚款金额计 算。显然,它是一个服务对象。注意,BorrowingHistory与Book是一对一的关系,因为我们需要为每一本书建立一条借阅历史记录。

现在,我们需要识别领域模型中的实体和值对象,以及可能的聚合。我们需要一个唯一的标识来区别读者,且这一标识具有连续性,因此Reader是 一个实体对象。同样,Book对象也是一个实体对象,因为我们需要一个唯一标识来完成对书籍的跟踪。注意,在这个模型中的Book实体,其实例代表的是具 体的某一本书,而不是指同一种书。因为图书馆可能就同一种书购买多本,而读者借阅的是真实的书本,而不仅仅是书的属性。此时,Book的标识ID就显得尤 为重要,甚至不能用书籍的ISBN来标识。

从表面上看,BorrowingHistory同样属于实体对象,它的每一条记录都是唯一 的,即使存在两条历史记录,具有相同的读者ID与书籍ID,我们仍将其视为不同的记录,因为它们的借阅时间并不相同。不过,对于系统的调用者而言,通常不 会去关注所有的借阅记录,而是查询某位读者的借阅记录,因此,我们可以将其作为与Reader放在一起的聚合。然而,随着对需求的深入分析,我们发现定义 这样的聚合存在问题,因为我们可能还需要查询某本书的借阅记录(例如,希望知道哪本书最受欢迎,跟踪每本书的借阅情况等)。由于Reader和Book应 该分属于不同的聚合,BorrowingHistory就存在无法划定聚合的问题。既然如此,我们应该将其分离出来,作为一个单独的聚合根。

让 人感觉疑惑不解的是LibraryCard对象。一方面,它的ID来源于Reader,且存在一对一的关系,因此它可以作为Reader聚合的一部分。根 据模型图来看,它实际上又记录了读者与书之间的关系。仔细分析,LibraryCard所维护的这样一种读者与书的关系,事实上正是 BorrowingHistory的一种体现,区别仅在于一个记录了当前的借书信息,一个还包括过去的借书信息。BorrowingHistory可以进 行信息的持久化,LibraryCard则完全可以在内存中维持一个当前借阅信息的集合。因此,可以将LibraryCard定义在Reader聚合中。 这样既可以减少对象之间的关联,又能保证对象之间的一致性。

我们还需要深入分析Reader对象和Book对象的标识ID,因为这两者 的标识ID都是通过基础设施的Scanner服务获得的。Scanner并没有能力知道二者之间的区别。而在借阅书籍时,根据需求规定的流程,必须是先扫 描借书卡,获得读者信息,然后再扫描书。此外,当扫描出现错误时,系统需要支持操作人员手工输入,因此对手工输入的内容也需要进行ID的验证。我们需要有 专门验证ID的对象。

我们还要考虑许多业务规则,例如是否允许读者借书的规则,是否超期的规则,计算罚款额度的规则。如果这些规则极为 简单,且不具有变化的可能,可以放在领域对象中。然而,一旦规则变得复杂,就会严重干扰相关领域对象的职责。根据职责分离的原则,我们可以提供专门的规则 对象,即领域驱动设计中规格模式的应用。如果可能变化,我们甚至可以引入策略模式,对这些规则进行抽象。经过分析后得到的领域模型如下所示:

clip_image008

Reader实体对象和LibraryCard实体对象 处于同一个聚合中,其中Reader为聚合根。BorrowingSpecification和ReturningSepecification均为值对 象,并放在Reader聚合中。FineCalculator是一个服务对象,它会调用FineRule值对象获得罚款规则,通过计算后返回Money值 对象值。由于聚合的原因,原来FineCalculator与LibraryCard之间的关系已经修改为计算Reader的罚款。

BorrowingHistory 和Book均为实体对象,而IdentityValidator则为服务对象,负责验证扫描码。

接下来需要为领域对象选择资源库 (Repository)。在领域模型中,只有Reader、BorrowingHistory和Book三个实体为聚合根对象,因此只需要为这三个对象 建立资源库对象即可。

clip_image010

由于需求较为简单,建立的领域模型已经比较完善,我们可 以着手编码,对这些模型进行验证。本文没有考虑限定上下文的情况,我希望未来的文章能够以真实的案例对此进行表述。整体而言,根据这个案例,我们已经能够 初步领略领域驱动设计的基本步骤。

[转载]程序员应知——数据库设计的两个误区

mikel阅读(1045)

[转载]程序员应知——数据库设计的两个误区 – 侯伯薇的凌云晓筑 – 博客园.

写在前面:本来“程序员应知”系列中应该写的都是与程序员密切相关的内容,而数据库设计似乎应该是数据库管理员的工作。然而,在实际的工作 环境中,我所经历几乎所有的项目中,数据库设计工作都是由程序员来完成的;就算我们是不需要做数据库设计的程序员,也至少需要对数据库的结构有充分的理 解,那样也便于我们编写和维护系统。思量再三,我还是将这篇与数据库设计相关的文章放在了这个系列当中。

在几乎所有的企业级应用程序中,包括各种MIS、ERP、CRM等等,都会使用数据库,这样的好处是显而易见的,很容易地实现了数据层和业务逻辑层 的分离,而且对于性能的优化也在一定程度上提供了便利。

然而,在我所经历过的项目中,某些数据库的设计会存在一些问题,尤其普遍的就是下面将要描述的这两点,个人觉得是应该避免的误区,总结出来与大家讨 论。

误区之一 备用字段

现象描述:

在数据表中,不仅设计了当前所需要的字段,而且还在其中留出几个字段作为备用。

比方说,我设计了一个人员表(Person),其中已经添加了各种必要的字段,包括姓名(Name)、性别(Sex)、出生年月日 (birthday)等等。大功告成之后,我忽然想到,将来系统中应 该还会有很多其它与人相关的内容吧,比方说毕业院校,比方说工作单位等等,尽管现在根本不需要填写,以后可能还是会用到的吧。拍脑 袋一项,那就加入5个varchar2型的字段,分别叫做Text1、Text2……Text5,然后又想,应该还有一些日期型的字段需要备用,就又建立 了三个date型的字段,分别起名叫做date1、date2、date3,……

原因分析:

大家应该已经看出问题了,在这个数据表中存在大量暂时无用的字段,我们可以称之为备用字段,它们的作用是什么呢?就是以防万一,防备可能的情况

这似乎可以叫做防患于未然,等到时候需要的时候,就不需要在表中增加新的字段了,而且这样做的话,一个表的数据应该会被存储在相邻的物理空间中,这 对于性能也是有好处的。

另外的原因就是,在古老的数据库中,如果改变数据库的定义(包括增加字段、改变字段的类型、删除字段等等),那么其中所有的数据就会丢失,所以这项 工作非常麻烦,我们需要先建立临时表,将数据备份出来,然后创建新表,将数据导入其中,最后再删除原来的表。

问题所在:

这样的做法对于项目会导致很多问题,而且原先想要解决的问题并不一定能够解决,不信的话,请往下看。

问题一:增加大量备用字段,必定会浪 费很多空间,尽管其中可能都没有具体的数据,但是仅仅是空字段也会占据一定的空间的。

问题二:由于命名的特点,如果没有完善 的文档管理流程,用不了多久(可能也就是两三年),就没有人能够说清 楚到底哪个字段代表的是什么意义了。就算有文档管理,这些管理工作也会比较麻烦,而且在每次使用的时候都需要申请,还有可能会出现 冲突的情况。

问题三:增加了这些备用字段就真的会够用吗?不 一定,因为我们只是每个类型的字段留出几个备用,如果数量超过,或者要使用特殊的、不常用的类型的时候,还是需要增加新的字段。比方说在上述的 Person表中,我们要存储照片,那么可能就要增加一个blob类型的photo字段,这在初期设计的时候可不一定会留出这样的备用字段。而且如果没有 完善的管理,谁又能说清楚倒底哪个字段已经被使用,哪个字段还可以使用呢?到时候还不是要增加新的字段。

解决方案:

其实上面的这种设计方式就是一种“过度设计”,我们应该做的就是“按需设计”,在经过详细有效的分析之后,在数据表中只放置必要的字段,而不要留出大量的备用字段。

当需要增加相关的信息的时候,就要具体情况具体分析:

如果数量很少,而且信息的性质与原表密切相关,那么就可以直接在原表上增加字段,并将相关的数据更新进去。

如果数量较大,或者并非是原表对象至关重要的属性,那么就可以新增一个表,然后通过键值连接起来。

对于表的数据的存储位置所导致的性能问题,我们可以通过在特定时间对数据库的数据进行重组来解决,而这项工作对于长期运行的数据库来说,也是需要定 期进行的。

误 区之二 有意义的编码

现象描述:

使用有意义的编码作为一条记录的ID,甚至作为数据库的主键存在,例如,一个员工的编码设置为0203004,其中02代表员工所在分公司,03代 表员工所在部门,004代表员工进入到该部门的序号。

原因分析:

ID的设置方式大概有以下几种,一种是纯粹的流水号,从1开始,每次加1,或者对其将以改进,将数字转换成为字符串的格式,比方说 “0000001”;一种是无意义的随机编码,比方说GUID;还有一种就是有意义的编码,特定的位数会代表一定的意义。

我想之所以大家这么喜欢使用这种方式,主要是因为想要从编码中就能够得到一些信息,甚至有些程序中还有专门的对编码进行解析的模块。就像我们的身份 证号码一样,看到身份证号就可以知道办身份证时的所在地、生日、性别等信息。

问题所在:

其实有意义的编码会导致很多问题,请看:

问题一:对编码资源的浪费。如果是纯粹 的流水号,那么从1到10000就可以代表一万条记录,但是,如果使用有意义的编码,很可能1000条记录就会让五位的编码不够用。我就遇到过真正的情 况,我们公司的投保单号码的第一位就是有意义的,代表的时该投保单所属的渠道,后面跟着很长的一串数字(9位)。理论上来说,这些编码永远都不会用完,但 是,最开始的三个渠道使用的是1、4、7三个编码,但是一次新保险法的实行,导致原有的投保单作废,于是又启用了三个数字2、5、8,接下来公司改名,三 个渠道又分别将投保单报废,重新启用新的开头数字,就这样,短短的几年间,所有的投保单号码全都被用完了,其实打印出来的投保单不过100万张。

问题二:不一定是唯一的,难以作为主键。 想一下,我们的身份证号码就是这样的。原先15位的时候,后三位是序号,而男性会使用奇数,女性会使用偶数,这样就是说,一个地区同一天生日的人,男女都 不能超过500人,否则就会导致号码的重复,尽管出现这种现象的概率比较低,但是还是客观存在的。

问题三:代表的意义不一定准确。比方说 用带有意义的编码来为员工定义工号,其中可能会有部门、职务等等意义,但是如果员工在部门间发生了调动,或者职级发生了改变,是否需要改变他的编码呢?改 变吧,那么所有的历史数据都要随之修改一次,工作量会非常大;不改变吧,那么代表的意义就不再准确,我们就无法从编码中得到该员工准确的信息。

解决方案:

所以,对于编码,非常不建议使用有意义的编码, 要么使用纯粹的流水号,但这样可能需要定义一个范围比较大的类型,对于海量记录的数据,可能会不够用;那样的话就可以使用GUID,这样编码永远都不会重 复,而且会有大量的编码资源可用。

从上面的两点我们可以看出,在数据库设计的过程中,有一些在非常多系统中都使用了,但是却带来了很多问题的方法,对于这种情况,我们就应该仔细思 考,然后痛下决心,坚决抵制。

作者:侯伯薇@大连
出处:http://www.cnblogs.com/houbowei/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

[转载]使用flash buidler 4制作一个迅雷快车旋风地址转换(附源代码)

mikel阅读(1037)

[转载]使用flash buidler 4制作一个迅雷快车旋风地址转换(附源代码) – 每天只要进步一点 – 博客园.

纪念独立博客开博一周年,并且纪念下一年前学习flex,特弄个小Demo,顺便使用下flash buidler 4。
开发需求:比如有时下 载个东西,上面的地址是flashget地址,你用迅雷就无法下载,那么就要进行地址转换。
要想开发这个必须知道迅雷,快车及旋风的加密方式,经 过参考网上很多的此类似工具转换得出以下:
【注:转载此文请注明转载地址或加上我的博客地址http://www.ajaxcn.net /archives/1013】
迅雷的加密的方式:
thunder://”+ 使用base64转换加密(“AA”+下载地址+”ZZ”);
代 码=”thunder://”+Base64.encode(“AA”+txtresult.text+”ZZ”);

快车的加密的 方式:
flashget://”+使用base64转换加密 (“[FLASHGET]”+下载地址+”[FLASHGET]”)+”&111″;//&后可随意
旋风加密的方 式:
“qqdl://”+使用base64转换加密(下载地 址);
以上是加密,解密的话就是反向求出上面的下载地址
比如迅雷:先要去掉thunder:// 就是要截断去掉前10个字,然后通过base64转换解密再去掉
“AA”和”ZZ”字母,在flex中代码大致如下:
url=url.substr(10,url.length-10);
str=Base64.decode(url);
str=str.substr(2,str.length-4);
另外说明:上面 Base64.encode,Base64.decode,Base64在flex中并没有内置函数,需要引用一个as3base64.swc,
并 且需要引用 import com.dynamicflash.util.Base64;具体使用不明白请参考:http://www.dynamicflash.com /goodies/base64
其它快车和旋风解密类似,具体看代码吧!
演示地址:http://www.ajaxcn.net/tools/httpconvert/httpconvert.html
源 码下载地址:

httpconver (23.8 KiB, 1 hits)
全 部flex mxml文件如下:

代码

<?xml version=1.0 encoding=utf-8?>
<s:Application xmlns:fx=http://ns.adobe.com/mxml/2009
xmlns:s
=library://ns.adobe.com/flex/spark
xmlns:mx
=library://ns.adobe.com/flex/mx minWidth=955 minHeight=600 >

<fx:Declarations>
<!– 将非可视元素(例如服务、值对象)放在此处 –>
</fx:Declarations>
<fx:Script>
<![CDATA[
import com.dynamicflash.util.Base64;

import mx.controls.Alert;
import mx.utils.ArrayUtil;
protected function btnC_clickHandler(event:MouseEvent):void
{
if(txtresult.text==)
{
mx.controls.Alert.show(
请输入链接地址);
return;
}
var str:String
=;
str
+=迅雷加密后:\n;
str
+=thunder://+Base64.encode(AA+txtresult.text+ZZ);
str
+=\n;
str
+=快车加密后:\n;
str
+=flashget://+Base64.encode([FLASHGET]+txtresult.text+[FLASHGET])+&www.ajaxcn.net;
str
+=\n;
str
+=旋风加密后:\n;
str
+=qqdl://+Base64.encode(txtresult.text);
str
+=\n;
lblresult.text
=str;

}

protected function btnR_clickHandler(event:MouseEvent):void
{
var url:String
=new String();
url
=txtresult.text;
var str:String
=“”;
if(url.toLowerCase().indexOf(thunder)>-1)
{
url
=url.substr(10,url.length10);
str
=Base64.decode(url);
str
=str.substr(2,str.length4);
str
=迅雷解密后:+str;
lblresult.text
=str;

}
else if(url.toLowerCase().indexOf(flashget)>-1)
{
url
=url.substr(11,(url.length11(url.lengthurl.indexOf(&))));
str
=Base64.decode(url);
str
=str.substr(10,str.length20);
str
=快车解密后:+str;
lblresult.text
=str;
}
else if(url.toLowerCase().indexOf(qqdl)>-1)
{
url
=url.substr(7,url.length7);
str
=Base64.decode(url);
str
=旋风解密后:+str;
lblresult.text
=str;
}
else
{
Alert.show(
地址出错);
}

}

protected function btncopy_clickHandler(event:MouseEvent):void
{
System.setClipboard(lblresult.text);
Alert.show(
复制成功!);
}

]]>
</fx:Script>
<s:TextInput id=txtresult x=118 y=108 width=594/>
<s:Button x=731 y=106 label=加密 id=btnC click=btnC_clickHandler(event) />
<s:Button x=809 y=106 label=解密 id=btnR click=btnR_clickHandler(event)/>
<s:Label x=61 y=118 text=链接地址:/>
<s:Label x=75 width=80% y=168 text=结果:/>
<s:TextArea id=lblresult x=118 y=168 width=601/>
<s:Button toolTip=把结果复制到剪切板 id=btncopy x=727 y=246 label=复制结果 click=btncopy_clickHandler(event)/>
<s:Label x=118 y=41 text=欢迎使用云飞扬IT开发的在线迅雷、快车、旋风与页面加解密 fontFamily=中易宋体 fontSize=20 width=663/>
</s:Application>

[转载]31 天重构学习笔记索引

mikel阅读(1018)

[转载]31 天重构学习笔记索引 – 圣殿骑士 – 博客园.

由于最近在做重构的项目,所以对重构又重新进行了一遍学习和整理,对31天重构最早接触是在2009年10月份,由于当时没有订阅Sean Chambers的blog,所以是在国外的社区上闲逛的时候链接过去的。记得当时一口气看完了整个系列并没有多少感觉,因为这些基本上项目都 在使用,只是我们没有专门把它标示和整理出来,所以也没有引起多大的重视。现在突然接手这个重构项目,由于团队成员技术和经验参差不齐,所以有必要专门整 理一个重构的纲要,当然这个系列也非常适合做新系统的代码规范参考,只要有代码的地方,这个重构规范就很有价值。周末也不想出去闲逛,因为在刚到这个美丽 的城市,没有亲戚或者朋友,所以才能静下心来两天时间写完这个重构参考规范。同时也感受了Windows Live writer写文章的快感。当然这个项目重构的整体架构得另当别论(整体架构在我的这篇文章有专门的讲解(http://www.cnblogs.com/zenghongliang/archive/2010/06/23/1763438.html)。 大的架构设计好了以后,这些重构细节点就成了东风之后的大火,对整个项目也是至关重要。31天重构这个系列和《代码大全》、《重构:改善既有代码的设计》 比较起来最大的特点就是比较简单、浅显易懂。那么我这些文章也都是学习Sean Chambers的31天重构的笔记整理,所以如果大家对这个笔记有任何异议也可以指出。

具体也可以通过http://www.lostechies.com/blogs/sean_chambers/archive/2009/07/31/31-days-of-refactoring.aspx查 看原文。

写得不好,还请多多见谅,如有问题或建议,敬请指教,谢谢!

以后打算在博客园安家,写下自己对技术的一点微薄见解,所以特此做一个文章索引 圣 殿骑士博文索引,如果有感兴趣的同仁,可以关注,如能起到帮助作用,那就是我莫大的荣幸!
作者:圣殿骑士
出处:http://www.cnblogs.com/zenghongliang/
关于作者:专注于微软平台项目架构、管理和企业解决方案。自认在面向对象及面向服务领域有一定的造诣,熟悉设计模式、TDD、极限编程、领域驱动、架构设 计、敏捷开发和项目管理。现主要从事WinForm、ASP.NET、WPF、WCF、WF、Silverlight 、Biztalk、Windows Azure方面的项目开发、架构和管理工作。如有问题或建议,请多多赐教!
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,如有问题,可以通过MSN(KnightsWarrior@msn.com ) 联系我,非常感谢。

[转载]C# 用装饰模式实现蝌蚪变青蛙的过程

mikel阅读(927)

[转载]C# 用装饰模式实现蝌蚪变青蛙的过程 – Richard 时间@脚印 – 博客园.

在面向对象的理论中,始终强调是抽象与组合以及一些其他面向对象的机制比如继承,封装和多态。

前面的文章都将有抽象是什么,至于抽象如何使用,就是把实现往上总结共性而去除特性,由此得到抽象层次和实现层次,所以设计模式中的基本上很多都是 利用这种方法来实现面向对象的抽象,以使得软件能够降低变化带来的影响。

我们知道C#是一种强类型的语言,也就是对象的行为在定义的时候就确定好了,如果要使用另外的行为,要么使用静态定义时期的继承机制,另外一种方法 就是组合(是一种动态机制,可以在程序运行期间给对象加上行为,这就是造就个体的差异,这种和我们的发展观是符合的。实际上在设计的过程 中就是一动一静相互平衡和谐。)

接下来讲的是在动态时期给对象实例加上行为。

故事是一只动物也就是上帝创造的一只蝌蚪,或许生下来就能游泳,然后经历一段时间的进化慢慢长成了青蛙,青蛙王子就能呼吸了。

JavaScript动态弱类型的实现动态给对象实例加上行为是没有难度的,可是给一个对象实例加上行为在C#这种静态类型机制中,实现是有难度 的,不过现在的C#支持动态属性这种方法。

在设计模式中也有一种装饰(decorator)模式,意图是给对象增加行为。

image

实现的过程是先定义蝌蚪,青蛙都是一种动物类型。

namespace DecoratorPattern
{
abstract class Animal
{
public abstract void Run();
}
}

namespace DecoratorPattern
{
class Tadpole :Animal
{
private Animal animal;

public Tadpole(Animal animal)
{
this.animal = animal;
}

public override void Run()
{
Console.WriteLine(“我是一只蝌蚪”);
}
}
}

namespace DecoratorPattern
{

class Frog:Animal
{
private Animal animal;

public Frog(Animal animal)
{
this.animal = animal;
}

public override void Run()
{
Console.WriteLine(“我是一只青蛙”);
}
}
}

然后游泳行为和呼吸行为也抽象为对象(在这里没有定义成接口)

namespace DecoratorPattern
{
class Swimable : Animal
{
private Animal animal;

public Swimable(Animal animal)
{
this.animal = animal;
}

public override void Run()
{
animal.Run();
Console.WriteLine(“我是能游泳的”);
}
}
}

namespace DecoratorPattern
{
class Breathable : Animal
{
private Animal animal;

public Breathable(Animal animal)
{
this.animal = animal;
}

public override void Run()
{
animal.Run();
Console.WriteLine(“我上岸了,我能呼吸啦.”);
}
}
}

上帝类型负责造物以及进化的过程

这样客户端看不出后台发生了什么变化,使用静态工厂模式隐藏创建和进化的过程。这样我们的客户端代码才会看起来优美流畅(哈哈顺便把我的代码也包括 了)。

namespace DecoratorPattern
{
class God
{
public static Tadpole CreateKedou()
{
return new Tadpole(null);
}

internal static Swimable FirstJinhua(Animal single)
{
Swimable swimable = new Swimable(new Tadpole(single));
return swimable;
}

internal static Breathable SecondJinhua(Animal single)
{
Breathable breathable = new Breathable(new Frog(single));
return breathable;
}
}
}

具体的源码见附件。TagpoleBecomesFrog