减少两个类之间不必要的重复

时间:2016-01-25 22:54:47

标签: ruby-on-rails ruby refactoring code-duplication

我有两个负责属性验证的类:

it.skip('login (return photo)', function(done) { ...

和第二个

class NameValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    message = options.fetch(:message, I18n.t('errors.attributes.name.invalid'))
    record.errors[attribute] << message unless NameValidator.valid_name?(value)
  end

  def self.valid_name?(name)
    name =~ /\A[a-z][\w\p{Blank}]+\z/i
  end
end

它们基本相同。我应该使用受保护的实用程序方法从一个类继承它们还是什么?

3 个答案:

答案 0 :(得分:1)

仅当一个类显然是另一个类的特殊情况时才使用继承。在您的示例中,似乎两个类是相同的。然后,使用mixin,而不是继承。

代码中与次要validate_each相悖的一个小点是NameValidator.valid_name?EmailValidator.valid_email?的硬编码。您需要在两个类中使用的公共代码中使它们相同。首先,您不需要提供不同的名称valid_name?valid_email?。他们的差异应该通过使用各自的类来吸收。使用通用名称。其次,您不需要对接收器进行硬编码。相反,请使用self.class。但是,不要使用类方法,而是使用实例方法。

module ValidatorModule
  def validate_each(record, attribute, value)
    message = options.fetch(:message, I18n.t("errors.attributes.#{attribute}.invalid"))
    record.errors[attribute] << message unless valid?(value)
  end
end

class NameValidator < ActiveModel::EachValidator
  include ValidatorModule
  def attribute; "name" end
  def valid?(value); value =~ /\A[a-z][\w\p{Blank}]+\z/i end
end

class EmailValidator < ActiveModel::EachValidator
  include ValidatorModule
  def attribute; "email" end
  def valid?(value); value =~ /\A.+@.+\..+\z/i end
end

如果您认为验证始终使用单个正则表达式完成,则可以更进一步:

module ValidatorModule
  def validate_each(record, attribute, value)
    message = options.fetch(:message, I18n.t("errors.attributes.#{attribute}.invalid"))
    record.errors[attribute] << message unless value =~ validation_pattern
  end
end

class NameValidator < ActiveModel::EachValidator
  include ValidatorModule
  def attribute; "name" end
  def validation_pattern; /\A[a-z][\w\p{Blank}]+\z/i end
end

class EmailValidator < ActiveModel::EachValidator
  include ValidatorModule
  def attribute; "email" end
  def validation_pattern; /\A.+@.+\..+\z/i end
end

答案 1 :(得分:1)

您可以进一步简化

class PatternValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    message = options.fetch(:message) || kind
    record.errors[attribute] << message unless value =~ validation_pattern
  end
end

class NameValidator < PatternValidator
  def validation_pattern; /\A[a-z][\w\p{Blank}]+\z/i end
end

class EmailValidator < PatternValidator
  def validation_pattern; /\A.+@.+\..+\z/i end
end

EachValidator有一个#kind方法,因此它会添加:name或:email作为失败消息,除非被覆盖。然后你可以让i18n根据rails guide中记录的标准级联进行查找。

答案 2 :(得分:0)

为清晰起见,请将它们分开。这些方法足够小,抽象的混淆会使得它更多,而不是更少,显而易见。

如果你开始有3,4,5,6或更多类似的验证器,这种模式开始显而易见,添加抽象可能会使它更容易理解,更改,依赖或删除。