上回說到 Fat Model 的邏輯散落在各處,那這回就要來說說散落在哪、以及造成這種現象的原因。
MVC(Model–View–Controller)是一種軟體架構,主要是將軟體系統分成三個部分,使得程式分工容易,利於團隊的擴張。
更多關於 MVC 的文章可以參考以下
Model、View、Controller 三分天下
接著我們看看 Rails 官網的教學,以下是顯示一篇文章的 model、controller 跟 view。
class Article < ActiveRecord::Base
end
class ArticlesController < ApplicationController
def show
@article = Article.find(params[:id])
end
end
<h1>Listing articles</h1>
<table>
<tr>
<th>Title</th>
<th>Text</th>
</tr>
<% @articles.each do |article| %>
<tr>
<td><%= article.title %></td>
<td><%= article.text %></td>
</tr>
<% end %>
</table>
寫起來很方便快速,但卻有個致命的問題,在 view 中可以直接拿到 model
接著我們加入一些情境,加速程式腐化的速度
假設現在收到一個需求是想要計算文章的數量,並跟著文章標題一起顯示,而且2個小時後就需要 demo!
最快的做法是在 controller 上新增這個邏輯並在 view 中取用
class ArticlesController < ApplicationController
def show
@article_count = Article.count
@article = Article.find(params[:id])
end
end
<h1>Listing articles</h1>
<table>
<tr>
<th>Title</th>
<th>Text</th>
</tr>
<% @articles.each do |article| %>
<tr>
<td><%= "#{@article_count}. #{article.title}" %></td>
<td><%= article.text %></td>
</tr>
<% end %>
</table>
這時候會衍生兩個問題,第一個是如果很多不同的 view 都會使用這個邏輯,這段 code 必須複製過去,違反 DRY 原則,第二個是如果顯示上有許多這樣的調整,controller 上會有許多邏輯,instance variable 也會無止境的擴張。
接下來可能會想要把 controller 上的邏輯收進 model 中。
class Article < ActiveRecord::Base
def display_title
"#{count}. #{title}"
end
end
<h1>Listing articles</h1>
<table>
<tr>
<th>Title</th>
<th>Text</th>
</tr>
<% @articles.each do |article| %>
<tr>
<td><%= article.display_title %></td>
<td><%= article.text %></td>
</tr>
<% end %>
</table>
於是 model 和 view 便有了耦合
隨著專案越來越大,view 中會參雜許多邏輯,加上 model 和 model 間也會有許多的關聯,久而久之便會變成一坨大泥球。
在 Rails 中提出的解決方法是透過 presenter 這個設計模式去解決這個問題,所有的顯示邏輯應該被包在 presenter 中而不是在 model 上,model 不應該知道 view 的存在,不過如何實作也是需要好好設計!
下一篇會分享 Rails 預設的資料夾結構如何加重這個問題