Rails Rails 4 会带来什么

knwang · 2012年09月21日 · 最后由 knwang 回复于 2012年10月09日 · 6138 次阅读

https://speakerdeck.com/u/bostonrb/p/what-to-expect-in-rails-4-dot-0

大概看下来,大的亮点是 Russian Doll Caching 和 ActiveSupport::Queue。也许是因为早就知道这两个会有,所以没觉得怎么激动,作为一个主要版本变化不算太大。有些惊讶 pjax 没有被包括

水果 5 没让我激动起来,rails4 估计也不行。

没有太大的改变,某种意义上来说,也是框架比较成熟了,便于学习和推广。

#3 楼 @hooopo
非常赞成 Jeff 说的 把 ORM 层和业务逻辑分开,这个已经是 Rails 圈子里面过去一两年来来回回讨论的了

#3 楼 @hooopo 啊 model 要分离成数据持久和业务逻辑两个部分???

貌似现在的问题是 AR 太复杂,一时半会儿分离不了。。。

#4 楼 @knwang #3 楼 @hooopo

印象中 mvc 中 Model 的定义就是『包含了业务规则的 data store』来着...

支持分离的理由主要有哪些呢?

#4 楼 @knwang 希望 Rails 能把这事搞定吧

现在 Django 分得很坑爹,搞了个半调子的 Form,弄得你用也不是不用也不是。

比如就像 GitHub 那样,每个用户的自己的仓库是不能重名的。

Django 里面你在 model 里定义unique_together = [('user', 'repository_name')]

接着,因为 user 是从 session 那里拿到的,你用 Django 自带的 ModelForm 生成 Form 的时候,会去 exclude user 这个 column。(这个是文档里提到的做法,https://docs.djangoproject.com/en/dev/topics/forms/modelforms/#using-a-subset-of-fields-on-the-form)

问题就来了,Django 的validate_unique的时候,因为 user 被 exclude 了,unique_together 里面,('user', 'repository_name')这条就直接就无视了。

据我所知 Django 到现在也没一个标准做法来处理这个问题。这个问题也不是一天两天了,是 4 年多了,这个问题一直没解决。

https://code.djangoproject.com/ticket/8795#comment:11 https://code.djangoproject.com/ticket/12028 https://code.djangoproject.com/ticket/12521 https://code.djangoproject.com/ticket/12901 https://code.djangoproject.com/ticket/13091 https://code.djangoproject.com/ticket/13249 https://code.djangoproject.com/ticket/15326

当然了,我承认在 Ruby 社区吐槽 Django 是很不好的行为。只是看到这个话题,联想到 Django 有个坑爹的问题,就顺便吐槽一下了。

#7 楼 @fleuria

Active Record 是一种 ORM 的模式,简单地说就是每一个业务模型的 object 对应一个数据库表的一个纪录。这在小的应用里面是很方便的。但一旦业务规则复杂了,很多业务模型并不是都对应数据库表的。比如在你的应用里需要有“VIP 用户特权“这样一个概念,包含比如”免运费“, ”促销商品再打九折“, ”自动短信提醒“等等。这东西并不对应数据库,因为是一些”规则“而不是具体的数据。那很多就会把这些散落在各种不同的 Model 比如"Shipping", "Promotion", "Reminder" 里面,或者做成 Call Back, 或者每个地方加上“if current_user.type == 'vip' "这样的判断。这样当你的业务规则增多的时候,你的代码会是一团乱麻,最明显的就是遍地像这样的条件判断(顺手写的)。。

if current_user.type == 'vip' 
   cart.shipping_cost = 0
   if current_user.cart.amount > 50 
      if current_user.gender == 'female'
        Gift.create(user: current_user, type: 'flower')
      else
        Gift.create(user: current_user, type: 'chocolate')
      end
   else
      ....
   end
else 
    if current_user.benefits.include?("free shipping")
      cart.shipping_cost = 0
    else
       ...
     end
end 

这样的逻辑散落在你系统各处。。能不能可持续的开发是一目了然的。。如果一旦上面穿西装的人说,业务规则全变了!想想后果把

#5 楼 @fresh_fish #6 楼 @fredwu #8 楼 @bhuztez

Rails 4 不会把这个分开,我觉得 Rails 不会从框架层面做这个决定,而是会吧这个决定留给开发员做。现在有 gem来做这个,但我觉得更重要的是开发的时候要注意这样的情况,该剥离的提早剥离成 Domain Model (不从 ActiveRecord::Base 继承的)

#4 楼 @knwang 参与过一个项目就全部把业务逻辑单独封装了,ORM 只用来保存和查询数据。感觉还不错,就是代码上多了些,业务清楚

#10 楼 @knwang 看那样子就变成 data mapper 了。但我没发现你举的这个例子,在两个 pattern 里有啥区别

http://martinfowler.com/eaaCatalog/activeRecord.html http://martinfowler.com/eaaCatalog/dataMapper.html

匿名 #14 2012年09月21日

#4 楼 @knwang 业务逻辑和 Model 分开的话,就变成 Java 了

#1 楼 @hhuaitest 这俩东西有啥关系吗?

#14 楼 @yggg 参照 java,加个 service 层,也未尝不可。业务逻辑全放到 model 里很容易一团糟。

@knwang 感觉 strong parameters 是个好东西呀,这些验证就应该放到最开始处。否则前面处理了一堆东西,后来保存时发现某个字段非法,前面做的处理就白费了。

#14 楼 @yggg 不是把业务逻辑和模型分开,而是把业务模型和持久化独立开,不过我觉的 rails4 对这个问题也还是没想清楚

#15 楼 @camel 虽然验证应该在越前面越好,但像 Django 现在那样,还不如不分开的 Rails 3。我不觉得现在的strong parameters已经完全解决了类似的问题。验证相关的代码分在两个地方,代码看起来麻烦啊。

#9 楼 @knwang 确实。。还真没考虑过这个。。可能做的都是小东西吧

#9 楼 @knwang 了解了,多谢讲解 :)

#16 楼 @fsword 业务模型很难和持久化独立开的,当你需要 JOIN 的时候,你还是需要知道数据库里是哪张表的。我不觉得,所有需要 JOIN 的查询都会放在业务模型的那个 class 下面。

#20 楼 @bhuztez 这就是我说 rails4 也还没想清楚的地方,而且我也没见过哪个框架是想清楚了的,事实上我怀疑这个问题能否想清楚,屏蔽掉持久层的想法很吸引人,但这恐怕和“透明的分布式处理”一样,只是看起来很美

#13 楼 @bhuztez 我个人确实喜欢 data mapper 多过 Active Record.

#15 楼 @camel Strong Parameters 是好东西但不是创新性

#20 楼 @bhuztez 请用代码举个例子

这个夏天好长,之前不是放出风说夏天可以发布吗。

#9 楼 @knwang #5 楼 @fresh_fish #6 楼 @fredwu #8 楼 @bhuztez

@knwang的代码例子可以改一下,我也随手写的

class User
  def free_shipping?
    vip? or benefits.include?("free shipping")
  end
end

class Cart
  def initialize(user)
    @user = user
  end

  def with_gift?
    amount > 50
  end

  def shipping_cost
    actual_cost = 5
    @user.free_shipping? ? 0 : actual_cost
  end

  def add
    ...
  end

  def checkout
    ...
      send_gift if with_gift?
  end

  def send_gift
    add Gift.create_by_user(@user)
  end
end

class Gift
  class << self
    def create_by_user(user)
      gift = Gift.new(user: user)
      gift.type = user.female? ? 'flower' : 'chocolate'
      gift.save
    end
  end
end

这个改写后的例子其实证明了把业务逻辑放入 ActiveRecord 的好处,ActiveRecord 里边可以有相关业务逻辑,但如果太复杂,一定是架构设计的不良,当然,完全可以建立跟持久化无关的 Model 来解决问题,比如 Cart 在任何教程里都是经典的非持久化 model,但是,一定不要在框架层面上鼓励将业务逻辑从 ActiveRecord 剥离出去

我赞成现有的方案,更简单清晰,对于 rails 这种约定大于配置的框架来说,很重要的一个原则,其实就是每次的约定都要谨慎

#27 楼 @lilu 业务逻辑什么时候剥离,剥离多少是 Ruby Geek 们津津乐跳的坑啊。如果大家闲着无聊可以看看这个讨论 - 注:新手同学们如果想 Follow 些圈内有影响的人,在这个里面随便点几个几乎不会有错

https://gist.github.com/2838490

需要 登录 后方可回复, 如果你还没有账号请 注册新账号