【问题标题】:Law of Demeter - how far do you go?得墨忒耳法则——你能走多远?
【发布时间】:2011-05-23 22:58:11
【问题描述】:

我想遵循得墨忒耳法则。当我通过我的代码搜索“两个点”时,我发现自己在问是否真的值得在这种类型的上下文中设置委派职责:

来自

class Activity
  def increment_user_points!
    self.user.increment_points!(points, true)
  end
end

module UserDelegator
  def user_increment_points!(points, increment_credits=false)
    self.user.increment_points!(points, increment_credits)
  end
end

class Activity
  include UserDelegator

  def increment_user_points!
    user_increment_points!(points, true)
  end
end

你有什么想法?

【问题讨论】:

  • 这里有两个点的原因是self 明确声明为接收者。你可以摆脱它,因为self 是默认接收者。

标签: ruby-on-rails law-of-demeter


【解决方案1】:

你的例子没有违反得墨忒耳法则。用户是活动的一个属性,并且您正在访问该用户的公共 API 方法,因此您对原始实现没有错误。

得墨忒耳法则的目标是避免破坏对象封装。您的“两点”方法有点过于简化了这个想法。实际上,您应该检查您的对象是如何交互的,并确保您没有对其他对象的属性或关系了解太多。例如,如果以下行为违反规则:

def update_user_address
  @user.address.set(new_address)
end

这是因为地址是用户的业务,它应该通过用户的API适当地封装对它的访问。作为用户的客户端,我们不应该直接访问用户属性的 API。

同样,在您的示例中,您直接使用用户 API,这很好,并且没有违反 Demeters Law。综上所述,我发现一般规则是一个很好的遵循。如果您避免破坏所示的对象封装,您的代码通常会更容易更改和维护,并且类会更容易重构。

【讨论】:

    【解决方案2】:

    我实际上希望 TO 看起来更像这样:

    class User
      def increment_points!(points, increment_credits)
        @points+=points if increment_credits
        @points-=points unless increment_credits
      end
    end
    
    class Activity
      def increment_user_points!
        @user.increment_points!(points, true)
      end
    end
    

    include 创建一个模块似乎会增加更多的复杂性。德墨忒耳法则的全部意义(我更喜欢将其视为指导方针..)是您应该能够对User 的内部进行任何您喜欢的操作,而无需在外部重写大量代码班级。你的UserDelegator 模块没有多大帮助——当你摆弄User 的内部结构时,你仍然可以重新编写代码。

    但如果是我,我想我什至不会为此烦恼,除非你发现自己要重写 很多 代码来对 User 进行简单的更改。也许这只是因为我习惯了 Linux 内核编码风格,这种风格经常违反 Demeter 法则:

    static inline int need_reval_dot(struct dentry *dentry)
    {
        if (likely(!(dentry->d_flags & DCACHE_OP_REVALIDATE)))
            return 0;
    
        if (likely(!(dentry->d_sb->s_type->fs_flags & FS_REVAL_DOT)))
            return 0;
    
        return 1;
    }
    

    三个对象之外 :) 我不确定如果编写代码会更清晰:

    need_reval_dot(dentry) {
        if(likely(!dentry_need_reval_dot(dentry))
            return 0;
    }
    
    dentry_need_reval_dot(dentry) {
        return superblock_need_reval_dot(dentry->d_sb);
    }
    
    superblock_need_reval_dot(sb) {
        return fs_type_need_reval_dot(sb->s_type);
    }
    
    fs_type_need_reval_dot(s_type) {
        return fs_flags_need_reval_dot(s_type->fs_flags);
    }
    
    fs_flags_need_reval_dot(fs_flags) {
        return fs_flags & FS_REVAL_DOT;
    }
    

    所以我都赞成适度地遵循指导方针——问问自己,你的修改是否真的会导致更清晰、更易于维护的代码,或者它是否只是为了遵循规则而遵循规则。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-06-25
      • 1970-01-01
      • 1970-01-01
      • 2012-08-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多