玩转评分星级

作者: Paul King
发布时间: 2023-01-25 12:00AM


星级评分

最近的一条推文展示了一些 C# 代码,用于生成一个星号字符串,该字符串可能用于在网站上显示评分。

Image of original tweet

让我们看看在 Groovy 中用几种方法来做同样的事情。

def rating0(percentage) {
    int stars = Math.ceil(percentage * 10)
    ("🔵" * stars).padRight(10, "⚪")
}
def rating1(percentage) {
    int stars = Math.ceil(percentage * 10)
    "🔵" * stars + "⚪" * (10-stars)
}
def rating2(percentage) {
    int skip = 10 - Math.ceil(percentage * 10)
    "🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵⚪⚪⚪⚪⚪⚪⚪⚪⚪⚪"[skip..<10+skip]
}
def rating3(percentage) {
    switch(percentage) {
        case 0             -> "⚪⚪⚪⚪⚪⚪⚪⚪⚪⚪"
        case { it <= 0.1 } -> "🔵⚪⚪⚪⚪⚪⚪⚪⚪⚪"
        case { it <= 0.2 } -> "🔵🔵⚪⚪⚪⚪⚪⚪⚪⚪"
        case { it <= 0.3 } -> "🔵🔵🔵⚪⚪⚪⚪⚪⚪⚪"
        case { it <= 0.4 } -> "🔵🔵🔵🔵⚪⚪⚪⚪⚪⚪"
        case { it <= 0.5 } -> "🔵🔵🔵🔵🔵⚪⚪⚪⚪⚪"
        case { it <= 0.6 } -> "🔵🔵🔵🔵🔵🔵⚪⚪⚪⚪"
        case { it <= 0.7 } -> "🔵🔵🔵🔵🔵🔵🔵⚪⚪⚪"
        case { it <= 0.8 } -> "🔵🔵🔵🔵🔵🔵🔵🔵⚪⚪"
        case { it <= 0.9 } -> "🔵🔵🔵🔵🔵🔵🔵🔵🔵⚪"
        default            -> "🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵"
    }
}

如果你愿意,可以测试各种边缘情况。

for (i in 0..3)
    for (j in [0, 0.09, 0.1, 0.11, 0.9, 1])
        println "rating$i"(j)

提高健壮性

这里的代码示例假设输入在 0 <= percentage <= 1 范围内。我们可以做一些调整来防止输入超出这些范围。

我们可以简单地添加一个断言,例如:

def rating0b(percentage) {
    assert percentage >= 0 && percentage <= 1
    int stars = Math.ceil(percentage * 10)
    ("🔵" * stars).padRight(10, "⚪")
}

或者,如果我们不想失败,可以调整一些条件,例如,对于 rating3,不是 case 0,我们可以使用 case { it ⇐ 0 }

我们可以通过创建一个特殊的类来将检查推入我们的类型,例如 Percent,该类只允许允许的值。

final class Percent {
    final Double value
    Percent(Double value) {
        assert value >= 0 && value <= 1
        this.value = value
    }
}

我们还可以选择使用一些元编程来提供一个自定义的 isCase 方法。

Double.metaClass.isCase = { Percent p -> delegate >= p.value }

这意味着我们可以调整评分方法为:

def rating3b(Percent p) {
    switch(p) {
        case 0.0d -> "⚪⚪⚪⚪⚪⚪⚪⚪⚪⚪"
        case 0.1d -> "🔵⚪⚪⚪⚪⚪⚪⚪⚪⚪"
        case 0.2d -> "🔵🔵⚪⚪⚪⚪⚪⚪⚪⚪"
        case 0.3d -> "🔵🔵🔵⚪⚪⚪⚪⚪⚪⚪"
        case 0.4d -> "🔵🔵🔵🔵⚪⚪⚪⚪⚪⚪"
        case 0.5d -> "🔵🔵🔵🔵🔵⚪⚪⚪⚪⚪"
        case 0.6d -> "🔵🔵🔵🔵🔵🔵⚪⚪⚪⚪"
        case 0.7d -> "🔵🔵🔵🔵🔵🔵🔵⚪⚪⚪"
        case 0.8d -> "🔵🔵🔵🔵🔵🔵🔵🔵⚪⚪"
        case 0.9d -> "🔵🔵🔵🔵🔵🔵🔵🔵🔵⚪"
        default   -> "🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵"
    }
}

在这里,我们可以使用 Percent 类上的 @EqualsAndHashCode 和/或使用 value 属性上的 @Delegate 来更复杂一些,这取决于我们希望 Percent 实例的功能有多丰富。

或者,我们可以使用契约式设计方法。

@Requires({ percentage >= 0 && percentage <= 1 })
def rating1b(percentage) {
    int stars = Math.ceil(percentage * 10)
    "🔵" * stars + "⚪" * (10-stars)
}

参考资料