zoukankan      html  css  js  c++  java
  • 视图分离技术的认识和应用

    在过去Win32编程时代,我们看到的程序界面都是由静态编程语言,从一个按钮的尺寸到布局,一行行地绘制出来。比如现在我们创建一个.Net WinForm窗体,打开其对应的designer.cs文件,就会看到长篇累牍窗体绘制代码。

    打从VB和Delphi出现后,尽管通过界面设计器自动生成代码成为主流,然而随之互联网时代的发展,我们需要丰富多样的软件界面,更灵活地应对需求变动。比如一个软件往往有多种界面,包括C/S和B/S,C/S中包括Windows/Linux/IPhone,B/S包括IE/Firefox/Chrome。无论是先设计业务,还是先构勒界面,最好能独立、清晰、明确的进行。更不想影响业务逻辑。将业务逻辑和用户界面分离,实现低耦合,变得势在必行。

    对于三层架构的概念,相信就是初学者也耳熟能详了。对于数据访问层(DAL)和业务逻辑层(Business Logic Layer)之间的分离,比较容易理解,也不难实现。但对于业务逻辑层和界面层(UI)的分离,就不是那么泾渭分明,至少我在很长时间里都比较模糊。

    这一方面由于理念认识的距离,一方面由于以前的框架没给予有力支持。

    我主要从事的是.Net Web开发,闲暇里也做一些C/S程序练练手。在初学编程时,我更倾向C/S开发,因WinForm中用的是统一的强类型语言,而B/S或Asp.Net开发中,众所周知,还需要掌握HTML/CSS/JavaScript。但随之这些技能障碍被逐渐克服,以及Asp.Net开发经验的积累,我的认识发生颠覆性变化,感觉开发Asp.Net要比WinForm更清晰,更容易把握,至少不会为改一个字体或效果重新发布整个程序,除非你把这些植写到配置文件中。

    可如果你运用了像Asp.Net那样的视图分离技术,相当于,一切UI皆可配置,而且还可以内容与样式分离,只修改样式时可以避免修改页面导致的自动编译,只要更新CSS即可。

    视图分离另一个好处是直观,无须费神地分析一大段代码,只要瞄一眼视图模板的结构,一切就了然于心。无论是设计、开发或维护,都可以大大提高效率。正如下面语句,

    var content = string.Format(@"Dear {0},
    
                            Thank you for {1}.
    
                            {2}", receiver, thing, sender);
    

    要优于:

    var content = "Dear " + receiver + ",\r\nThank you for " + thing + ".\r\n" + sender;
    

    在过去,应该是在2.0以前,Asp.Net只有页面,还有很多人固执地以C/S开发的思想做B/S,在页面后端类中生成控件,甚至直接输出html文本。 而随着MVC、Razor等技术的推广,页面的概念逐渐被模板取代。WPF和Silverlight技术,就是Asp.Net的视图分离思想在C/S开发上的移植。时至今日,视图分离技术取得了压倒性的主流地位。

    还可以再兴几个视图分离技术的例子。

    Visual Studio集成的ADO.Net Entity设计器(请参考园子的AEF专题):

     image

    Visual Studio集成的工作流设计器(请参考麒麟同学的WF专题):

    image

    RDLC格式报表(请参考MSDN演练:创建 ReportViewer 报表):

    image

    这些视图都是基于扩展的XML定义。Html自然还是最常见XML扩展。随着浏览器差异的缩小,当你掌握Html/CSS后,开发B/S系统体验要更胜于C/S。

    视图分离已不只是一门技术,而是软件设计中的重要思想,它分离了界面与业务逻辑的耦合,用途非常广泛。

    最近开发一些系统服务,服务会定时做一些数据存取和分析工作,然后要将运行结果用邮件发送出去。邮件内容,要反映服务执行过程的细节。开始时做完是先定一个StringBuilder,每执行完一步,就写入执行结果,这样对简单的业务流程没问题。但实际上由于有些执行流程很长,分支很多,生成邮件内容的代码充斥各个角落,写起来很烦,维护时的可读性也很差。有时邮件格式要求稍为复杂一点,比如包含表格,开发负担会更重。 而如果最终呈现的邮件内容在在一些内在逻辑(比如对数据分析后,如果满足一定条件就生成一个报表),那就非常繁琐,会弄得你抓狂。

    常言道,穷则思变,我把目光瞄向了视图分离。下面,将介绍邮件生成将如何运用视图分离的技术。

    第一种方式很简单,也很好用,就是做一个邮件文本模板,将动态的要运行时生成的内容,用特殊符号标记。其实和String.Format方法的原理一样,不过待替换的字段最好不用{0}、{1}这样编号,而是用名称比如{地址}、{电话}等,可读性更好。在具体应用中,只要将动态内容以键值对形式添加到Dictionary中,而无须关心顺序问题。

    这种方式用得很多,我看到普遍的问题是,大家都忽视了对替换内容与模板之间匹配的验证。Dictionary中的各个键,应该与模板中被替换的字段一致,不能多不能少,否则应该抛出异常,这可以有效提高系统可维护性。

    MailBuilder类是用于创建内容基于模板的邮件,下面是它的属性:

    public class MailBuilder
    {
        /// <summary>
        /// The fields to be replaced.
        /// </summary>
        protected Dictionary<string, string> fields = new Dictionary<string, string>();
        /// <summary>
        /// The resources embedded in the mail.
        /// </summary>
        protected List<LinkedResource> resources = new List<LinkedResource>();
        /// <summary>
        /// The attachments to be sent out with the mail.
        /// </summary>
        protected List<Attachment> attachments = new List<Attachment>();
        protected SmtpClient client = new SmtpClient();
    
        #region Properties
    
        /// <summary>
        /// SMTP server location.
        /// </summary>
        public string Host { get; set; }
    
        /// <summary>
        /// Smtp credentials.
        /// </summary>
        public ICredentialsByHost Credentials { get; set; }
    
        /// <summary>
        /// Smtp port.
        /// </summary>
        public int Port { get; set; }
    
        /// <summary>
        /// Content of mail template.
        /// </summary>
        public string Template { get; set; }
    
        string tempFile;
        /// <summary>
        /// File path of mail template.
        /// </summary>
        public string TemplateFile
        {
            get { return tempFile; }
            set
            {
                tempFile = value;
                this.Template = File.ReadAllText(tempFile);
            }
        }
        /// <summary>
        /// Subject of mail
        /// </summary>
        public string Subject { get; set; }
    
        /// <summary>
        /// FROM address
        /// </summary>
        public string From { get; set; }
    
        /// <summary>
        /// TO addresses
        /// </summary>
        public string[] To { get; set; }
    
        /// <summary>
        /// CC addresses
        /// </summary>
        public string[] CC { get; set; }
    
        /// <summary>
        /// BCC addresses
        /// </summary>
        public string[] Bcc { get; set; }
    
        /// <summary>
        /// Priority
        /// </summary>
        public MailPriority Priority { get; set; }
    
        /// <summary>
        /// Indicates result message for sending email.
        /// </summary>
        public string Result { get; private set; }
    
        /// <summary>
        /// Get or sets the reply to address of the mail message.
        /// </summary>
        public string ReplyTo { get; set; }
    
        /// <summary>
        /// Indicate whether need to return receipt. If needed, the ReplyTo value should be set first.
        /// </summary>
        public bool ReturnReceipt { get; set; }
        #endregion
    
        …… ……
    }
    

    这个类除有添加替换字符串AddField方法外,还支持添加DataTable和实体集合,将被转换为相应的Html表格内容。

    /// <summary>
    /// Add a key-value pair
    /// </summary>
    /// <param name="key">key's name</param>
    /// <param name="value">value</param>
    public void AddTable(string key, DataTable dt)
    
    /// <summary>
    /// Add entities to render them as a html table.
    /// </summary>
    /// <typeparam name="T">Entity type</typeparam>
    /// <param name="key"></param>
    /// <param name="entities">Entities to be rendered</param>
    /// <param name="columns">The corresponding html table's columns.</param>
    /// <param name="rowValues">Function to get values of a row from an entity.</param>
    public void AddEntities<T>(string key, IEnumerable<T> entities, string[] columns, Func<T, object[]> rowValues)
    

    经过一段时间的使用,感觉还是相当顺手的,因此觉得拿出晒晒,希望大家不吝赐教。

    纯文本邮件模板虽然简单方便,但对于实现逻辑复杂一点的邮件,就有点力不从心。比如邮件要示,未尾的提示,要根据前面的内容决定是否显示,我们总不能为一点差别用两个不同模板吧。于是,这种情况下我们面临新的课题—动态模板技术。其实这也不难,因为我们早就有合适的方案,就是老而弥坚XML + XSLT技术。

    这种方式,模板换成了XSLT文件。XSLT不只可以转换XML数据,还可以将动态内容以参数方式传入,.Net甚至支持XSLT中嵌入Javascript和C#。不过不推荐嵌入C#调用外部函数,那会破坏视图的本质,一般前两种方式足够了。我倾向于XML传值,有层次感,贴一下转换的XSLT的代码:

    /// <summary>
    /// Transform XSLT file to HTML.
    /// </summary>
    /// <param name="template">Template's content.</param>
    /// <param name="dicField">The fields to be filled in corresponding place of the template</param>
    public static string TransformXsl(string template, Dictionary<string, string> dicField)
    {
        var elements = from field in dicField
                       select new XElement(field.Key, field.Value.Contains("<table") ? (object)XElement.Parse(field.Value) : (object)field.Value);
    
        var xe = new XElement("Root", elements);
    
        using (var writer = new StringWriter())
        using (var reader = XmlReader.Create(new StringReader(template)))
        {
            var xslTransform = new System.Xml.Xsl.XslCompiledTransform();
            xslTransform.Load(reader);
            xslTransform.Transform(xe.CreateReader(), null, writer);
            return writer.ToString();
        }
    }
    

    介绍XSLT转换的文章很多,大家可以参考一下,随便找两篇:如何使用JavaScript 结合XSLT转换XML文档, 博客园备份档案浏览的小工具

    这种方式的主要缺点是要求有一定XSLT语言基础,临阵磨枪学了点XSLT,对付小型应用还凑合,将来要是大规模应用,还是得专业点才行。

    我无耻地希望,门槛可以再低点……我们学的语言已经够多了。

  • 相关阅读:
    zoj 2316 Matrix Multiplication 解题报告
    BestCoder7 1001 Little Pony and Permutation(hdu 4985) 解题报告
    codeforces 463C. Gargari and Bishops 解题报告
    codeforces 463B Caisa and Pylons 解题报告
    codeforces 463A Caisa and Sugar 解题报告
    CSS3新的字体尺寸单位rem
    CSS中文字体对照表
    引用外部CSS的link和import方式的分析与比较
    CSS样式表引用方式
    10个CSS简写/优化技巧
  • 原文地址:https://www.cnblogs.com/XmNotes/p/2254090.html
Copyright © 2011-2022 走看看