4

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

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

你觉得呢?你有没有什么想法?

4

2 回答 2

2

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

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

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

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

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

于 2011-05-24T01:07:13.033 回答
1

I'd actually expect TO to look more like this:

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

Creating a module to include would seem to create more complexity. And the whole point of the Law of Demeter (I like to think of it more as a guideline..) is that you ought to be able to do whatever you like to User's internals without having to rewrite much code outside the class. Your UserDelegator module doesn't help much -- you still get to re-write that code when you fiddle with User's internals.

But if it were me, I don't think I'd even bother with this, unless you're finding yourself rewriting a lot of code to make simple changes to User. Maybe that's just because I'm used to the Linux kernel coding style, which breaks the law of Demeter on a regular basis:

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;
}

Three objects away :) and I'm not sure the code would be more legible if written:

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;
}

So I'm all in favor of following guidelines in moderation -- ask yourself if your modifications actually lead to cleaner, more maintainable code, or if it is just following a rule for the sake of following rules.

于 2011-05-23T23:19:02.837 回答