Asp.net MVC FluentHTML and Fluent Interface

 

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

        

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

 

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

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

 

     <% =  this.TextBox(x  =>  x.Person.Name).Title( " Enter the person's name " ).Label( " Name: " %> < br  />
        
        …  …        
        
<% =  this.Select(x  =>  x.Person.Gender).Options(Model.Genders).Size( 5 ).Label( " Gender: " )
                .Title(
" Select the person's gender " %> < br  />  
 浏览器中生成的代码为:
< label  id ="Person_Name_Label"  for ="Person_Name" > Name: </ label >
< input  id ="Person_Name"  type ="text"  value ="Jeremy"  title ="Enter the person's name"  name ="Person.Name"  maxlength ="50" />
 .
< select  id ="Person_Gender"  title ="Select the person's gender"  size ="5"  name ="Person.Gender" >
< option  value ="M"  selected ="selected" > Male </ option >
< option  value ="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方法:

public  StringBuilder Append( string  value)
{
    
if (value != null)
    
{
        
string stringValue = this.m_StringValue;
        IntPtr currentThread 
= Thread.InternalGetCurrentThread();
        
if (this.m_currentThread != currentThread)
        
{
            stringValue 
= string.GetStringForStringBuilder(stringValue, stringValue.Capacity);
        }

        
int length = stringValue.Length;
        
int requiredLength = length + value.Length;
        
if (this.NeedsAllocation(stringValue, requiredLength))
        
{
            
string newString = this.GetNewString(stringValue, requiredLength);
            newString.AppendInPlace(value, length);
            
this.ReplaceString(currentThread, newString);
        }

        
else
        
{
            stringValue.AppendInPlace(value, length);
            
this.ReplaceString(currentThread, stringValue);
        }

    }

    
return this;
}

 

阅读这段有两个特别要注意的点:1.方法的返回值是StringBuilder类型 2.最后一句:return this;为了深刻理解,我们写一个简单的StringBuilder:

 

public   interface  IContentBuilder
    {
        
void  WriteContent();
        IContentBuilder Append(
string  partialContent);
    }
    
public   class  TestContentBuilder : IContentBuilder
    {
        
string  temp;
        
#region  IContentBuilder Members

        
void  IContentBuilder.WriteContent()
        {
            Console.Write(temp);
        }

        IContentBuilder IContentBuilder.Append(
string  partialContent)
        {
            temp 
+=  partialContent;
            
return   this ;
        }

        
#endregion
    }
… …
// 调用代码
IContentBuilder t  =   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>一窥端倪:

 

public   abstract   class  Input < T >  : FormElement < T >   where  T : Input < T > , IElement
{
    
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, MemberExpression forMember, IEnumerable < IBehaviorMarker >  behaviors)
        : 
base (HtmlTag.Input, name, forMember, behaviors)
    {
        builder.MergeAttribute(HtmlAttribute.Type, type, 
true );
    }

    
///   <summary>
    
///  Set the 'value' attribute.
    
///   </summary>
    
///   <param name="value"> The value for the attribute. </param>
     public   virtual  T Value( object  value)
    {
        elementValue 
=  value;
        
return  (T) this ;
    }

    
///   <summary>
    
///  Set the 'size' attribute.
    
///   </summary>
    
///   <param name="value"> The value for the attribute. </param>
     public   virtual  T Size( 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:

 

namespace  MvcContrib.FluentHtml
{
    
///   <summary>
    
///  Extensions to IViewDataContainer
    
///   </summary>
     public   static   class  ViewDataContainerExtensions
    {
        
///   <summary>
        
///  Generate an HTML input element of type 'text' and set its value from ViewData based on the name provided.
        
///   </summary>
        
///   <param name="view"> The view. </param>
        
///   <param name="name"> Value of the 'name' attribute of the element.  Also used to derive the 'id' attribute. </param>
         public   static  TextBox TextBox( this  IViewDataContainer view,  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也是htmlHelperExtension Method.相比较起来,htmlHelper提供了基础的页面控件定义和构造,FluentHTML表现的更为灵活.除了FluentHTML,著名的Spark View Engine也有类似的实现,大家可以关注一下.

 

 嗯哼,全文完.

 

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

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值