赏花归去马如飞, 去马如飞酒力微.
酒力微醒时已暮, 醒时已暮赏花归.---苏轼
我们力求页面层代码简洁并具有较好的可读性,在Asp.net MVC的平台上,我们以新的起点来实现这一目标.MvcContrib.FluentHtml和Spark ViewEngine给我们做出了榜样.本文将以MvcContrib.FluentHtml为例探究它的实现机制:Fluent Interface.
读过开篇的诗句,不知是否感受到文字之美.不仅仅是在文学作品中,在代码中,这种美一样存在.
在MvcContrib.FluentHtml的应用中,我们随处可以见到下面的代码:
<%
=
this.TextBox(x
=>
x.Person.Name).Title(
"
Entertheperson'sname
"
).Label(
"
Name:
"
)
%>
<
br
/>

……
<%
=
this.Select(x
=>
x.Person.Gender).Options(Model.Genders).Size(
5
).Label(
"
Gender:
"
)
.Title(
"
Selecttheperson'sgender
"
)
%>
<
br
/>
浏览器中生成的代码为:
<
label
id
="Person_Name_Label"
for
="Person_Name"
>
Name:
</
label
>
<
input
id
="Person_Name"
type
="text"
value
="Jeremy"
title
="Entertheperson'sname"
name
="Person.Name"
maxlength
="50"
/>


.
<
select
id
="Person_Gender"
title
="Selecttheperson'sgender"
size
="5"
name
="Person.Gender"
>
<
option
value
="M"
selected
="selected"
>
Male
</
option
>
<
option
value
="F"
>
Female
</
option
>
</
select
>
上面对动态生成TextBox和Select的代码很有意思,我们使用普通的方式在页面上生成同样的客户端代码,CS代码大致是这样的:
Label label = new Label();
label.Text = "Name";
TextBox textbox= new TextBox();
textbox.ToolTip ="Enter the person's name";
textbox.ID = "No.10001";
textbox.ID = "Person.Name";
而FluentHtml创建页面元素的方式让我们很容易联想到StringBuilder的使用:
StringBuilder stringbuilder = new StringBuilder();
stringbuilder.Append("Hello").Append(" ").Append("World!");
Fulent Interface
这种实现编程方式就是"Fluent Interface",这并不是什么新概念,2005年Eric Evans 和Martin Fowler就为这种实现方式命名.源文档 <http://www.martinfowler.com/bliki/FluentInterface.html> 可以通过维基百科中对Fluent Interface的描述获得一个基本的了解:In software engineering, a fluent interface (as first coined by Eric Evans and Martin Fowler) is a way of implementing an object oriented API in a way that aims to provide for more readable code.
我们分解上面的话:
- 它是面向对象API的一种实现方式
- 目的是增加代码的可读性
既然我们最熟悉的是StringBuilder,我们就从这个线索追下去:打开Re
flector,很容易找到StringBuilder的Append方法:
public
StringBuilderAppend(
string
value)
{
if(value!=null)

{
stringstringValue=this.m_StringValue;
IntPtrcurrentThread=Thread.InternalGetCurrentThread();
if(this.m_currentThread!=currentThread)

{
stringValue=string.GetStringForStringBuilder(stringValue,stringValue.Capacity);
}
intlength=stringValue.Length;
intrequiredLength=length+value.Length;
if(this.NeedsAllocation(stringValue,requiredLength))

{
stringnewString=this.GetNewString(stringValue,requiredLength);
newString.AppendInPlace(value,length);
this.ReplaceString(currentThread,newString);
}
else

{
stringValue.AppendInPlace(value,length);
this.ReplaceString(currentThread,stringValue);
}
}
returnthis;
}
阅读这段有两个特别要注意的点:1.方法的返回值是StringBuilder类型 2.最后一句:return this;为了深刻理解,我们写一个简单的StringBuilder:
{
void WriteContent();
IContentBuilderAppend( string partialContent);
}
public class TestContentBuilder:IContentBuilder
{
string temp;
#region IContentBuilderMembers
void IContentBuilder.WriteContent()
{
Console.Write(temp);
}
IContentBuilderIContentBuilder.Append( string partialContent)
{
temp += partialContent;
return this ;
}
#endregion
}
……
// 调用代码
IContentBuildert = new TestContentBuilder();
t.Append( " test " ).Append( " Hello " ).WriteContent();
跑一下代码,和StringBuilder效果是一样的.从上面的应用也可以看出:Fluent Interface经常用来完成对象的构造和属性赋值.
言归正传:FluentHTML
了解了Fluent Interface,我们来看一下MVCContrib.FluentHTML的实现,这里以TextBox为例进行考察,首先看一下它的继承关系:
public class TextBox : TextInput<TextBox>
public abstract class TextInput<T> : Input<T>, ISupportsMaxLength where T : TextInput<T>
public abstract class Input<T> : FormElement<T> where T : Input<T>, Ielement
泛型是一种高层次的算法抽象,我们就通过Input<T>一窥端倪:
{
protected object elementValue;
protected Input( string type, string name): base (HtmlTag.Input,name)
{
builder.MergeAttribute(HtmlAttribute.Type,type, true );
}
protected Input( string type, string name,MemberExpressionforMember,IEnumerable < IBehaviorMarker > behaviors)
: base (HtmlTag.Input,name,forMember,behaviors)
{
builder.MergeAttribute(HtmlAttribute.Type,type, true );
}
/// <summary>
/// Setthe'value'attribute.
/// </summary>
/// <paramname="value"> Thevaluefortheattribute. </param>
public virtual TValue( object value)
{
elementValue = value;
return (T) this ;
}
/// <summary>
/// Setthe'size'attribute.
/// </summary>
/// <paramname="value"> Thevaluefortheattribute. </param>
public virtual TSize( int value)
{
Attr(HtmlAttribute.Size,value);
return (T) this ;
}
protected override void PreRender()
{
Attr(HtmlAttribute.Value,elementValue);
base .PreRender();
}
}
以Size方法为例,可以看出这是一种典型的Fluent Interface实现:
public virtual T Size(int value)
{
Attr(HtmlAttribute.Size, value);
return (T)this;
}
分析到这里,上面的语句中还有一点比较奇怪,就是Lambda表达式的部分:
this.TextBox(x => x.Person.Name).Title("Enter the person's name").Label("Name:")
TextBox的实现代码里面我们没有看到对Lambda表达式的支持.那是在什么地方完成的呢?通过跟进,我们来到了ViewDataContainerExtensions,它是IViewDataContainer 的Extension Method:
{
/// <summary>
/// ExtensionstoIViewDataContainer
/// </summary>
public static class ViewDataContainerExtensions
{
/// <summary>
/// GenerateanHTMLinputelementoftype'text'andsetitsvaluefromViewDatabasedonthenameprovided.
/// </summary>
/// <paramname="view"> Theview. </param>
/// <paramname="name"> Valueofthe'name'attributeoftheelement.Alsousedtoderivethe'id'attribute. </param>
public static TextBoxTextBox( this IViewDataContainerview, string name)
{
return new TextBox(name).Value(view.ViewData.Eval(name));
}
……
看一下return new TextBox(name).Value(view.ViewData.Eval(name));所以这里就成了TextBox定义方法链的第一步.
总结
为了能够在View中能够简洁清晰的构造HTML元素,Asp.net MVC中通过htmlHelper.InputHelper来实现页面元素的构造. 页面层所使用的<%= Html.TextBox("username") %>,HTML也是htmlHelper的Extension Method.相比较起来,htmlHelper提供了基础的页面控件定义和构造,而FluentHTML表现的更为灵活.除了FluentHTML,著名的Spark View Engine也有类似的实现,大家可以关注一下.
嗯哼,全文完.
P.S:最近要找工作了,有好机会联系我:mailto:ligaoren@gmail.com
FluentInterface之美
本文探讨了FluentInterface在Asp.net MVC平台上的应用,通过MvcContrib.FluentHtml库实现了更简洁且可读性强的页面元素生成。介绍了FluentInterface的概念及其在StringBuilder中的应用,并详细分析了TextBox组件的实现机制。
156

被折叠的 条评论
为什么被折叠?



