Закон Деметры - как далеко вы идете? - PullRequest
4 голосов
/ 24 мая 2011

Я хочу следовать Закону Деметры. Проходя по коду в поисках «двух точек», я спрашиваю себя, действительно ли стоит устанавливать обязанности делегирования в этом контексте:

FROM

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

Что ты думаешь?

Ответы [ 2 ]

2 голосов
/ 24 мая 2011

Ваш пример не нарушает Закон Деметры.Пользователь является атрибутом действия, и вы обращаетесь к общедоступному методу API для пользователя, поэтому вы не ошиблись с исходной реализацией.

Цель закона Деметры - избежать разрушения инкапсуляции объекта.Ваш подход "две точки" является несколько упрощением идеи.В действительности вы должны проверить, как взаимодействуют ваши объекты, и убедиться, что вы не слишком углубляетесь в атрибуты или взаимосвязи других объектов.Например, если следующим правилом будет нарушение правила:

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

Это будет связано с тем, что адрес является бизнесом пользователя, и он должен инкапсулировать доступ к нему черезAPI пользователя.Как клиент пользователя, мы никогда не должны иметь прямого доступа к API атрибутов пользователей.

Опять же, в вашем примере вы используете API пользователей напрямую, что хорошо и не нарушает Закон о параметрах.После всего сказанного я нашел общее правило, которому нужно следовать.Ваш код, как правило, будет легче изменять и поддерживать, а классы будет легче реорганизовать, если вы избежите нарушения инкапсуляции объектов, как показано.

1 голос
/ 24 мая 2011

Я бы на самом деле ожидал, что 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, который регулярно нарушает закон Деметры:

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

Таким образом, я все в пользу следования рекомендациям по модерации - спросите себя, действительно ли ваши изменения приводят к более чистому, более понятному коду или просто следуют правилу ради следующих правил.

...