`
mike.gao
  • 浏览: 46890 次
  • 性别: Icon_minigender_1
  • 来自: 北京
最近访客 更多访客>>
社区版块
存档分类
最新评论

瘦controller,富model

阅读更多
----先看这么一段rhtml代码:渲染模板中加入了这么多的逻辑,看起来不伦不类,这么做行是行,但是缺点很多,新手一般有这个毛病。那么这样做,首先是可读性很差,因为在渲染代码中最好都是贴近HTML代码,而这堆代码里把C的内容也加进来了。
<% people = Person.find(
            :conditions => ["added_at > ? and deleted = ?", Time.now.utc, false],
            :order => "last_name, first_name") %>
<% people.reject { |p| p.address.nil? }.each do |person| %>
    <div id="person-<%= person.new_record? ? "new" : person.id %>">
        <span class="name">
            <%= person.last_name %>, <%= person.first_name %>
        </span>
        <span class="age">
            <%= (Date.today - person.birthdate) / 365 %>
        </span>
    </div>
<% end %>

再看看controller和model里的东西:
# app/controllers/people_controller.rb
class PeopleController < ActionController::Base
end

# app/models/person.rb
class Person < ActiveRecord::Base
    has_one :address
end

空荡荡的controller,而在model里仅仅有一句关系声明。这么组合成的MVC显得很别扭,似乎就是一层渲染模板。几乎把C与M都忽略了。

无论如何这在MVC框架里是糟糕透顶的现象,MVC经过这么多年的实践考验,它的优点在于“可读性强”“可维护性好”“模块化”“关注点的分离”等等,我想你用MVC框架也就是想实现这些优点哇?那么首先需要改进的是尽可能的将逻辑内容搬到controller中,controller 的作用就是介于view和model之间,起到一个类似中介的作用。下面来看下这样改动之后的代码:
<!-- app/views/people/index.rhtml -->
<% @people.each do |person| %>
    <div id="person-<%= person.new_record? ? "new" : person.id %>">
        <span class="name">
            <%= person.last_name %>, <%= person.first_name %>
        </span>
        <span class="age">
            <%= (Date.today - person.birthdate) / 365 %>
        </span>
    </div>
<% end %>


# app/controllers/people_controller.rb
class PeopleController < ActionController::Base
    def index
        @people = Person.find(
            :conditions => ["added_at > ? and deleted = ?", Time.now.utc, false],
            :order => "last_name, first_name")
        @people = @people.reject { |p| p.address.nil? }
    end
end

这样看起来就好多了,模板中的代码更像是HTML的结构,而且你粗略的看一下controller里的代码就能知道在这个action渲染的模板中会显示什么数据。

还可以更进一步做的事情就是将现在模板代码中关于一部分数据的处理挪到Model中来:
# app/models/person.rb
class Person < ActiveRecord::Base
    # ...

    def name
        "#{last_name}, #{first_name}"
    end

    def age
        (Date.today - person.birthdate) / 365
    end

    def pseudo_id
        new_record? ? "new" : id
    end
end

<!-- app/views/people/index.rhtml -->
<% @people.each do |person| %>
    <div id="person-<%= person.pseudo_id %>">
        <span class="name"><%= person.name %></span>
        <span class="age"><%= person.age %></span>
    </div>
<% end %>

这样通过在model中添加几个虚拟属性,在view里调用,显得很合理,而且模板代码更简洁明了了。

下一步就是将controller里的代码理一理。因为controller只能算是个中介,不应该参与很多的逻辑处理。
# app/models/person.rb
class Person < ActiveRecord::Base
    def self.find_recent
        people = find(
            :conditions => ["added_at > ? and deleted = ?", Time.now.utc, false],
            :order => "last_name, first_name")
        people.reject { |p| p.address.nil? }
    end

    # ...
end

# app/controllers/people_controller.rb
class PeopleController < ActionController::Base
    def index
        @people = Person.find_recent
    end
end

现在看index这个action,扫一眼就知道它要干吗。而且如果find_recent方法在以后需要改变时,可以直接在model里进行修改。

--总结一下,尽量使得controller的actions中的代码和view中的代码更少,在action中要是能只写一行达到效果最好。在view中要尽量使代码贴近html结构。

还有一个不太明显的好处就是,瘦action可以使得 respond_to 结构更突出,可以看出输出的类型是什么。
class PeopleController < ActionController::Base
    def index
        @people = Person.find_recent

        respond_to do |format|
            format.html
            format.xml { render :xml => @people.to_xml(:root => "people") }
            format.rss { render :action => "index.rxml" }
        end
    end
end
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics