65.9K
CodeProject 正在变化。 阅读更多。
Home

分层编码 - 第一部分

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.29/5 (22投票s)

2003年9月15日

5分钟阅读

viewsIcon

70812

downloadIcon

2745

自动填充 UI 窗体。

引言

本文是系列文章的第一篇,演示了一种使用 Automator 类自动填充 Windows 窗体的方法。本系列文章将按以下顺序介绍。

  • 分层编码 - 第一部分 (自动填充 UI 表单)
  • 分层编码 - 第二部分 (在 Windows 和 Web 窗体中重用代码)

提供的可下载源代码和可执行代码包含示例应用程序。我使用 Visual Studio .NET 2003 和 Framework SDK 1.1 来创建此示例。

模式、级别、层和分层

企业解决方案不仅应高可用且可扩展,还应灵活且可重用,以适应快速变化的业务需求。设计可重用软件并非易事。编写不那么可重用的软件比设计和编写由可重用组件组成的软件要容易得多。前者可能适合原型设计,但从长远来看却不尽如人意。从重用角度设计的软件非常强大,因为它可以在代码即插即用时节省大量宝贵的时间(以及成本),从而减少开发和调试代码的工作量。

在软件组件可重用性方面,设计模式占据中心地位。模式描述了在给定上下文中出现的重复性问题,并基于一组指导原则推荐解决方案。模式使软件灵活、优雅,从而可重用。模式存在于许多不同的抽象级别。每个级别都可以被认为是一组设计模式的集合。

每个级别可以包含多个层,例如表示层、服务层、业务层、数据访问层等。层由大致相同抽象级别的元素组成。必须识别每个层中的依赖关系,以制定分层策略。在没有良好的依赖管理策略的情况下构建应用程序会导致组件脆弱且不稳定,难以且昂贵地维护、扩展和替换。如果可以将级别想象成垂直布局,那么层就是水平布局。这将导致一个由定义和使用经过验证的软件设计模式的单元组成的矩阵。从基础设施的角度来看,每个层都有一个相应的分层,在多层体系结构模型中可以将其分类如下...

  • 客户端分层 - 访问应用程序的所有设备或系统客户端,例如 Windows 应用程序、Web 浏览器等。
  • 表示层 - 提供用户界面 (UI) 来执行业务逻辑的组件。
  • 服务层 - 应用程序提供的与业务层通信的服务抽象。
  • 业务层 - 执行业务逻辑和事务的组件。
  • 集成层 - 与外部资源(如数据存储)通信的组件。
  • 资源层 - 包含由集成层访问的业务数据。

最佳实践

分离不同的代码是成功实现结构良好的应用程序的关键。让代码更接近它需要交互的分层。创建用户界面并执行用户交互的组件应纳入表示层,而执行数据库插入、更新和查询的组件应属于集成层。分层之间的紧密耦合会导致代码难以维护和重用。例如,考虑一个 Windows 应用程序,它提供了一个用于在系统中注册用户的用户界面。如果业务层中的代码要使用表示层中的组件,那么当用户界面从 Windows 窗体更改为 Web 窗体时,可能会出现问题。

using System.Windows.Forms; // Windows Forms

// Windows Application
namespace Win
{
    // Windows Form
    public class AutoForm : System.Windows.Forms.Form
    {
        // Members

        // Store the state of the form
        private void SaveButton_Click(object sender, System.EventArgs e)
        {
            // Register a new user
            AccountManager.Register(this);
        }
    }

    // A class to manage user accounts
    public sealed class AccountManager
    {
      // Register a new user
      public static void Register(AutoForm form) { // Process Registration }
    }
}

上面的 AccountManager 代码在没有对 System.Windows.Forms.dll 的引用的情况下将无法编译。通过将可重用代码提取到类库中,并创建一个新的 State 类,该类使用属性(如下面的代码片段所示)来保存用户界面输入的所有信息,可以解决此问题。

// Windows Application
using System.Windows.Forms; // Windows Forms

using Lib; // For the AccountManager and Registration class

// Presentation Tier (Windows)
namespace Win
{
    // Windows Form
    public class AutoForm : System.Windows.Forms.Form
    {
        // The registration information
        private State state = new State();

        // Members

        // Store the state of the form
        private void SaveButton_Click(object sender, System.EventArgs e)
        {
            // Populate state with values from the UI controls
            AccountManager.Register(this.state);
        }
    }
}

// ASP.NET Web Application
using System.Web.UI; // Web Forms

using Lib; // For the AccountManager and Registration class

// Presentation Tier (Web)
namespace Web
{
    // Web Form
    public class AutoForm : System.Web.UI.Page
    {
        // The registration information
        private State state = new State();

        // Members

        // Store the state of the form
        private void SaveButton_Click(object sender, System.EventArgs e)
        {
            // Populate state with values from the UI
            AccountManager.Register(this.state);
        }
    }
}

// Class Library
using System;

// Business/Domain Tier
namespace Lib
{
    // A class to hold registration information
    public class State
    {
        // Properties corresponding to the controls on the UI
    }

    // A class to manage user accounts
    public sealed class AccountManager
    {
        // Register a new user
        public static void Register(State state) { // Process Registration }
    }
}

通过提供一个额外的服务层,可以消除对 AccountManager 的依赖,正如我们在后续文章中所见。State 类中定义的每个属性都依赖于 UI 上使用的控件类型,即 TextBox 将表示为 string,而 CheckBox 应表示为 bool。但是,还有一个问题。AutoForm 类(Windows 应用程序ASP.NET Web 应用程序)的 SaveButton_Click() 方法需要将 State 的实例用 UI 中的值填充。在本文中,我们将仅限于Windows 应用程序的讨论。

引入 Automator

SaveButton_Click() 方法看起来会与下面显示的代码片段有些相似。

// Store the state of the form
private void SaveButton_Click(object sender, System.EventArgs e)
{
    // Store TextBox value
    this.state.Username = usernameTextBox.Text;

    // Store CheckBox value
    this.state.Subscribe = subscribeCheckBox.Checked;

    AccountManager.Register(state);
}

假设有一个 LoadButton_Click() 方法用于加载表单的最后存储状态;LoadButton_Click() 方法看起来会与下面显示的代码片段有些相似。

// Restore the state of the form
private void LoadButton_Click(object sender, System.EventArgs e)
{
    // Restore TextBox value
    usernameTextBox.Text = this.state.Username;

    // Restore CheckBox value
    subscribeCheckBox.Checked = this.state.Subscribe;
}

当使用包含大量控件的复杂表单时,这可能会非常麻烦且容易出错。我们需要一个实用类 "Automator",它可以自动存储和恢复表单的状态。下面显示了描述其用法的骨架代码片段。

namespace Win
{
    // Windows Form
    public class AutoForm : System.Windows.Forms.Form
    {
        // Members

        // The form automator
        private Automator automator;

        public AutoForm
        {
            automator = new Automator(this, this.state);
        }

        // Store the state of the form
        private void SaveButton_Click(object sender, System.EventArgs e)
        {
            // Store the state using the automator
            automator.Store();
            // Register the user
            AccountManager.Register(this.state);
        }

        // Restore the state of the form
        private void LoadButton_Click(object sender, System.EventArgs e)
        {
            // Restore the state using the automator
            automator.Restore();
        }
    }
}

太棒了!但这会如何实现呢?这是通过使用反射来实现的。State 上的属性名称与要保存的表单控件的名称(区分大小写)相匹配。Automator 类上的 Store() 方法将遍历表单中包含的所有控件,将每个控件的名称与 State 对象上的属性进行匹配。如果找到匹配项,则在应用类型转换(如果需要)后,将控件上的值设置到匹配的属性上。Automator 类上的 Restore() 方法的工作顺序完全相反,即用匹配属性的值填充控件。

控件名称 控件类型 属性名称 属性类型
用户名 System.Windows.Forms.TextBox 用户名 字符串
Subscribe (订阅) System.Windows.Forms.CheckBox Subscribe (订阅) bool
兴趣 System.Windows.Forms.ListBox 兴趣 string[]

结束语

本文演示了一种自动填充Windows 窗体的技术。我的下一篇文章将提取部分代码,使其更加灵活可重用。除了本文中描述的表示层业务层之外,我们还将引入一个新的服务层。后续文章将演示三种不同类型应用程序(即 Windows、Web 和 Web 服务)中的代码重用。

参考/链接

© . All rights reserved.