一接觸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,如果有什么不對的地方請各位指正,很感謝。從上一篇文章中,各位的指正讓我明白了很多東西,拋磚引玉了。
如果感覺此文對您有幫助,請頂一下了^_^