【问题标题】:Getting started with software design using MVC, OO, and Design patterns [closed]开始使用 MVC、OO 和设计模式进行软件设计 [关闭]
【发布时间】:2011-01-08 01:00:34
【问题描述】:

注意: 此问题已更新,以提供比以前更详细和更深入的信息。

更新: 我只想对所有回复的人说声谢谢。我仍然对哪种设计模式最适合小部件一无所知。也许是工厂模式或建造者模式之一?


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

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

高级要求

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

低级要求

  • 目前数据不会改变,因此小部件不需要自动更新。
  • 小部件可以表示两种事物的比率(例如,以饼图形式表示的失败测试与成功测试的比率)、一系列点或有时是单个数值。
  • 新小部件的开发应该轻而易举,无需修改现有代码。
  • 要使用的框架:Zend 框架,基于 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() {...}; 
    } 
    

    看着它,我已经可以看到一些问题。

  4. 例如,将在控制器中创建的小部件传递给视图进行渲染是很简单的。

    但在实施模型时,它似乎并不那么简单。表网关模式比 ORM 更易于实现。但由于表网关模式对每个模型/表都有一个类,它似乎不符合要求。我可以为特定表创建一个模型,然后在其中实例化所需的任何其他模型。但这似乎不适合表网关模式,而是 ORM 模式。 表网关模式可以用多个表实现吗?有哪些选择?控制器创建小部件,小部件创建模型有意义吗?

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

    假设我抽象了 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(); 
    }
    

    这是一个好的设计吗?如何改进?

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

我希望任何人都可以对此设计提供至少一些反馈。验证它? 撕开它。叫我白痴。那也很好。我可以使用任何向前的牵引力。

几个具体问题:

第一季度。作为 Widget 类的一部分或作为单独的类,实现渲染器的最佳方式是什么? 1a。如果分开,它将如何与小部件类交互?

第二季度。我如何改进这种设计以简化新类型小部件的创建?

第三季度。最后,我觉得我在数据封装方面遗漏了一些东西。数据封装如何与需求相关并在此场景中发挥作用?

【问题讨论】:

  • 这是使用 Zend 框架,所以 MVC 是固有的。
  • 是否所有数据都像您给出的示例那样采用比率形式,或者某些小部件会显示比率而其他小部件会显示例如一组点?
  • 您的问题是关于小部件还是包含小部件的页面?
  • 只有一页就不需要 MVC。控制器执行导航部分,根据您的要求,您无需导航。

标签: model-view-controller design-patterns oop


【解决方案1】:

对于#2,如果您在 Windows 上使用 WPF,或者一般来说使用 Silverlight,请考虑使用 MVVM 模式(模型-视图-视图模型),以下是 WPF 实现的解释: MVVM at msdn

对于 #1(cmets 不回答):对于 MVC 的确切实现(和微小的变体),这实际上取决于您使用的语言。

MVC 的另一个替代方案是 MVP Model View Presenter

请记住,OO 的目标不是将设计模式塞进您的代码中,而是创建具有更少错误/提高可读性的可维护代码。

【讨论】:

    【解决方案2】:

    高要求 - 显示一组小部件的页面。小部件基于数据库中几个单独的表中包含的数据。 - 小部件的数据将基于数据库查询。小部件以特定方式显示其数据。 - 小部件应该能够快速创建。

    低级要求 - 数据变化,多个图表需要变化,推送模型(从数据到ui) - 新小部件的开发应该轻而易举,无需修改现有代码

    来自设计模式基础的建议 - MVC 支持一对多通知模式,所以是的,一旦你的小部件被初始化、创建并连接到网页,它应该等待来自数据库的通知。 - 策略模式,你的整个代码应该开发成一个接口。应该将新的小部件添加到参数化的 LinkedList(或其他一些数据结构)。这样,新的小部件开发人员只需实现接口,您的框架即可获取这些通知,而无需更改现有代码。

    悉达多

    【讨论】:

    • 我喜欢您对要求的细分。我很抱歉没有更清楚。一个要求被严重关闭,它不是一个推送类型的模型。小部件不需要经常更新 that。如果您对基于此的小部件使用什么模式有建议,请告诉我。
    【解决方案3】:

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

    仅模型的架构就不是小问题。您有许多模式和许多框架可供选择,而选择正确的模式(模式或框架)将完全取决于您的领域的具体情况,我们在此提供的内容太少,无法为您提供更具体的建议。可以这么说,建议您花一些时间了解一些适用于您的特定技术堆栈(Java、.NET 等)的 Object-Relational Mapping 框架以及它们所基于的 respective patterns

    还要让自己熟悉MVPMVC 之间的区别——Martin Fowler 的工作在这里是必不可少的。

    至于设计模式,大多数标准GOF 模式的应用很容易以某种形式发挥作用,建议您花时间在Design Patterns 或众多introductory texts 之一主题。这里没有人可以给出关于 MVC 如何应用于您的领域的具体答案——这只能由经验丰富的工程师与有权制定工作流程和 UI 决策的产品负责人合作来回答,这将极大地影响他们的具体决策.

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

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

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

    【讨论】:

    • 终极 UI 交付 == 严格的 HTML/JavaScript 环境。
    【解决方案4】:

    听起来您想使用 MVC 和其他模式,因为它们是新的流行词。在模型视图和控制器之间拆分您的设计应该告诉您如何扩展应用程序的功能。尽管我完全同意使用 MVC 是正确的方法,但我建议您研究该模式并查看一些实现它的源代码。不过,作为您问题的开始,将显示的小部件将是您的视图,这应该是显而易见的。来自用户的输入(例如更改某些小部件的参数或请求其他信息)将进入您的应用程序,并应由控制器处理。一个具体的例子是基于 Java 的 HttpServlet。控制器 servlet 接收用户请求并询问应用程序的较低层(服务、持久性等)以获取模型的更新表示。该模型包括所有特定于域的对象(即来自数据库的数据等)。这些数据(更新后的模型)返回到控制器,控制器反过来为用户推出一个新视图。希望这足以让您开始设计应用。

    作为进一步的帮助,您可以考虑使用框架来协助开发您的应用。我非常喜欢 Spring,它具有一流的 MVC 实现,真正有助于指导您设计正确的 MVC Web 应用程序。

    【讨论】:

    • 欢迎对投反对票的人发表评论
    【解决方案5】:

    您可以考虑使用Subject Observer Pattern

    让你的类,命名为 DataReader 作为单个主题。您的多个小部件将充当观察者。一旦您的 DataReader 从服务器接收到数据,它(Subject)将通知多个小部件(Observer)。

    您的各个小部件可能有不同的呈现方式来呈现给来自 DataReader 的同一组数据。

    更新

    在主题通知观察者的消息中,您也可以包含消息类型信息。小部件将只处理符合自己兴趣的消息类型,而忽略消息的其余部分。

    【讨论】:

    • 感谢您的回答。我很好奇它是否适用于小部件可能基于单独查询的情况。另外,关于我问题的第二部分,如何最好地将其集成到 MVC 设计中?
    【解决方案6】:

    注意:这是我基于更新后的新问题的新答案。

    这是一个建议的类图。我将在序列图上工作。

    我的旧答案在这里:

    根据您描述的要求,您的模型是您的数据库或数据仓库,您的视图是您的饼图、条形图等。我认为不需要控制器,因为听起来您有一个控制器带有小部件的页面仪表板。
    您需要将时间花在数据库模型上。由于您正在执行所有选择并且没有更新,因此请使用非规范化数据模型来提高这些查询的效率。您应该根据维度的数量将这些查询的结果放入表类型对象(例如 2 维数组)或 3 维数组中。除非您使用动画,否则您只能使用 3 个维度。

    【讨论】:

    • 关于仅选择系统的非规范化架构的好建议!请澄清最后一部分“你应该推动......”的意思。是为了缓存吗?还是为了最终的表现?
    • LWoodyiii:谢谢,你用什么来创建这个图表?
    猜你喜欢
    • 1970-01-01
    • 2023-03-05
    • 1970-01-01
    • 2010-09-14
    • 1970-01-01
    • 1970-01-01
    • 2010-09-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多