在我开始下一个重大项目之前,我正在阅读OO.为了给你一些快速的背景知识,我是一名PHP开发人员,致力于Web应用程序.
特别感兴趣的一个领域是用户界面; 具体如何构建它并将其连接到我的OO"模型".
我一直在读这个领域.我最喜欢的一个是: 为面向对象的系统构建用户界面
"所有对象都必须提供自己的UI"
考虑到我的问题,我可以看到这很好用.例如,我构建了我的"用户"对象来代表登录我网站的人.我的一种方法是"display_yourself"或类似方法.我可以在整个代码中使用它.也许从这开始就是他们的名字.之后,如果我需要调整以显示他们的名字+小头像,我可以更新这个方法,嘿-presto,我的应用程序更新.或者如果我需要将他们的名字作为他们个人资料的链接,嘿-presto我可以从一个地方轻松再次更新.
在OO系统方面; 我认为这种方法效果很好.看看其他StackOverflow线程,我在"Separation of Concerns"下找到了这个: Soc
"在计算机科学中,关注点分离(SoC)是将计算机程序分解为尽可能少地在功能上重叠的不同特征的过程.关注点是程序中的任何兴趣或焦点.通常,关注点是同义词在信息隐藏的帮助下,传统上通过模块化和封装来实现SoC的进步.
在我看来,我已经实现了这一点.我的用户对象隐藏了它的所有信息.在我显示它之前,我在代码中没有任何地方我说$ user-> get_user_name().
然而,这似乎违背了其他人似乎认为的"最佳实践".
引用同一问题中的"选定"(绿色)答案:
"关注点的分离是将每个问题的代码分开.改变接口不应该要求改变业务逻辑代码,反之亦然.模型 - 视图 - 控制器(MVC)设计模式是分离这些问题的一个很好的例子.为了更好的软件可维护性."
为什么这样可以提高软件的可维护性?当然,对于MVC,我的View必须对模型有很多了解吗?阅读JavaWorld文章,了解有关这一点的详细讨论: 为面向对象的系统构建用户界面
无论如何......最后到达实际点!
1.任何人都可以推荐任何详细讨论此内容的书籍吗?我不想要一本MVC书; 我不是在MVC上卖的.我想要一本讨论OO/UI,潜在问题,潜在解决方案等的书.(可能包括MVC)Arthur Riel的面向对象设计启发式
触及它(也是一本优秀的书!),但我想要更详细的东西.
2.任何人都可以提出一个与Allen Holub的JavaWorld文章一样充分解释的论点,该文章解释了为什么MVC是一个好主意?
非常感谢能够帮助我就此得出结论的任何人.
这是如何经常教授OOP的失败,使用像rectangle.draw()和dinosaur.show()这样的例子,这些都没有任何意义.
当你谈论有一个显示自己的用户类时,你几乎回答了自己的问题.
"稍后,如果我需要调整以显示他们的名字+小头像,我可以更新这个方法而且嘿-presto,我的应用程序更新."
暂时想想那件小事.现在看看Stack Overflow并注意您的用户名出现的所有地方.在每种情况下看起来都一样吗?不,在顶部,您的用户名旁边有一个信封,后跟您的声誉和徽章.在一个问题主题中,你已经获得了你的头像,然后是你的用户名,下面是你的声誉和徽章.你认为有一个用户对象有像getUserNameWithAvatarInFrontOfItAndReputationAndBadgesUnderneath()这样的方法吗?罗.
对象涉及它所代表的数据以及对该数据起作用的方法.您的用户对象可能具有firstName和lastName成员,以及检索这些部分所需的getter.它也可能有一个方便的方法,比如toString()(用Java术语),它会以通用格式返回用户的名字,比如第一个名字后跟一个空格,然后是姓氏.除此之外,用户对象不应该做太多其他事情.由客户决定它想要对象做什么.
以您向我们提供用户对象为例,然后考虑如果在其中构建"UI",您将如何执行以下操作:
创建一个CSV导出,显示按姓氏排序的所有用户.例如姓氏,名字.
提供重量级GUI和基于Web的界面以使用用户对象.
在一个位置显示用户名旁边的头像,但只显示另一个用户名.
提供RSS用户列表.
在一个地方显示用户名粗体,在另一个地方显示斜体,在另一个地方显示超链接.
在适当的位置显示用户的中间名首字母.
如果你考虑这些要求,它们都归结为提供一个只关注它应该关注的数据的用户对象.它不应该试图成为所有人的所有事情,它应该只是提供获取用户数据的手段.您将创建许多视图中的每个视图,以决定它如何显示用户数据.
您想要在一个地方更新代码以在许多地方更新您的视图是一个很好的想法.这仍然是可能的,不会在太低的水平上捣乱.您当然可以创建类似于小部件的类,这些类将封装各种常见的"东西"视图,并在整个视图代码中使用它们.
这是我使用MVC /关注点分离模式在PHP中创建网站时采用的方法:
我使用的框架有三个基本部分:
模型 - PHP类.我为它们添加方法来获取和保存数据.每个模型代表系统中不同类型的实体:用户,页面,博客帖子
视图 - Smarty模板.这就是html所在的地方.
控制器 - PHP类.这些是应用程序的大脑.通常,站点中的URL会调用该类的方法.example.com/user/show/1将调用$ user_controller-> show(1)方法.控制器从模型中取出数据并将其提供给视图.
这些部分中的每一部分都有特定的工作或"关注".该模型的工作是为数据提供一个干净的界面.通常,站点的数据存储在SQL数据库中.我向模型添加方法以获取数据并保存数据.
该视图的工作是,以显示数据.所有HTML标记都在视图中.在视图中处理用于处理数据表的斑马条带的逻辑.用于处理日期应显示的格式的代码在视图中.我喜欢使用Smarty模板进行视图,因为它提供了一些很好的功能来处理这样的事情.
该控制器的工作是充当用户,模型,视图之间的中介.
让我们看看这些如何结合在一起以及益处所在的一个例子:
想象一个简单的博客网站.主要数据是帖子.此外,假设该网站记录了帖子的查看次数.我们将为此创建一个SQL表:
posts id date_created title body hits
现在假设您想要显示5个最受欢迎的帖子.以下是您可能在非MVC应用程序中看到的内容:
$sql = "SELECT * FROM posts ORDER BY hits DESC LIMIT 5"; $result = mysql_query($sql); while ($row = mysql_fetch_assoc($result)) { echo "$row['title']
"; }
这段代码非常简单,如果符合以下条件,效果很好:
这是您想要展示最受欢迎帖子的唯一地方
你永远不想改变它的外观
你永远不会决定改变什么是"热门帖子"
想象一下,您想在主页上显示10个最受欢迎的帖子,在子栏目的侧栏中显示5个最受欢迎的帖子.您现在需要复制上面的代码,或者将其放在带有逻辑的包含文件中以检查它的显示位置.
如果您想更新主页的标记以向今天创建的帖子添加"新帖"类,该怎么办?
假设您认为帖子很受欢迎,因为它有很多评论,而不是点击.数据库将更改以反映这一点.现在,必须更新应用程序中显示热门帖子的每个位置以反映新逻辑.
你开始看到复杂形式的雪球.很容易看出在项目过程中如何变得越来越难以维护.另外,考虑多个开发人员在处理项目时的复杂性.在向输出中添加类时,设计人员是否应该咨询数据库开发人员?
采用MVC方法并在应用程序中强制分离关注点可以缓解这些问题.理想情况下,我们希望将其分为三个方面:
数据逻辑
应用逻辑
和显示逻辑
让我们看看如何做到这一点:
我们将从模型开始.我们将有一个$post_model
类并给它一个名为的方法get_popular()
.此方法将返回一组帖子.另外,我们将给它一个参数来指定要返回的帖子数量:
post_model.php class post_model { public function get_popular($number) { $sql = "SELECT * FROM posts ORDER BY hits DESC LIMIT $number"; $result = mysql_query($sql); while($row = mysql_fetch_assoc($result)) { $array[] = $row; } return $array; } }
现在在主页上我们有一个控制器,我们称之为"家".让我们假设我们有一个url路由方案,在请求主页时调用我们的控制器.它的工作是获得热门帖子并给他们正确的观点:
home_controller.php class home_controller { $post_model = new post_model(); $popular_posts = $post_model->get_popular(10); // This is the smarty syntax for assigning data and displaying // a template. The important concept is that we are handing over the // array of popular posts to a template file which will use them // to generate an html page $smarty->assign('posts', $popular_posts); $smarty->view('homepage.tpl'); }
现在让我们看一下视图的样子:
homepage.tpl {include file="header.tpl"} // This loops through the posts we assigned in the controller {foreach from='posts' item='post'} {$post.title} {/foreach} {include file="footer.tpl"}
现在我们有了应用程序的基本部分,可以看到关注点的分离.
该模型涉及获取数据.它了解数据库,它了解SQL查询和LIMIT语句.它知道它应该交回一个不错的数组.
该控制器知道用户的请求,他们正在寻找的网页.它知道主页应该显示10个热门帖子.它从模型中获取数据并将其提供给视图.
该视图知道桩的阵列应显示为与后他们休息标签一系列阿克标签.它知道帖子有标题和id.它知道帖子的标题应该用于锚文本,并且帖子id应该用在href中.该视图还知道页面上应该显示页眉和页脚.
提及每件作品不知道的内容也很重要.
该模型不知道热门帖子显示在主页上.
该控制器和视图不知道的帖子都存储在一个SQL数据库.
该控制器和型号不知道,每一个环节的岗位在主页上应后有一个休息的标签.
因此,在这种状态下,我们已经建立了数据逻辑(模型),应用程序逻辑(控制器)和显示逻辑(视图)之间关注点的明确分离.那么现在怎么办?我们采用了一个简短的PHP代码片段并将其分成三个令人困惑的文件.这给了我们什么?
让我们来看看如何分离关注点可以帮助我们解决上述问题.重申一下,我们希望:
在子栏目的侧栏中显示热门帖子
使用额外的css类突出显示新帖子
更改"热门帖子"的基础定义
要在侧边栏中显示热门帖子,我们将在子页面中添加两个文件:
子页面控制器......
subpage_controller.php class subpage_controller { $post_model = new post_model(); $popular_posts = $post_model->get_popular(5); $smarty->assign('posts', $popular_posts); $smarty->view('subpage.tpl'); }
...和子页面模板:
subpage.tpl {include file="header.tpl"}{foreach from='posts' item='post'} {$post.title} {/foreach}{include file="footer.tpl"}
新的子页面控制器知道子页面应该只显示5个热门帖子.子页面视图知道子页面应该将帖子列表放在侧边栏div中.
现在,在主页上我们想要突出显示新帖子.我们可以通过修改homepage.tpl来实现这一点.
{include file="header.tpl"} {foreach from='posts' item='post'} {if $post.date_created == $smarty.now} {$post.title} {else} {$post.title} {/if} {/foreach} {include file="footer.tpl"}
这里视图处理显示热门帖子的所有新逻辑.该控制器和模型并不需要知道这些改变任何东西.它纯粹是显示逻辑.子页面列表继续像以前一样显示.
最后,我们想改变一个热门帖子的内容.我们希望它不是基于页面获得的点击次数,而是基于帖子获得的评论数量.我们可以将该更改应用于模型:
post_model.php class post_model { public function get_popular($number) { $sql = "SELECT * , COUNT(comments.id) as comment_count FROM posts INNER JOIN comments ON comments.post_id = posts.id ORDER BY comment_count DESC LIMIT $number"; $result = mysql_query($sql); while($row = mysql_fetch_assoc($result)) { $array[] = $row; } return $array; } }
我们增加了"流行帖子"逻辑的复杂性.但是,一旦我们在模型中进行了这种改变,在一个地方,新逻辑就会应用到任何地方.主页和子页面没有其他修改,现在将根据评论显示热门帖子.我们的设计师不需要参与其中.标记不受影响.
希望这为分离数据逻辑,应用程序逻辑和显示逻辑的关注点提供了一个引人注目的例子,可以使您的应用程序开发更容易.一个地区的变化往往对其他地区的影响较小.
遵循此约定并不是一个能够自动使您的代码完美的灵丹妙药.毫无疑问,你会遇到一些问题,即分离应该在哪里.最后,它是关于管理应用程序内的复杂性.
您应该充分考虑如何构建模型.他们将提供什么样的接口(参见格雷戈里关于合同的答案)?控制器和视图期望使用哪种数据格式?提前考虑这些事情将使事情变得更加容易.
此外,在启动项目以使所有这些部分能够很好地协同工作时,可能会有一些开销.有许多框架为模型,控制器,模板引擎,URL路由等提供构建块.有关PHP MVC框架的建议,请参阅SO上的许多其他帖子.这些框架将帮助您启动并运行,但您作为开发人员负责管理复杂性并强制执行关注点分离.
我还要注意,上面的代码片段只是简化的示例.他们可能(很可能)有虫子.但是,它们在结构上与我在我自己的项目中使用的代码非常相似.