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,对付小型应用还凑合,将来要是大规模应用,还是得专业点才行。

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

  • 相关阅读:
    mexHttpBinding协议 【发布元数据终结点】
    Revit API创建一个拷贝房间内对象布局命令
    Revit API判断是不是柱族模板
    Revit API封装一个通用函数“过名称找元素”
    Revit手工创建族
    Revit API根据参数类型取得参数的值
    Revit Family API 创建参考平面
    Revit Family API 添加参数与尺寸标注
    osgearth earth文件规范-符号参考
    Revit API创建几何实体Solid并找到与之相交的元素
  • 原文地址:https://www.cnblogs.com/XmNotes/p/2254090.html
Copyright © 2011-2022 走看看