使用MVC,OO和设计模式开始软件设计

hin*_*hoo 9 oop model-view-controller design-patterns

注意:此问题已更新,以提供比以前更多的详细信息和见解.

更新:我只想对所有回复的人说声谢谢.对于Widget来说,哪种设计模式效果最好,我仍然处于黑暗中.也许是Factory或Builder模式之一?


我刚刚开始一个新项目,需要使用MVC,OO和设计模式.

这是一个想法:想象一个显示一组小部件的页面.这些小部件(通常)是基于数据库中几个单独表中包含的数据的图表.我使用一个报告学生表现的页面运行示例.

高级要求

  • 显示一组(仅限HTML)小部件的页面.
  • widget的数据将基于数据库查询.
  • 该页面可用于查看包含类似布局数据的单独数据集.例如,单个页面将显示报告单个学生表现的各个方面的小部件.
  • 想看到另一个学生的表现,拉起另一页.不需要为不同的学生显示不同的小部件(尽管以后可能会很好).
  • 可能有很多学生,但数据库中包含的数据同样适用于所有学生.
  • 窗口小部件的显示方式可以轻松更改(例如,将小部件从显示为饼图更改为显示为条形图).
  • 小部件应该能够快速创建.

低级要求

  • 目前数据不会更改,因此小部件不需要自动更新.
  • 窗口小部件可以表示两个事物的比率(例如,失败的测试与成功测试的比率作为饼图),一系列点,或者有时是单个数值.
  • 开发新的小部件应该是轻而易举的,现有的代码不需要修改.
  • 要使用的框架:Zend Framework,基于MVC.

(最低限度)定义窗口小部件有三件事:要报告的数据集(在上面的示例中,学生ID),描述报告的度量标准的查询以及呈现模式(条形图,时间序列等).

这是打破MVC每一层职责的过程:

视图: Zend视图是注入了PHP的HTML模板.它们将包含几种类型的小部件之一.窗口小部件有多种形式,包括:静态JPEG图像(从远程站点加载,即:<img src="http://widgetssite.com?x=2&y=3"/>基于JSON的javascript小部件,或各种图表(饼图,条形图等))

控制器:创建窗口小部件,然后将它们分配给视图.要在页面上显示的小部件集需要在某处维护.由于我无法想到在视图中执行此操作的好方法,我现在将其添加到控制器的职责中.如果有一个更好的地方请大声呼喊.控制器还必须处理任何其他输入参数并将它们传递给小部件.例如,data_set id可以在url行传递为http:/.../report/?student_id=42

模型:Zend框架中的模型负责提取数据,因此很可能包含用于访问数据库的每个窗口小部件的类.

一些要点:

  1. 此处的模型表示特定小部件的数据.所以必然,它需要知道查询将是什么,以便将获取该数据所需的表汇总在一起.

  2. 在窗口小部件呈现数据之前,最有可能需要一个额外的处理步骤.这取决于将使用哪个渲染器.有时可能需要从返回的数据中形成一个url.其他时候,一个JSON数组.其他时候可能会创建一些标记.这可以在模型或控制器或视图中进行.除非有人能想出将其移动到控制器或视图的充分理由,否则最好让它存在于模型中并保持视图和控制器的精简.

  3. 同样,小部件将由3个内容,其参数,数据和渲染器组成.

    问题的一个重要部分是:在面向对象的设计中表示窗口小部件的好方法什么? 我已经问了一次,无法得到答案.是否有可以应用于对此项目最有意义的窗口小部件的设计模式?

    这是Widget的一个相当简单的类的第一次传递:

    class Widget{ 
      //method called by the view 
      render() {//output the markup based on the widget Type and interleaved the processed data} 
    
      //methods called by the controller: 
      public function __construct() {//recieve arguments for widget type (query and renderer), call create()}  
      public function create() {//tell the widget to build the query, execute it, and filter the data} 
      public function process_data() {//transform into JSON, an html entity etc} 
    
      //methods called by the model: 
      public function build_query() {...}; 
      public function execute_query() {...}; 
      public function filter_data() {...}; 
    } 
    
    Run Code Online (Sandbox Code Playgroud)

    看着它,我已经看到了一些问题.

  4. 例如,可以直接将在控制器中创建的窗口小部件传递给要渲染的视图.

    但是,当涉及到实施模型时,它似乎并不那么直截了当.表网关模式比ORM更容易实现.但由于表网关模式对于每个模型/表都有一个类,因此它似乎不符合要求.我可以为特定的表创建一个模型,然后在其中,实例化所需的任何其他模型.但这似乎不适合表格网关模式,而是适合ORM模式.表网关模式可以用多个表实现吗?有什么替代品?控制器是否创建窗口小部件并且窗口小部件创建模型是否有意义?

  5. 出现的另一个问题是这种设计无法轻松创建小部件.即.说我想创建一个PiechartWidget,可以重用多少代码?使用一些OO想法(如接口或抽象类/方法和继承)会更有意义吗?

    假设我抽象了Widget类,所以只有具体定义了共享方法,其余的被声明为抽象方法.修改Widget类以使其抽象化(第二遍):

    abstract class Widget{
    
      private $_type;
      private $_renderer;
    
      //methods called by the controller:
      //receive arguments for widget type (query and renderer), 
      protected function __construct($type, $renderer) {
        $this->_type = $type;
        $this->_render = $renderer;
        $this->create();
       } 
    
      //tell the widget to build the query, execute it, and filter the data
      private function create() {
        $this->build_query();
        $this->execute_query();
        $this->filter_data();
      }
    
      //methods called by the model:
      abstract protected function build_query();
    
      protected function execute_query() {
        //common method
      }
    
      abstract protected function filter_data();
    
      //method called by controller to tranform data for view
      //transform into JSON, an html entity etc
      abstract protected function process_data();
    
      //method called by the view
      //output the markup based on the widget Type and interleave the processed data
      abstract protected function render(); 
    }
    
    Run Code Online (Sandbox Code Playgroud)

    这是一个很好的设计吗?怎么可以改进?

  6. 我假设编写一个新的小部件将需要至少一些新的代码来构建查询,并且可能过滤数据,但它应该能够使用预先存在的代码来完成其余的所有功能,包括已经存在的渲染器.

我希望任何人都能提供至少一些有关此设计的反馈.验证它?撕裂它.叫我白痴.那也没关系.我可以使用任何前进牵引力.

一些具体问题:

Q1.实现渲染器的最佳方法是什么,作为Widget类的一部分或作为单独的类?1A.如果是单独的,它将如何与widget类进行交互?

Q2.我怎样才能改进这种设计以简化新类型小部件的创建?

Q3.最后,我觉得我在这里遗漏了一些有关数据封装的内容.数据封装如何与需求相关并在此场景中发挥作用?

Dav*_*ims 1

所有这些想法(MVC、模式等)背后的目的本质上是相同的:每个类都应该做一件事,并且应用程序中的每个不同的职责都应该分为不同的层。您的视图(页面和小部件)应该很薄,除了呈现从模型收集的数据之外,几乎不会做出任何决定。模型应该不可知地在数据层上运行,也就是说,它们不应该知道其数据源是否是特定类型的数据源。控制器也应该很薄,基本上充当视图和模型之间的路由层。控制器接收用户的输入并对模型执行相关操作。此概念的应用因您的目标环境(Web、富客户端等)而异。

模型的架构本身就不是一个小问题。您有许多模式和许多框架可供选择,而选择正确的模式(模式或框架)将完全取决于您所在领域的具体情况,而我们这里的具体情况太少,无法为您提供更具体的建议。我只想说,建议您花一些时间了解一些适合您的特定技术堆栈(无论是 Java、.NET 等)的对象关系映射框架以及它们构建的相应模式。

还要让自己熟悉MVPMVC之间的区别——Martin Fowler的工作在这里至关重要。

至于设计模式,大多数标准GOF模式的应用可以很容易地以某种形式发挥作用,建议您花时间阅读设计模式或有关该主题的许多介绍性文本之一。这里没有人能够给出关于 MVC 如何应用于您的领域的具体答案 - 这只能由经验丰富的工程师与产品负责人合作来回答,产品负责人有权做出工作流程和 UI 决策,这些决策将极大地影响其具体决策。

简而言之,您问题的本质表明您需要一位经验丰富的 OOP 架构师或以前做过此工作的高级开发人员。或者在继续前进之前给自己大量的时间进行深入研究。您的项目范围包含大量的学习内容,许多程序员需要数年时间才能完全掌握。这并不是说您的项目注定要失败——事实上,如果您选择正确的技术堆栈、框架等,并且假设您相当聪明并专注于手头的任务,您可能能够完成很多工作。但我认为,在时间有限的情况下,第一次尝试就无法获得像“MVC”或“OO”这样广泛的概念。

编辑:我刚刚听到你的编辑:Zend。拥有一个适当的框架是件好事,它可以处理很多架构决策。我不熟悉 Zend,但我会坚持使用它的默认值。这里更多取决于您的最终 UI 交付 - 您是否处于 Flash 或 Silverlight 之类的 RIA 环境中,还是处于严格的 HTML/JavaScript 环境中?在任何一种情况下,控制器仍然应该很薄,并作为路由器运行,从 HTTP 获取和发布中获取用户请求,并立即移交给模型。意见也应该保持薄弱,并做出尽可能少的决定。Rails 和随后的框架已经很好地确立了在 Web 环境中应用 MVC 的概念,我假设 Zend 在这方面类似于 CakePHP 之类的东西:应用程序服务器有一个路由系统,将 HTTP 调用映射到以特定视图响应的控制器操作。请求/响应周期基本上是这样的:

  1. 通过 URL 发布的用户请求
  2. 路由器将控制权交给控制器类
  3. 控制器使用给定的参数调用模型
  4. 该模型对数据进行操作,发回控制器
  5. 该框架将完成的数据映射到视图中,并使用某种将请求结果放入视图范围内的代码隐藏。
  6. 该框架创建 html(或 xml 或其他)并回发给调用者。