了解 MVC 模式

2022-09-01 01:38:57

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

据我所知,.是用户所看到的。所以它通常是窗口/形式。介于 和 之间。控制器将使数据在两个方向上“流动”。它还会在需要时保留状态(如果我有一个包含5个步骤的向导,则有责任确保它们以正确的顺序进行,等等)。这是我的应用程序逻辑的核心所在。ViewControllerViewModelControllerModel,

这种观点是否正确?

为了尝试将其转化为更有意义的东西,我将尝试用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();
    }
}

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

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

应该负责创建吗?在这个简单的情况下,我认为它是可以接受的(无论如何都会做完全相同的事情,并且没有关联的状态)。但是,让我们假设我想要用于所有不同的控制器,我的应用具有相同的 .我必须传递给这个(以及所有其他需要它的人)我想要使用的实例。谁将对此负责?在这个WinForms的例子中,这将是?或者,这将是创建 ?ControllerMyNumbersMyNumbersMyNumbersControllerMyNumbersViewView

转过身来:这3个部分的实例化顺序是什么?“所有者”调用的代码是什么?应该同时创建 和 ?实例化 和 ?MVCControllerViewModelViewControllerControllerModel

第二个问题:

假设我只希望我的应用程序具有此描述,那么该方法应该是什么样子?mainUse CaseController

第三:

为什么在下面的 MVC 图中,有一个箭头指向 ?难道不应该永远是两者之间的桥梁吗?ViewModelControllerViewModel

alt text


我还有一两个问题,但在我理解了第一个细节之后,他们可能会更有意义。或者也许在我理解了第一个问题之后,所有其他问题都撕裂了。

谢谢!


答案 1

掌握MVC的最简单方法是在强制实施它的框架中使用它,话虽如此。

  • 模型与数据源(DB 或其他)交互,并允许您访问数据。
  • 视图与外部世界交互,它接收来自某个地方的输入并将数据传递给控制器,它还侦听控制器以确保其显示正确的数据。
  • 控制器是所有魔术发生的地方;控制器操作数据,推送事件,并在两个方向上处理更改(到/从视图到/从模型)。

这张图非常有用(它比维基百科的更有意义):MVC Diagram

来源,以及一篇关于MVC的精彩文章!


答案 2

至于我帖子中的批评,我想我会写一篇关于我如何倾向于在PHP中创建MVC模式的帖子

在PHP中,我将框架分为几个部分,当涉及到MVC时,有些是正常的。

初选:

  • 控制器
  • 视图

次要性 - 模型层

  • 视图加载器
  • 图书馆
  • 错误层

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

这是我构建它的方式

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

从我的图表中,我通常会绕过连接并执行一个,然后从中的链接分配数据。View <-> ModelController <-> ModelController <-> 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模式,但它可以称为收养MVC。

其他对象(如视图加载器)不应放入注册表中,因为那里不是专门为用户感兴趣的,而是由其他实体(如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>

我希望我的例子能帮助你更多地了解这一点。


推荐