【问题标题】:What is the MVC version of this code?这段代码的 MVC 版本是什么?
【发布时间】:2010-11-01 00:28:37
【问题描述】:

我正在努力思考如何对我的代码进行企业化:采用一个简单的例程并将其拆分为 3 或 4 个类中的 5 或 6 个方法。

我很快想出了三个简单的代码示例我目前是如何编写的。有人可以将这些转换成 MVC/MVP 混淆版本吗?


示例 1姓氏是强制性的。如果未输入任何内容,则将文本框涂成红色。如果输入内容,则将其涂成绿色:

private void txtLastname_TextChanged(object sender, EventArgs e)
{
   //Lastname mandatory. 
   //Color pinkish if nothing entered. Greenish if entered.
   if (txtLastname.Text.Trim() == "")
   {
      //Lastname is required, color pinkish
      txtLastname.BackColor = ControlBad;
   }
   else
   {
      //Lastname entered, remove the coloring
      txtLastname.BackColor = ControlGood;
   }
}

示例 2名字 是可选的,但尝试 获取它。我们将为这个“try to get”字段添加蓝色调:

private void txtFirstname_TextChanged(object sender, EventArgs e)
{
    //Firstname can be blank.
    //Hint them that they should *try* to get it with a bluish color.
    //If they do enter stuff: it better be not all spaces.
    if (txtFirstname.Text == "")
    {
       //Nothing there, hint it blue
       txtFirstname.BackColor = ControlRequired;
    }
    else if (txtFirstname.Text.Trim() == "")
    {
       //They entered spaces - bad user!
       txtFirstname.BackColor = ControlBad;
    }
    else
    {
       //Entered stuff, remove coloring
       txtFirstname.BackColor = SystemColors.Window;
    }
}

示例 3age 完全是可选的。如果输入了年龄,最好是有效的:

private void txtAge_TextChanged(object sender, EventArgs e)
{
   //Age is optional, but if entered it better be valid
   int nAge = 0;
   if (Int32.TryParse(txtAge.Text, out nAge))
   {
      //Valid integer entered
      if (nAge < 0)
      {
         //Negative age? i don't think so
         txtAge.BackColor = ControlBad;
      }
      else
      {
         //Valid age entered, remove coloring
         txtAge.BackColor = SystemColors.Window;
      }
   }
   else
   {
      //Whatever is in there: it's *not* a valid integer,
      if (txtAge.Text == "")
      {
         //Blank is okay
         txtAge.BackColor = SystemColors.Window;
      }
      else
      {
         //Not a valid age, bad user
         txtAge.BackColor = ControlBad;
      }
   }
}

每次我看到 MVC 代码时,它看起来几乎就像将代码随机拆分为不同的方法、类和文件。我无法确定他们疯狂的原因或模式。如果不了解他们为什么这是某种方式,这是没有意义的。并且使用 modelviewcontrollerpresenter 等词,就像我应该知道这意味着什么,没有帮助。

模型就是你的数据。

视图在屏幕上显示数据。

控制器用于执行 用户操作

橙子尝起来是橙色的。


这是我为了使代码更难理解而尝试拆分的尝试。这是靠近 MVC 的地方吗?

private void txtFirstname_TextChanged(object sender, EventArgs e)
{
   FirstnameTextChangedHandler(sender, e);
}

private void FirstnameTextChangedHandler(sender, e)
{
   string firstname = GetFirstname();

   Color firstnameTextBoxColor = GetFirstnameTextBoxColor(firstname);

   SetFirstNameTextBoxColor(firstnameTextBoxColor);
}

private string GetFirstname()
{
   return txtFirstname.Text;
}

private Color GetFirstnameTextBoxColor(string firstname)
{
    //Firstname can be blank.
    //Hint them that they should *try* to get it with a bluish color.
    //If they do enter stuff: it better be not all spaces.
    if (firstname == "")
    {
       //Nothing there, hint it blue
       return GetControlRequiredColor();
    }
    else if (firstname.Trim() == "")
    {
       //They entered spaces - bad user!
       return GetControlBadColor();
    }
    else
    {
       //Entered stuff, remove coloring
       return GetControlDefaultColor();
    }
}

private Color GetControlRequiredColor()
{
   return ControlRequired;
}

private Color GetControlBadColor()
{
   return ControlBad;
}

private Color GetControlGoodColor()
{
   return ControlGood;
}
//am i doin it rite

我已经混淆了代码,但它仍然是完全一样的。我认为 MVC 混淆的下一步是将代码隐藏在 3 或 4 个不同的文件中。

这是我不明白的下一步。将哪些功能移到其他哪些类中的逻辑分离是什么?有人可以将我上面的 3 个简单示例翻译成成熟的 MVC 混淆吗?


编辑:不是 ASP/ASP.NET/Online。假装它在台式机、手持设备、表面、信息亭上。并假装它与语言无关。

【问题讨论】:

  • 你在撒尿吗?
  • 我不确定这意味着什么。是英国俚语吗?
  • "如何企业化我的代码";分离关注点并在适当的地方使用模式称为“好”代码,而不是企业代码。

标签: model-view-controller language-agnostic design-patterns


【解决方案1】:

MVC/MVP 模式的目的不是混淆,而是separation of concerns。混淆是(conceal the) intended meaning in communication, making communication confusing, intentionally ambiguous, and more difficult to interpret:ref。模式的使用是为了使代码更简洁,易于理解。我建议您首先阅读MVCMVP 上的维基百科条目。

这两种模式都是结构化代码的方式,以便将应用程序分解为执行特定目的的元素,这些元素具有明确定义的交互边界。与在应用程序的各个类中专门解决业务关注点、输入/输出处理和表示的代码不同,这些关注点在各种架构组件中被分离和隔离。这些架构元素通过交互边界(接口)相互隔离,使它们更加独立,更容易修改而不影响整个应用程序。

【讨论】:

  • 我已经阅读了维基百科条目。购买了 GoF 书,一本关于 C# 设计模式的书,阅读了 smalltalk 中关于 MVC 的原始论文,并阅读了大量的网络文章和博客。
  • 如果你已经完成了所有这些,那么你需要做的就是把一些东西放在一起,做一些编码。老实说,这听起来你想多了。
  • @Robery Harvey:每次开始时,我都会在第 1 步中遇到困难:制作模型、视图或控制器。我需要实际的例子。
【解决方案2】:

在为 Windows 窗体实现 MVC 时,我的主要想法是我希望对我的模型和控制器进行单元测试。为了实现这一点,我的控制器不应该知道任何关于使用它的视图的信息,因此应该在 UI 级别处理的任何通知都作为事件实现。在您的示例中,我的控制器看起来像这样:

class Controller
{
    // This is the model we are operating on
    private Model model_;

    public enum Status
    {
        Normal,
        Required,
        Good,
        Bad
    }

    public delegate void FirstNameStatusChangedDelegate(Status newStatus);
    public event FirstNameStatusChangedDelegate FirstNameStatusChangedEvent;

    public string FirstName
    {
        get { return model_.FirstName; }
        set
        {
            if (value == "")
                RaiseFirstNameStatusChanged(Status.Required);
            else if ( value.Trim() == "" )
                RaiseFirstNameStatusChanged(Status.Bad);
            else
            {
                model_.FirstName = value;
                RaiseFirstNameStatusChanged(Status.Normal);
            }
        }
    }

    private void RaiseFirstNameStatusChanged(Status newStatus)
    {
        if ( FirstNameStatusChangedEvent != null )
            FirstNameStatusChangedEvent(newStatus);
    }
}

视图将为FirstNameStatusChanged 事件提供处理程序:

class View : Form
{
    private Controller controller_;
    private static readonly Dictionary<Controller.Status, Color> statusColors_ = new Dictionary<Controller.Status, Color>
    {
        {Controller.Status.Normal, SystemColors.Window},
        {Controller.Status.Required, ControlRequired},
        {Controller.Status.Good, ControlGood},
        {Controller.Status.Bad, ControlRed}
    };

    public View(Controller controller)
    {
        InitializeComponent();
        controller_ = controller;

        contoller_.FirstNameStatusChangedEvent += OnFirstNameStatusChanged;
    }

    private void txtFirstname_TextChanged(object sender, EventArgs e)
    { controller_.FirstName = txtFirstName.Text; }

    private void OnFirstNameStatusChanged(Controller.Status newStatus)
    { txtFirstName.BackColor = statusColors_[newStatus]; }
}

【讨论】:

  • 你回答了我的要求,但是这让我眼前一亮。
  • 这种方式与常见的“全部扔进表单”有很大的不同,但是我发现按照MVC实现的代码可读性更强,也更容易理解,更不用说测试和验证了。
【解决方案3】:

您在代码中所做的大部分事情都属于 Controller 类,因为它描述了逻辑。您的 View 应该只描述 UI 并提供对 UI 组件的轻松访问。 Model 类应该描述你的数据模型。

这个想法很简单:Controller 做所有事情,但它必须知道 ViewModel。例如,当 View 被初始化时,Controller 设置了所有的逻辑(有点像你已经在做的)。由于 Model 已分配给 Controller - 它会将值设置到适当的 UI 控件中,并执行相同的操作来检索数据并返回 Model

因此,基本上您将数据模型类提供给控制器,它会进行编辑并再次将您的数据作为模型类返回。

【讨论】:

  • “控制器”或“演示者”从文本框中引发更改事件与 WinForms 这样做有什么区别? WinForms 正在处理演示文稿,他们只是向我发送事件,然后我可以使用这些事件来驱动视图。 Form不是presenter吗,我是controller?
【解决方案4】:

如果可能的话,在经典的 ASP.NET 中遵循 MVC 会非常困难,所以我将基于 MVP 进行回复。

在您的第一个示例中,您正在尝试进行验证。验证姓氏是 Presenter 的责任。将字段显示为红色是 View 的责任。所以,你的视图类应该是这样的:

private void Page_Load()
{
    this._presenter = new Presenter();
}

private void txtLastname_TextChanged(object sender, EventArgs e)
{
    txtLastName.BackColor = presenter.IsLastnameValid(txtLastName.Text) ?
        ControlGood : ControlBad;
}

您的演示者课程将是这样的:

public Presenter()
{
    public bool IsLastNameValid(string lastname)
    {
        return string.IsNullOrEmpty(lastname);
    }
}

姓是你的模特。

请注意,我准备这些课程只是为了展示您将如何形成 MVP 结构。在现实世界中,有很多更好的方法来进行验证。通常您会在您的业务中使用这种方法而不是验证。

【讨论】:

  • 您能否详细说明一些更好的验证方法可能是什么?另外,当您说“这种方法”时,您是指我的方法,您的方法还是其他“更好”的方法?最后,当您说“这种”方法比验证更适合您的业务时,您的意思是什么?是不是验证不好?验证不好吗?
【解决方案5】:

伊恩,

如果您希望控件立即验证,您需要使用 javascript 或 jQuery。这也适用于经典的 ASP.NET。由于您使用的是 Code Behind 方法,我假设您的验证等待回发。

以下示例来自 NerdDinner 项目。 NerdDinner 是一个作为 ASP.NET MVC 架构示例的开源项目。作者慷慨地提供了一个教程,可在http://nerddinnerbook.s3.amazonaws.com/Intro.htm

在ASP.NET MVC中提交表单时,作为FormCollection对象进入对应的控制器:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(int id, FormCollection formValues) {
    Dinner dinner = dinnerRepository.GetDinner(id);

    try
    {
        UpdateModel(dinner);
        dinnerRepository.Save();
    }
    catch
    {
        ModelState.AddModelErrors(dinner.GetRuleViolations())
    }
    return RedirectToAction("Details", new { id = dinner.DinnerID });
}

UpdateModel 采用表单值并尝试将它们填充到晚餐对象中。晚餐对象如下所示:

public partial class Dinner {
    public bool IsValid {
        get { return (GetRuleViolations().Count() == 0); }
    }
        public IEnumerable<RuleViolation> GetRuleViolations() {
        yield break;
    }
     public IEnumerable<RuleViolation> GetRuleViolations() {

        if (String.IsNullOrEmpty(Title))
            yield return new RuleViolation("Title is required", "Title");

        if (String.IsNullOrEmpty(Description))
            yield return new RuleViolation("Description is required", "Description");

        if (String.IsNullOrEmpty(HostedBy))
            yield return new RuleViolation("HostedBy is required", "HostedBy");

        if (String.IsNullOrEmpty(Address))
            yield return new RuleViolation("Address is required", "Address");

        if (String.IsNullOrEmpty(Country))
            yield return new RuleViolation("Country is required", "Address");

        if (String.IsNullOrEmpty(ContactPhone))
            yield return new RuleViolation("Phone# is required", "ContactPhone");

        if (!PhoneValidator.IsValidNumber(ContactPhone, Country))
            yield return new RuleViolation("Phone# does not match country", "ContactPhone");

        yield break;
    }
   partial void OnValidate(ChangeAction action) {
        if (!IsValid)
            throw new ApplicationException("Rule violations prevent saving");
    }
}

注意 IsValid 方法和 RuleViolations 枚举器。如果一切设置正确,您所要做的就是在此处定义您的验证,而 ASP.NET MVC 将为您处理其余的工作。

最终验证结果如下:

我鼓励您通过http://nerddinner.codeplex.com/ 获取 NerdDinner 应用程序和教程

【讨论】:

  • 我没有使用网络。假装它是与语言无关的计算机代码。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-28
  • 1970-01-01
相关资源
最近更新 更多