`
lovnet
  • 浏览: 6711465 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
文章分类
社区版块
存档分类
最新评论

Asp.net MVC FluentHTML and Fluent Interface

阅读更多

赏花归去马如飞, 去马如飞酒力微.
酒力微醒时已暮
, 醒时已暮赏花归.---苏轼

我们力求页面层代码简洁并具有较好的可读性,Asp.net MVC的平台上,我们以新的起点来实现这一目标.MvcContrib.FluentHtmlSpark ViewEngine给我们做出了榜样.本文将以MvcContrib.FluentHtml为例探究它的实现机制:Fluent Interface.

读过开篇的诗句,不知是否感受到文字之美.不仅仅是在文学作品中,在代码中,这种美一样存在.

MvcContrib.FluentHtml的应用中,我们随处可以见到下面的代码:

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><%=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/>
浏览器中生成的代码为:
<labelid="Person_Name_Label"for="Person_Name">Name:</label>
<inputid="Person_Name"type="text"value="Jeremy"title="Entertheperson'sname"name="Person.Name"maxlength="50"/>
.
<selectid="Person_Gender"title="Selecttheperson'sgender"size="5"name="Person.Gender">
<optionvalue="M"selected="selected">Male</option>
<optionvalue="F">Female</option>
</select>

上面对动态生成TextBoxSelect的代码很有意思,我们使用普通的方式在页面上生成同样的客户端代码,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",这并不是什么新概念,2005Eric 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,很容易找到StringBuilderAppend方法:

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->publicStringBuilderAppend(stringvalue)
{
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:

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->publicinterfaceIContentBuilder
{
voidWriteContent();
IContentBuilderAppend(
stringpartialContent);
}
publicclassTestContentBuilder:IContentBuilder
{
stringtemp;
#regionIContentBuilderMembers

voidIContentBuilder.WriteContent()
{
Console.Write(temp);
}

IContentBuilderIContentBuilder.Append(
stringpartialContent)
{
temp
+=partialContent;
returnthis;
}

#endregion
}
……
//调用代码
IContentBuildert=newTestContentBuilder();
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>一窥端倪:

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->publicabstractclassInput<T>:FormElement<T>whereT:Input<T>,IElement
{
protectedobjectelementValue;

protectedInput(stringtype,stringname):base(HtmlTag.Input,name)
{
builder.MergeAttribute(HtmlAttribute.Type,type,
true);
}

protectedInput(stringtype,stringname,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>
publicvirtualTValue(objectvalue)
{
elementValue
=value;
return(T)this;
}

///<summary>
///Setthe'size'attribute.
///</summary>
///<paramname="value">Thevaluefortheattribute.</param>
publicvirtualTSize(intvalue)
{
Attr(HtmlAttribute.Size,value);
return(T)this;
}

protectedoverridevoidPreRender()
{
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:

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->namespaceMvcContrib.FluentHtml
{
///<summary>
///ExtensionstoIViewDataContainer
///</summary>
publicstaticclassViewDataContainerExtensions
{
///<summary>
///GenerateanHTMLinputelementoftype'text'andsetitsvaluefromViewDatabasedonthenameprovided.
///</summary>
///<paramname="view">Theview.</param>
///<paramname="name">Valueofthe'name'attributeoftheelement.Alsousedtoderivethe'id'attribute.</param>
publicstaticTextBoxTextBox(thisIViewDataContainerview,stringname)
{
returnnewTextBox(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也是htmlHelperExtension Method.相比较起来,htmlHelper提供了基础的页面控件定义和构造,FluentHTML表现的更为灵活.除了FluentHTML,著名的Spark View Engine也有类似的实现,大家可以关注一下.

嗯哼,全文完.

P.S:最近要找工作了,有好机会联系我:mailto:ligaoren@gmail.com

坚强2002和你一起回头再说...
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics