FluentAPI --- 用C#寫的JS代碼生成器


      一接觸FluentAPI,就被它的流暢編碼和良好可讀性吸引,園子里有很多介紹FluentAPI的文章,我也忍不住把自己最近寫的一個小類庫放在這里來補充一下流暢API的應用場景。

      我寫這個小型的類庫來源於我昨天的發表的文章<asp.net webForm也可以這樣用Ajax -- My Ajax Framework>一文后面網友 “、Dark”對我的一個建議,是否可以用框架動態生成JS文件,這樣直接類似AjaxPro的方式,仔細考慮感覺是很好的建議,於是就寫了一下,結果發現向Response中寫入一些JS數據流是多么亂,又很容易出錯。

我起初的代碼是這樣的:(為了生成一段Js代碼我這樣做):

Response.Output.WriteLine("<script>");
Response.Output.WriteLine("var PowerAjax = function () { };");
Response.Output.WriteLine("PowerAjax.__Private = function () { };");
Response.Output.WriteLine("PowerAjax.AsyncAjax = function (methodName, paramArray, success, failure) { PowerAjax.__Private.Ajax(methodName, paramArray, success, failure, true);}");
Response.Output.WriteLine("PowerAjax.SyncAjax = function (methodName, paramArray, success, failure) { PowerAjax.__Private.Ajax(methodName, paramArray, success, failure, false); };");
Response.Output.WriteLine("PowerAjax.__Private.Ajax = function(methodName, paramArray, success, failure,isAsync) {");
Response.Output.WriteLine(" var data = {};");
Response.Output.WriteLine(" switch (paramArray.length) {");
Response.Output.WriteLine(" case 0:data = { 'isAjaxRequest': true, 'MethodName': methodName };break;");
Response.Output.WriteLine(" case 1:data = { 'isAjaxRequest': true, 'MethodName': methodName, 'param0': paramArray[0] }; break;");
Response.Output.WriteLine(" }");
Response.Output.WriteLine(" var url = document.location.href;");
Response.Output.WriteLine("};");
Response.Write("</script>");

感覺這樣太不靠譜了,經常性的就會寫錯了,尤其是對於 “{" 和 ”}“這兩個符號的對應,實在糾結,一個不留神不是忘記{就是忘記},結果會導致Javascript根本無法打開。有些語句還經常忘記加一個“;”,那么就因為語句的這一個小“;”,代碼就無法執行。

於是我就做了一個簡單的改進,寫了一個簡單的小類庫來生成:

我的想法是:  把所有js代碼分為兩類 ”一般語句“ 和 ”由“{.......}”組成的兩種語句, 大括號里可以繼續寫一般語句和{.....} 。就好比函數里可以繼續套N個函數和N條語句,於是就有了以下API。

先看看改善之后的代碼是什么樣的:

ScriptFactory.CreateScript(
                    new ScriptSentence("var PowerAjax = function () { }"),
                    new ScriptSentence("PowerAjax.__Private = function () { }"),
                    new ScriptContainer("PowerAjax.AsyncAjax = function (methodName, paramArray, success, failure)",
                        new ScriptSentence("PowerAjax.__Private.Ajax(methodName, paramArray, success, failure, true)")),
                    new ScriptContainer("PowerAjax.SyncAjax = function (methodName, paramArray, success, failure)",
                        new ScriptSentence("PowerAjax.__Private.Ajax(methodName, paramArray, success, failure, false)")),
                    new ScriptContainer("PowerAjax.__Private.Ajax = function(methodName, paramArray, success, failure,isAsync)",
                        new ScriptSentence("var data = {}"),
                        new ScriptContainer("switch (paramArray.length)",
                            new ScriptSentence("case 0:data = { 'isAjaxRequest': true, 'MethodName': methodName };break"),
                            new ScriptSentence("case 1:data = { 'isAjaxRequest': true, 'MethodName': methodName, 'param0': paramArray[0] }; break"),new ScriptSentence("var url = document.location.href"))));

     有感覺好看一些嗎?主要是現在所有的語句我都不需要管;,也不需要加個什么{和}的對應關系了。

     敏銳的朋友可能已經感覺到,這種API正是LinqToXML生成一段XML一樣類似的代碼,如:

 XDocument doc = new XDocument(
                new XDeclaration("1.0", "utf-8", "yes"),
                new XElement("xxxConfig",
                    new XElement("a", s_LDAPEnabled),
                    new XElement("b", s_ServerPath),
                    new XElement("c", s_BaseDN),
                    new XElement("d", s_UserCN)));

這種結構的優勢:不容易出錯,寫起來很舒服,排版很嚴謹,很容易就容易從代碼中看出整個文檔的結構特點。同樣我的輸出的JS代碼也能很清晰的看出這個在C#的中生成的JS文件是什么樣子的?

演示就到這里,把整個API發出來,大家互相學習:

********ScriptFactory類********

目的:方便前台調用。因為本身加載起來的是一個樹形的結構,所以需要使用遞歸來解析數據,生成JS代碼。

    public sealed class ScriptFactory
    {
        private static string CreateScriptTrue(params IScriptBase[] scriptBases)
        {
            StringBuilder sb = new StringBuilder();
            foreach (var scriptBase in scriptBases)
            {
                if (scriptBase is ScriptSentence)
                {
                    sb.Append(scriptBase.ScriptContent + ";");
                }
                if (scriptBase is ScriptContainer)
                {
                    sb.Append(scriptBase.ScriptContent);
                    sb.Append("{");
                    sb.Append(CreateScriptTrue(((ScriptContainer)scriptBase).Children.ToArray())); // 遞歸調用生成JS樹形結構
                    sb.Append("};");
                }
            }
            return sb.ToString();
        }
        public static string CreateScript(params IScriptBase[] scriptBases)
        {
            return string.Format("<script>{0}</script>",ScriptFactory.CreateScriptTrue(scriptBases));
        }
    }

********IScriptBase接口********

目的:提供內容規范

    public interface IScriptBase
    {
        string ScriptContent { get; set; }
    }

********ScriptContainer : IScriptBase********

目的:承載包含{}結構的語句,{}中還可能包含語句和各種,所以會有一個m_ScriptBases來保存所有結構

    public sealed class ScriptContainer : IScriptBase
    {
        private List<IScriptBase> m_ScriptBases = new List<IScriptBase>();

        public IEnumerable<IScriptBase> Children
        {
            get { return this.m_ScriptBases; }
        }

        public string ScriptContent { get; set; }

        public ScriptContainer(string content, params IScriptBase[] scriptBeses)
        {
            this.ScriptContent = content;
            this.m_ScriptBse = scriptBeses.ToList();
        }
    }

 

********Scriptesentence : IScriptBase********

目的:承載一般性質的語句,語句本身內部不具備數據結構,僅僅是內容而已。

    public sealed class ScriptSentence : IScriptBase
    {
        public string ScriptContent { get; set; }
        public ScriptSentence(string content)
        {
            this.ScriptContent = content;
        }
    }

OK,以上就是全部代碼,是不是很簡單呢?而代碼有極為簡單呢?

此API要點:接口繼承,params用法,樹形結構,遞歸。請注意構造函數中參數的用法,這個流暢API的秘密就在這里(大牛可以繞過了,呵呵)

 

以上是試水的東西,意在演示這種類LinqToXML生成方式的流暢API,如果有什么不對的地方請各位指正,很感謝。從上一篇文章中,各位的指正讓我明白了很多東西,拋磚引玉了。

如果感覺此文對您有幫助,請頂一下了^_^


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM