重构出更加swifty的代码

原文
不小心在推特上浏览到一个文章,很好的诠释了什么是swifty的swift代码。
如何检测一个信用卡类型呢,参数如下表格所示(主要的4个,还有各种乱七八糟的)

Issuing Network IIN ranges Card Number Length
American Express 34, 37 15
MasterCard 51–55,2221–2720 16
Visa 4 13, 16, 19
Discover 65,6011,644–649,622126–622925 16, 19

Option 1: if大法

先来试试最粗暴的算法:

如果是4开头就是visa,如果是3开头则可能是Amex, Diners, or JCB,如果是6开头则可能是Discover

这里有段现成的代码 PaymentKit,翻译成swift大概就是这样

// ...
// Discover
if cardString.hasPrefix("6011") || cardString.hasPrefix("65") {
    return .discover
}
else if cardString.hasPrefix("622") {
    // If the number has a prefix in the range 622126-622925, it's Discover
    let prefixLength = 6;
    if (cardString.characters.count >= prefixLength) {
        let prefixIndex = number.index(cardString.startIndex, offsetBy: prefixLength)
        let sixDigitPrefixString = cardString.substring(to: prefixIndex)
        let sixDigitPrefixInt = Int(sixDigitPrefixString)!
        if sixDigitPrefixInt >= 622126 && sixDigitPrefixInt <= 622925 {
            return .discover;
        }
    }
    return .unknown
}
else if cardString.hasPrefix("64") { ... }
// ...

这特么是什么鬼代码,别人维护起来得多痛苦,尤其是银行玩点幺蛾子多改下的时候,修改这些代码简直是噩梦

Option 2:正则大法

现在我们用正则来替代if大法

//amex starts with 34 or 37 and is 15 digits
case .amex:
    return "^3[47][0-9]{13}$"

很cooool 很简洁是吧,现在来看看.masterCard:

//MasterCard starts with 51 through 55 
//or 2221 through 2720. All have 16 digits.
case .masterCard:
   return "^(?:5[1-5][0-9]{2}|" +
            "222[1-9]|" +
            "22[3-9][0-9]|" +
            "2[3-6][0-9]{2}|" +
            "27[01][0-9]|" +
            "2720)" +
            "[0-9]{12}$"
...

黑人问号???
正则表达式是个很强大的工具,但是正则在表达式在表达数值范围时候就显得不那么优雅了,更别说是这种可变长度的数字,而且还很不好懂,这也pass。

一个更简洁,具有变现力点代码:

我们用的是swift,肯定有一种更好的方式来处理类似的情况,先别换编译器的嘲笑,我们先写点伪代码,

case .amex:         prefix = ["34", "37"]
                    length = [15]
 
case .diners:       prefix = ["300"..."305", "309", "36", "38"..."39"]
                    length = [14]
 
case .discover:     prefix = ["6011", "65", "644"..."649", "622126"..."622925"]
                    length = [16]
 
case .jcb:          prefix = ["3528"..."3589"]
                    length = [16]
 
case .masterCard:   prefix = ["51"..."55", "2221"..."2720"]
                    length = [16]
 
case .visa:         prefix = ["4"]
                    length = [13, 16, 19]

现在判断的规则是不是变的相当的简洁明了,如何做到呢,你可以先看代码 CardParser.swift.

强大的ENUM:

CardType是swift的中枚举类型,swift枚举是个强大的武器不但可以进行模式匹配还可以验证规则:

enum CardType {
    case visa
    case masterCard
    ...
 
    var segmentGroupings: [Int] {...}
    var cvvLength: Int {...}
    ...
 
    func isValid(_ accountNumber: String) -> Bool {...}
    func isPrefixValid(_ accountNumber: String) -> Bool {...}
}

visa.cvvLength          // 3
visa.isPrefixValid("4") // true
visa.isValid("4")       // false

强大的协议

待续

你可能感兴趣的:(重构出更加swifty的代码)