Model设计参考¶
在MVC中,Model排第一,是有一定暗示的。一是Model是整个架构中,代码量最大,复用程度最高, 也是最体现程序员设计功力的地方。 二是View和Controller相对于Model而言,在实际开发中,复用程度不高,逻辑复杂程度较低。 可以说,Model设计得好,整个MVC就好,应用开发起就顺。
因此,这一节将以Model为核心,来讲MVC的设计。 实话说,MVC尽管提出了Model View Controller的划分思想,但到了具体实操中,并不是很好把握的。 下面介绍的设计参考,也仅仅是个人在实际项目中的一些体会和想法,仅作参考。 在具体设计中,可以后把握这么几点:
Model应当集中整个应用的数据和业务逻辑¶
应用当中涉及到的所有业务对象都应尽可能抽像成Model。 如,博客系统当中,文章要抽象成Post,评论要抽象成Comment。 而相关的业务逻辑,如发布新文章可以用 Post::create()
,删除评论可以用 Comment::delete()
。 这样子整个应用就显得很清晰明了。
基础Model应当尽可能细化¶
在一个应用中,特别是对于大型复杂应用,Model间关系可能比较复杂。在构造应用时,特别是基础Model时, 要从足够小的粒度来设计。 此时,就要考虑采取继承、封装等措施了。 比如,一个博客文章Post,一般包含了若干标签,在页上一般写在作者、日期等Post字段的旁边。 从逻辑上来看,把标签作为Post的一个属性,是说得通的。 但是如果把标签作为一个属性像标题、正文等字段一样依附于Post。那么在有的功能上,实现起来是有难度的。 比如,客户要求,当一个Post含有标签 “yii, model” 时,可以点击 “yii” , 然后系统列出所有具标签中含有 “yii” 的文章。
为了实现这个功能,正确的设计是单独将标签抽象成Tag。这样,Post和Tag是多对多的关系, 即一个Post有多个Tag,一个Tag也对应多个Post。这个多对多关系可以通过一张数据表 tbl_post_tag
来表示。 接下来,为Post增加 Post::getTags()
方法,并通过 tbl_post_tag
表来查询当前Post的所有标签。 同时,为Tag增加 Tag::getPosts()
方法,也通过 tbl_post_tag
表来查询当前Tag对应的文章。 这样,就具备了实现客户要求的新功能的基础。
因此,在Model设计上,要以尽量小的粒度进行设计。一般而言,粒度越小,复用的可能性就越高。
有的读者可能会问了,既然要求粒度尽可能地小,那么,Post是不是也应当再细化,把段落抽象为Model? 是否有这个必要,看客户需求。一般情况确实没有这必要,如果这么做,那是不是再以句子为单位进行抽象? 但如果客户要求这个博客系统的评论是针对段落进行的评论的, 要将评论显示在对应的段落旁边,甚至显示每个段落评论人次等功能。那么就需要把段落抽象成Model了。
分层次设计Model¶
从设计流程上,数据库结构设计与Model的设计是紧密相关的。先有数据库结构设计,后有Model设计。 在设计数据库结构的时候,也是在设计Model。 一般而言,最单元、粒度最小的Model就是根据每个数据库表所生成的Model,这往往是个Active Record。
比如标签的问题,在数据库存储过程中,Post和Tag是分开存的,而且这两个表的字段,没有冗余。 tbl_post_tag
表也只记录他们的ID,没有实质内容。
在获取数据渲染视图,向用户展现时,这两个Model及他们的字段,是完全够用,且没有冗余的。
那么,能不能说 Post 和 Tag 这两个Model是够用的呢?显然还不够。
当用户在创建文章、修改文章、审核文章时,需要采用一个表单来显示来收集用户输入。 其中,对于标签的采集,一般是一个长条的文本框,让用户一次性输入多个标签,并以 ,
等进行分隔的。
但是,这个文本框没有一个字段与之进行对应。我们也没办法对这个字段的用户输入进行任何的验证、预处理。
因此,Post的功能是不够用的。不够用怎么办?那就加吧。但直接在 Post 里面加个 public $tagString
并不好。 毕竟只是在使用表单时,才会有这个问题,其他场合,这个字段是没用的。
这种情况下,一般使用继承:
public class PostForm extends Post
{
public $tagString;
... ...
}
这样,当控制器发现用户在创建、修改、审核文章时,可以使用 PostForm Model来渲染视图了, 而其他场合则仍使用Post。这样就在需要时,增加了一个 tagString
的字段用于收集用户输入的标签。
在具体设计过程中,由于Model本身就会包含很多代码,因此,要多使用这继承等手段,把代码组织好。
仔细为Model方法命名¶
由于Model的代码量比较大,又集中了大量的逻辑,因此,会在一个Model中有大量的方法。仍然以Post为例, 会涉及到创建、审核、发布、回收等流程,相关的方法比较多,在命名上要用心。 可能会涉及到的、名字又比较接近的方法就有:
- getPrevPost(),前一篇文章,用于导航
- getNextPost(),下一篇文章,用于导航
- getRelatedPosts($n = 10),获取相关的N篇文章,用于相关文章推荐列表
- getPostsOfAuthor($n = 10),获取同一作者的N篇相关文章,用于作者文章列表
- getLatestPosts($n = 10),最新的N篇文章,静态方法,用于文章列表或RSS输出
- getHotestPosts($n = 10),最热门的N篇文章,静态方法,用于热门文章列表
- getPublishPosts($n = -1),获取已经发布的N篇文章,静态方法,用于文章列表
- getDraftPosts($n = -1),获取未发布的N篇文章,静态方法,用于作者页面
这里只是一些获取其他Post的方法,命名比较合理,一看就知道意思。 而且全部写成getter的形式,可以使用读取属性的方式进行访问。
不单单是在Model方法的命名上要用心, 在变量名、类名、方法名等的命名上,也要养成习惯,形成规律。 不要图一时之快,胡乱起名。否则,出来混,迟早要还的。
网友评论