首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在Ruby中验证IBAN

在Ruby中验证IBAN
EN

Code Review用户
提问于 2018-03-21 19:06:38
回答 2查看 515关注 0票数 3

寻找关于如何改进我的代码和领域的技巧,我可以更好地遵循面向对象的原则。这个班结构可以吗?代码的总体情况如何?

问题:有两个IBAN标准: IBAN-10和IBAN-13.IBAN-10是由9个数字加上一个支票数字组成的.IBAN-13是由12位数字加上一个支票数字组成的。空格和连字符可能包含在代码中,但并不重要。

IBAN-10的校验是通过将每一位数乘以其位置,将这些积加在一起并取结果的模数11来计算的。如果结果是10,则替换X。

IBAN-13的校验数字是通过将每一个数字交替乘以1或3,将这些乘积相加,取结果的模10并从10中减去这个值来计算的。然后用这个结果取10的模,从而将这个数降为一位数。

如果是有效的IBAN-10/IBAN-13,则返回true。

代码语言:javascript
复制
class IBAN

  def initialize(number)
    puts "number: #{number}"

    @number = number
    @number.gsub!(/[^0-9]/i, '')
    return @number
  end

  def number
    @number
  end

  # # check IBAN length and if valid proceed to process IBAN type
  def check_number_format
    iban_length = self.number.length

    unless iban_length == 10 || iban_length == 13
      puts "Not a valid IBAN - incorrect length"
      return false
    else
      self.validate_iban_type
    end
  end

  def validate_iban_type
    case self.number.length
    when 10
      @iban_ten = IBANTEN.new(self.number)
      @iban_ten.process_ten_digit_iban
    when 13
      @iban_thirteen = IBANTHIRTEEN.new(self.number)
      @iban_thirteen.process_thirteen_digit_iban
    else
      return false
    end
  end
end


class IBANTEN

  def initialize(iban_number)
    @iban_number = iban_number
  end

  def iban_number
    @iban_number
  end

  def process_ten_digit_iban
    number_split = self.iban_number.split('')
    final_digit = number_split[-1]
    running_total = 0

    number_split[0, (number_split.length - 1)].each_with_index do |num, index|
      # multiply each digit by its position
      sum = num.to_i * (index.to_i + 1)
      running_total += sum.to_i
    end
    # get the modulus of the running total if it exuals 10 replace it with X otherwise return the result
    digit_sum_total = running_total % 11
    calculated_result = digit_sum_total == 10 ? "X" : digit_sum_total

    if calculated_result == final_digit.to_i
      puts "Valid IBAN-10 Number"
      return true
    else
      puts "Invalid IBAN-10 Number"
      return false
    end
  end

end


class IBANTHIRTEEN

  def initialize(iban_number)
    @iban_number = iban_number
  end

  def iban_number
    @iban_number
  end

  def process_thirteen_digit_iban
    number_split = self.iban_number.split('')
    final_digit = number_split[-1]
    running_total = 0

    number_split[0, (number_split.length - 1)].each_with_index do |num, index|
      # if the remainder equals zero multiply by 1 otherwise multiplu by 3
      sum = (index % 2 == 0 ) ? num.to_i * 1 : num.to_i * 3
      running_total += sum.to_i
    end
    # modulo 10 of the result and subtracting this value from 10, and then taking the modulo 10 of the result again to produce a single digit
    calculated_result = ( (10 - (running_total % 10) ) % 10)

    if calculated_result == final_digit.to_i
      puts "Valid IBAN-13 Number"
      return true
    else
      puts "Invalid IBAN-13 Number"
      return false
    end
  end
end


@iban_v13_valid = IBAN.new("978 0 471 48648 0")
@iban_v13_valid.check_number_format

@iban_v13_invalid = IBAN.new("9780470059021")
@iban_v13_valid.check_number_format

@iban_v10_valid = IBAN.new("0 471 60695 2")
@iban_v10_valid.check_number_format

@iban_v10_invalid = IBAN.new("0-470-84525-6")
@iban_v10_invalid.check_number_format


@iban_invalid_length = IBAN.new("0-470-84525-618423")
@iban_invalid_length.check_number_format
EN

回答 2

Code Review用户

回答已采纳

发布于 2018-03-23 16:24:56

假设你喜欢使用3个不同的类;

  • 用鲁波克宝石。
    • 更喜欢警卫条款。
    • 更喜欢单引号而不是双引号。
    • 当不需要实例变量时,更倾向于局部变量。
    • 不需要显式返回。

  • 理解gsub!gsub之间的区别。
  • 不需要含蓄地写自我。
  • 更喜欢attr_而不是编写方法。
  • 就个人而言,不太喜欢使用初始化器来修改数据。如果你这么做了,就尽量少做。
  • 使用更短的方法。阅读单一责任原则(SRP)。
  • 使用简单的变量名。
  • 没有重构长的方法,因为它可以依赖于个人引用。然而:
    • 您可以提取#process_ten_digit_iban#process_thirteen_digit_iban,因为它们非常相似。
    • 您可以在if条件下提取puts valid/invalid以保持代码的干爽。

代码语言:javascript
复制
class IBAN
  REQUIRED_LENGTH = [10, 13].freeze

  attr_reader :number

  def initialize(number)
    @number = number.gsub(/[^0-9]/i, '')
  end

  def validate_iban_type
    return if check_number_format?

    case number.length
    when 10
      IBANTEN.new(number).process_ten_digit_iban
    when 13
      IBANTHIRTEEN.new(number).process_thirteen_digit_iban
    else
      false
    end
  end

  # check IBAN length and if valid proceed to process IBAN type
  def check_number_format?
    iban_length = number.length
    raise 'Incorrect length' unless REQUIRED_LENGTH.include?(iban_length)
  end
end

class IBANTEN
  attr_reader :iban_number

  def initialize(iban_number)
    @iban_number = iban_number
  end

  def process_ten_digit_iban
    numbers_array = iban_number.split('')
    total = 0

    numbers_array[0, (numbers_array.length - 1)].each_with_index do |num, index|
      # multiply each digit by its position
      sum = num.to_i * (index.to_i + 1)
      total += sum
    end

    # get the modulus of the running total if it exuals 10 replace it with X
    # otherwise return the result
    digit_sum_total = total % 11
    result = digit_sum_total == 10 ? 'X' : digit_sum_total

    if result == numbers_array[-1].to_i
      puts 'Valid IBAN-10 Number'
      true
    else
      puts 'Invalid IBAN-10 Number'
      false
    end
  end
end

class IBANTHIRTEEN
  attr_reader :iban_number

  def initialize(iban_number)
    @iban_number = iban_number
  end

  def process_thirteen_digit_iban
    numbers_array = iban_number.split('')
    total = 0

    numbers_array[0, (numbers_array.length - 1)].each_with_index do |num, index|
      # if the remainder equals zero multiply by 1 otherwise multiply by 3
      sum = index.even? ? num.to_i * 1 : num.to_i * 3
      total += sum
    end

    # modulo 10 of the result and subtracting this value from 10, and then
    # taking the modulo 10 of the result again to produce a single digit
    result = ((10 - (total % 10)) % 10)

    if result == numbers_array[-1].to_i
      puts 'Valid IBAN-13 Number'
      true
    else
      puts 'Invalid IBAN-13 Number'
      false
    end
  end
end

iban_v13_valid = IBAN.new('978 0 471 48648 0')
iban_v13_valid.validate_iban_type

iban_v13_invalid = IBAN.new('9780470059021')
iban_v13_invalid.validate_iban_type

iban_v10_valid = IBAN.new('0 471 60695 2')
iban_v10_valid.validate_iban_type

iban_v10_invalid = IBAN.new('0-470-84525-6')
iban_v10_invalid.validate_iban_type

iban_invalid_length = IBAN.new('0-470-84525-618423')
iban_invalid_length.validate_iban_type
票数 1
EN

Code Review用户

发布于 2018-04-01 14:07:46

我希望为这三种情况实现一个具有不同实现的结构:

  1. IBAN13
  2. IBAN10
  3. 上述任何一种

您可以提供这样的内容,其中IBAN#new被重写以检测提供的整数数量,并初始化正确的对象来处理它。

这里真正的诀窍是提供Invalid模块,它响应您期望ThirteenTen对象响应的所有东西,在类似于Nil对象模式的实现中--在本例中是无效对象。

我忽略了#valid? etc类的实现,但将指向Lisbn gem,在这个gem中,我们实现了一个非常快速的算法https://github.com/ragalie/lisbn/blob/master/lib/lisbn/lisbn.rb#L111。基于可枚举方法的基准测试通常看起来相当优雅,该算法比在某些ISBN gems中实现的相同逻辑快40倍。

下面是代码大纲:

代码语言:javascript
复制
module IBAN
  def self.new(string)
    numeric = string.gsub(/[^0-9]/,"")
    case numeric.size
    when 13
      then Thirteen.new(string)
    when 10
      then Ten.new(string)
    else
      Invalid
    end
  end

  class Thirteen
    def initialize(number)
      @number = number
    end

    def checksum
      # TODO
    end

    def valid?
      # TODO
    end

    attr_reader :number
  end

  class Ten
    def initialize(number)
      @number = number
    end

    def checksum
      # TODO
    end

    def valid?
      # TODO
    end

    attr_reader :number
  end

  module Invalid
    def self.valid?
      false
    end

    def self.checksum
      nil
    end
  end
end

ps。您可能更喜欢将Invalid实现为单例而不是模块。

代码语言:javascript
复制
    ...
    else
      Invalid.instance
    end
    ...

  class Invalid
    include Singleton

    def valid?
      false
    end

    def checksum
      nil
    end
  end
end
票数 2
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/190139

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档