理解MVC模式

时间:2010-07-22 14:04:43

标签: c# java .net winforms model-view-controller

我在理解MVC模式时遇到了一些麻烦。我确实理解我们正试图将GUI与业务逻辑分离,尽管我在理解方法时遇到了问题。

据我所知,View是用户看到的内容。所以它通常是窗口/窗体。 Controller位于ViewModel之间。控制器将使数据在两个方向上“流动”。它还会在需要时保持状态(如果我有一个包含5个步骤的向导,Controller负责确保按正确顺序制作它们等等。 Model,是我的应用程序逻辑的核心所在。

这个观点是否正确?

为了尝试将其转化为更有意义的东西,我将尝试使用WinForms绘制一个简单的示例(请不要使用ASP.NET或WPF! - 对于java群体,从我的理解,Swing的工作原理以与WinForms类似的方式!),看看我是否正确,并且我会提出我在做这件事时总是会遇到的问题。


让我们假设我有一个只包含一个类的模型(只是为了让它变得更容易。我知道它会使示例看起来很愚蠢,但这样更容易):

class MyNumbers {
    private IList<int> listOfNumbers = new List<int> { 1, 3, 5, 7, 9 };

    public IList<int> GetNumbers() {
        return new ReadOnlyCollection<int>(listOfNumbers);
    }
}

现在是时候制作我的Controller

class Controller
{
    private MyNumbers myNumbers = new MyNumbers();

    public IList<int> GetNumbers() {
        return myNumbers.GetNumbers();
    }
}

View应该只有一个ListBox,其中包含MyNumbers中检索到的所有数字作为项目。

现在,第一个问题出现了:

Controller是否应负责创建MyNumbers?在这个简单的例子中,我认为它是可接受的(因为MyNumbers将完全相同,无论如何,并且没有相关的状态)。但是我们假设我想要用于所有不同的控制器,我的应用程序具有相同的MyNumbers实例。我必须传递给Controller(和所有其他需要它的人)我要使用的MyNumbers实例。谁将对此负责?在这个WinForms示例中,那会是View吗?或者那是创建View

的类

转过来问题:这3个部分的实例化顺序是什么? MVC的“所有者”要求创建它的代码是什么? Controller是否应同时创建ViewModelView是否应实例化ControllerController Model

第二个问题:

main方法应该是什么样子,假设我只希望我的应用程序使用Use Case这个Controller描绘?

第三

为什么在以下MVC图表中View有箭头指向ModelController不应始终是ViewModel之间的桥梁吗?

alt text


我还有一两个问题,但在我理解了第一个细节后,他们可能会更有意义。或者也许在我明白第一个问题之后,其他所有人都会分崩离析。

谢谢!

10 个答案:

答案 0 :(得分:26)

获得MVC处理的最简单方法是在一个强制执行它的框架中使用它,这就是说..

  • 模型与数据源(数据库或其他)交互,并允许您访问数据。
  • View与外界交互,它从某处接收输入并将数据交给Controller,它还会监听Controller以确保其显示正确的数据。
  • 控制器是所有魔法发生的地方; Controller操纵数据,推送事件,并处理两个方向的变化(进出View或进出模型)。

这个图非常有用(它比维基百科更有意义): MVC Diagram http://java.sun.com/developer/technicalArticles/javase/mvc/images/Figure4.gif

Source,以及关于MVC的精彩文章!

答案 1 :(得分:3)

至于我帖子中的批评,我想我会发一篇关于我如何用PHP创建MVC模式的帖子

在PHP中,我将框架分为几个部分,其中有些部分在MVC中是正常的。

<强>初选:

  • 控制器
  • 模型
  • 查看

<强> Secondariness   - ModelLayer

  • ViewLoader
  • ErrorLayer

在控制器中,我通常允许所有访问辅助层以及从主要访问视图和模型。

这是我构建它的方式

|---------|       |------------|       |------------|
| Browser | ----> | Controller | ----> |   Model    |
|---------|       |------------|       |------------|
     |                  |   |                |
     |                  |   |----------------|
     |                  |
     |            |------------|
     -------------|    View    |
                  |------------|

在我的图表中,我通常会绕过View <-> Model连接并执行Controller <-> Model,然后Controller <-> View的链接会分配数据。

在我的框架中,我倾向于创建一个对象存储系统,以便我可以轻松地获取对象等等。我的对象存储的一个例子是这样的

class Registry
{
   static $storage = array();

   public static function get($key)
   {
       return isset(self::storage[$key]) ? self::storage[$key] : null;
   }

   public static function set($key,$object)
   {
       self::"storage[$key] = $object;
   }
}

这是轮廓的更高级,所以当我第一次初始化对象时,我将它们存储为Registry::set("View",new View());,以便始终可以访问。

所以在我的控制器中,巫婆是基本控制器,我创建了几个魔术方法__get() __set(),这样任何扩展控制器的类我都可以轻松返回请求,例如:

abstract class Controller
{
   public function __get($key)
   {
       //check to make sure key is ok for item such as View,Library etc

       return Registry::get($key); //Object / Null
   }
}

用户控制器

class Controller_index extends Controller
{
    public function index()
    {
       $this->View->assign("key","value"); // Exucutes a method in the View class
    }
}

该模型也将被置于注册表中,但只允许从ModelLayer

调用
class Model_index extends ModelLayer_MySql
{
}

class Model_index extends ModelLayer_MySqli
{
}

或文件系统

class Model_file extends ModelLayer_FileSystem
{
}

这样每个类都可以特定于存储类型。

这不是传统类型的MVC模式,但它可以称为Adoptive MVC。

其他对象(例如View Loader)不应放入注册表中,因为不是专门针对用户的兴趣,而是由其他参与者使用,例如View

abstract class ViewLoader
{
   function __construct($file,$data) //send the file and data
   {
       //Include the file and set the data to a local variable
   }

   public function MakeUri()
   {
       return Registry::get('URITools')->CreateURIByArgs(func_get_args());
   }
}

由于模板文件包含在View加载器中而不是View类中,它将用户方法与系统方法分开,并允许在视图中使用方法来获得通用逻辑。

模板文件示例。

<html>
   <body>
      <?php $this->_include("another_tpl_file.php"); ?>
      <?php if(isset($this->session->admin)):?>

          <a href="<?php echo $this->MakeUri("user","admin","panel","id",$this->session->admin_uid) ?>"><?php echo $this->lang->admin->admin_link ?></a>

      <?php endif; ?>
   </body>
</html>

我希望我的例子可以帮助你理解这一点。

答案 2 :(得分:2)

回答第三个问题

当模型更改时,它会通知视图,然后视图使用其getter从模型中获取数据。

答案 3 :(得分:1)

“根据我的理解,视图,是用户看到的。所以它通常是窗口/窗体。控制器位于视图和模型之间。控制器将”处理“两个方向的数据。它将在需要时也保持状态(如果我有一个包含5个步骤的向导,那么控制器负责确保它们以正确的顺序制作,等等。)模型,是我的应用程序逻辑的核心所在。“

这几乎是正确的。控制器不会保留数据。它调用一个持久保存数据的服务。原因是,持久化数据绝不仅仅是对保存的调用。您可能希望对数据进行验证检查,以确保根据您的业务需求保持理智。您可能需要进行一些身份验证以确保用户可以保存数据。如果您在服务中执行此操作,那么您可以反复使用一组很好的功能,例如Web应用程序和Web服务。如果您在控制器中执行此操作,例如对于Web应用程序,则在您编写Web服务时,您将不得不重构和/或复制代码。

回应你的评论“我不确定我是否完全明白你的意思。控制器是否检查了UI输入,或是模型是否能够完成?”

您的控制器应该只控制执行哪些业务功能路径。多数民众赞成。控制器应该是编写代码中最容易的部分。您可以对gui进行一些验证(例如视图,例如确保电子邮件地址格式正确,文本输入不超过最大值),但业务层也应该验证输入 - 出于我之前提到的原因,当你开始站起来更多的端点,你不必重构。

答案 4 :(得分:1)

这是来自Java,但希望它会有所帮助。

主要:

public static void main(String[] args) 
{
       MyNumbers myNums = new MyNumbers();  // Create your Model
       // Create controller, send in Model reference.      
       Controller controller = new Controller(myNums); 
}

您的控制器需要对您的模型的引用。在这种情况下,控制器实际上创建了所有Swing组件。对于C#,您可能希望在此处保留表单初始化,但视图/表单需要引用模型(myNums)和Controller(控制器)。希望一些C#人可以在这方面提供帮助。视图还需要注册为模型的观察者(参见观察者模式)。

这是我的构造函数(根据您的情况调整):

public NumberView(Controller controller, MyNumbers myNums)
{
      this.controller = controller; // You'll need a local variable for this
      this.myNums = myNums; //You'll need a local variable for this
      myNums.registerObserver(this); // This is where it registers itself
}

View将工作传递给Controller以处理用户的操作(按钮,等等)。 Controller决定在模型中调用/执行的操作。一般来说,模型会做一些事情并改变它的状态(列表中的数字可能更多。无论它做什么)。此时,模型将让其观察者知道它已经改变并自我更新。然后View进入并获取新数据并自行更新。这就是Model和View谈论的原因(你的第3个问题)。

因此模型将具有:

public void notifyObservers()
{
    for (Observer o: observers)
    {
        o.update();  // this will call the View update below since it is an Observer
    }
}

所以在View中,你会有这样的东西:

public void update()
{
    setListBox(myNums.getNumbers());  // Or whatever form update you want
}

希望有所帮助。我知道它是Java,但这个概念仍然适用。您必须对Observer模式进行一些阅读才能完全获得它。祝你好运!

答案 5 :(得分:1)

  

财务主任是否应负责任   用于创建MyNumbers?

我会说'绝对不是。'

如果MVC模式被设计为解耦M,V和&amp; C元素,如果C只用new MyNumbers()实例化M,那么它如何工作?

在Java中,我们会在这里使用类似Spring Framework的内容。您需要一种方法来表达依赖关系 - 或者更确切地说,它是如何实现的 - 在配置文件或其他合适的位置(不在编译代码中)。

但是这个问题还有另外一个因素:你可能不应该用你打算使用的具体的运行时类型定义myNumbers变量(在C里面)。使用接口或抽象类,并使其保持打开状态,以确定实际的运行时类型。通过这种方式,将来您可以重新实现IMyNumbers接口以满足新兴要求(您今天不知道的那些),并且您的C组件将继续完美地工作,而不是更明智。

答案 6 :(得分:1)

  

为什么在下面的MVC图中,View有一个箭头指向模型? Controller不应该始终是View和Model之间的桥梁吗?

它是MVC模型2.您通常可以在Java企业应用程序中看到它 CONTROL执行业务以及处理来自/到MODEL的数据,并选择要呈现给客户端的VIEW。渲染到客户端时,VIEW将使用MODEL:

中的数据

alt text http://www.blogjava.net/images/blogjava_net/marco/7342/o_2.JPG

以下是如何从JSP(VIEW)文件中访问数据的示例(bean):

class Person {String name;} // MODEL
My name is ${bean.name}     // VIEW

答案 7 :(得分:1)

我会尝试从一个相对不太技术性的立场来回答这个问题。我将尝试通过一个一般的例子。

Controller控制view使用的内容。所以,比方说,如果您正在写信给页面,controller将引导您转到input view(比如说​​),而如果您正在阅读同一页面,它将引导您转到success view(说)。

在向页面写入之后,controller会将这些参数传递给相关的model,其中包含与他们必须完成的操作相关的逻辑。如果出现错误,则controller会引导您转到error view

我的知识基于我对Agavi的一个月经验。希望这会有所帮助。

答案 8 :(得分:0)

  • 查看绘制模型并将其表示给用户
  • Controller处理用户输入并将其转换为对模型的修改
  • 模型保存数据和修改逻辑

没有必要将其转换为代码。无论如何,你不会直接纠正它。

答案 9 :(得分:0)

查看

  • 用户界面/负责输入输出/一些验证/需要有办法向外界通知UI级事件

  • 只知道模型

模型

  • 数据结构/表示呈现/不应包含业务逻辑的数据(可能最多只能进行一些数据/结构验证)
  • 只知道自己(想想一个只有名字和年龄的人类)

控制器

  • 负责业务逻辑/它创建视图并将各自的模型粘合到它们上/必须能够响应查看事件/访问应用程序的其他层(持久性/外部服务/业务层等)

  • 知道所有事情(至少是视图和模型)并负责将所有内容粘在一起