您的位置:首页 > 编程语言 > ASP

Asp.net MVC FluentHTML and Fluent Interface

2009-06-15 08:51 381 查看
 
        赏花归去马如飞, 去马如飞酒力微.
        酒力微醒时已暮, 醒时已暮赏花归. ---苏轼
        
  我们力求页面层代码简洁并具有较好的可读性,在Asp.net MVC的平台上,我们以新的起点来实现这一目标.MvcContrib.FluentHtml和Spark 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>



 
上面对动态生成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 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也是htmlHelper的Extension Method.相比较起来,htmlHelper提供了基础的页面控件定义和构造,而FluentHTML表现的更为灵活.除了FluentHTML,著名的Spark View Engine也有类似的实现,大家可以关注一下.
 
 嗯哼,全文完.
 
P.S:最近要找工作了,有好机会联系我:mailto:ligaoren@gmail.com
 

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息