rails中的law of demeter

作者:周星 发布:2014-09-08

       首先解释一下什么是 law of demeter,law of demeter 中文翻译为“迪米特法则”,属于设计模式之一,通俗的理解就是:一个对象模型只应该和它关联的对象“通信”,而不应该和关联的对象的关联对象“通信”,或者说一个对象和其他对象“联系”越少越好,它的目的是降低程序的耦合度。我们的rails 中经常会触碰到这一原则,举个例子:

class Blog < ActiveRecord::Base
  belongs_to  :user
end

<%= @blog.user.name %>
<%= @blog.user.address %>
<%= @blog.user.age %>

       上述代码在各种项目中随处可见,Blog模型调用了和它有关联的模型(user)的属性,但是它触碰了迪米特法则,幸运的是 Rails 提供了一个叫 delegate 的方法,它不仅可以降低耦合,而且通过设置 allow_nil: true,你甚至可以避免在 nil 上调用方法报错,下面我们看一下正确的例子:

class Blog < ActiveRecord::Base
  belongs_to  :user

  delegate :name, :address, :age,   to: :user, prefix: true
end

<%= @blog.user_name %>
<%= @blog.user_address %>
<%= @blog.user_age %>

delegate 的使用方法:

:to           # 指定目标对象
:prefix       # 在新方法上加前缀(下划线),前缀可以为小写的目标对象名,或者你也可以指定一个前缀 
:allow_nil    # 如果值为true, 则可以避免抛出一个 NoMethodError 异常 delegate 接收一个或者多个方法名(符号或者字符串),通过 :to 参数来指定目标对象

方法可以“委托”给实例变量、类变量、常量

class Foo
  CONSTANT_ARRAY = [0,1,2,3]
  @@class_array  = [4,5,6,7]

  def initialize
    @instance_array = [8,9,10,11]
  end
  delegate :sum, to: :CONSTANT_ARRAY
  delegate :min, to: :@@class_array
  delegate :max, to: :@instance_array
end

Foo.new.sum # => 6
Foo.new.min # => 4
Foo.new.max # => 11

通过:class可以把方法“委托”给一个类

class Foo
  def self.hello
    "world"
  end

  delegate :hello, to: :class
end

Foo.new.hello # => "world"

如果指定了prefix: true,则新的方法名前缀为 :to 指定的对象名,你也可以指定一个前缀名。

class Blog < ActiveRecord::Base
  belongs_to  :user

  delegate :name,   to: :user,            prefix: :custom
end

<%= @blog.custom_name %>
# 同样是 @blog.user.name

allow_nil: true 的作用在于:如果“委托”的目标不存在,而且不能 respond_to 这个方法,allow_nil: true 会返回 nil,如果不加则抛出 NoMethodError

class User < ActiveRecord::Base
  has_one :profile
  delegate :age, to: :profile
end

User.new.age # raises NoMethodError: undefined method `age'

class User < ActiveRecord::Base
  has_one :profile
  delegate :age, to: :profile, allow_nil: true
end

User.new.age # nil

注意:如果“委托”的目标存在,但是不能 respond_to 这个方法,还是会抛出 NoMethodError 异常。


如果您对本文有什么意见或建议,请联系博主

支付宝扫码赞助博主


评论(1)

lhy-fans

lhy-fans第1楼

我不是来坐沙发的,我是来学习来了。 blog.user.name 和blog.user_name,如果blog本身就有user_name方法,是否会有问题? 耦合是不能完全消除的,没有耦合的代码毫无关联,而且会增加代码的可阅读性

2014-09-16 06:11:05