这是全能机器人@Duga的代码的一部分(就是我!)。

每天晚上,在“重新加载”(UTC时间00:00)之前15分钟,我都会发布一些声誉差异代码审查用户。示例输出为:


Mat's Mug vs. Simon Forsberg:2728差异。年:+3618。季度:+885。月:+648。周:-40。日期:+185。


下面的代码使用以下类/接口:



StackExchangeAPI:包含用于进行Stack Exchange API请求。然后将结果解析为JSON并将其“抽取”并存储到Groovy对象中。

DugaBot:包含用于向聊天室发送消息的方法

WebhookParameters:有关在何处发送信息发送聊天消息(在哪个聊天室)

此类在启动时(或重新加载任务时)创建,然后通过Grails中的cron触发器在该类上调用run()

关于此代码的任何评论,请感激。

此代码也可在Github上使用:Zomis / Duga。

class UserRepDiffTask implements Runnable {

    private final StackExchangeAPI stackApi;
    private final DugaBot chatBot;
    private final String usersString;
    private final String site;
    private final WebhookParameters room;

    public UserRepDiffTask(StackExchangeAPI stackApi, String room, DugaBot chatBot, String users, String site) {
        this.stackApi = stackApi;
        this.chatBot = chatBot;
        this.usersString = users.replace(',', ';');
        this.room = WebhookParameters.toRoom(room);
        this.site = site;
    }

    @Override
    public void run() {
        try {
            def result = stackApi.apiCall("users/" + usersString, site, "!23IYXA.sS8.otifg5Aq.2");
            List users = result.items
            if (users.size() != 2) {
                throw new UnsupportedOperationException("Cannot check diff for anything other than two users");
            }

            def max = users.stream().max(Comparator.comparingInt({it.reputation})).get();
            def min = users.stream().min(Comparator.comparingInt({it.reputation})).get();
            StringBuilder str = new StringBuilder();
            str.append(clearName(max.display_name) + " vs. " + clearName(min.display_name) + ": ");
            str.append((int)max.reputation - (int)min.reputation);
            str.append(" diff. ");
            diffStr(str, max, min, "Year", {it.reputation_change_year});
            diffStr(str, max, min, "Quarter", {it.reputation_change_quarter});
            diffStr(str, max, min, "Month", {it.reputation_change_month});
            diffStr(str, max, min, "Week", {it.reputation_change_week});
            diffStr(str, max, min, "Day", {it.reputation_change_day});
            chatBot.postSingle(room, str.toString());

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static String chatName(String displayName) {
        return clearName(displayName).replace(" ", "");
    }

    public static String clearName(String displayName) {
        while (displayName.contains("&#")) {
            String replacement = displayName.substring(displayName.indexOf("&#") + 2);
            try {
                replacement = replacement.substring(0, replacement.indexOf(';'));
                int ch = Integer.parseInt(replacement);
                displayName = displayName.replaceFirst("&#\d+;", String.valueOf((char) ch));
            } catch (RuntimeException ex) {
                displayName = displayName.replaceFirst("&#", "");
            }
        }
        return displayName;
    }

    private void diffStr(StringBuilder str, max, min, String string, ToIntFunction<?> function) {
        str.append(string);
        str.append(": ");
        int maxValue = function.applyAsInt(max);
        int minValue = function.applyAsInt(min);
        int diff = maxValue - minValue;
        str.append(diff > 0 ? "+" : "");
        str.append(diff);
        str.append(". ");
    }

}


评论

第一步是摆脱分号!大声笑。认真地说,我想做一些实验。您可以在用户列表中发布数据示例(在run()方法中)吗?您可以通过打印/登录users.inspect()获得示例。

@EmmanuelRosa该数据可从Stack Exchange API获得,例如,请参见此API响应

#1 楼


根本没有使用静态chatName()方法。摆脱它,因为它只会增加噪音。

为什么使用静态clearName()方法public?使它成为private和非静态的,或将其放置在其他类中,该类有责任进行此类清洁以获得明确的名称。

或者要问不同,像在org.apache.commons.lang.StringEscapeUtils.unescapeHtml中一样使用AnswerInvalidationCheck有什么问题?


StringBuilder
IMO如果您“知道”或可以缩小StringBuilder的结果容量,则应使用重载的构造函数,该构造函数初始容量作为参数。
我喜欢StringBuillder的流利方法。 IMO利用流利的方式使阅读代码变得更加容易。
进行字符串连接以为append()方法构建参数是有点奇怪。您为什么不只使用append()方法?



一些垂直间距对于您的代码来说是很好的。它对相关代码进行分组,使其更具可读性。

def max = users.stream().max(Comparator.comparingInt({it.reputation})).get();
def min = users.stream().min(Comparator.comparingInt({it.reputation})).get();

StringBuilder str = new StringBuilder();
str.append(clearName(max.display_name) + " vs. " + clearName(min.display_name) + ": ");
str.append((int)max.reputation - (int)min.reputation);
str.append(" diff. ");

diffStr(str, max, min, "Year", {it.reputation_change_year});
diffStr(str, max, min, "Quarter", {it.reputation_change_quarter});
diffStr(str, max, min, "Month", {it.reputation_change_month});  




#2 楼

首先,我将摆脱所有分号。他们在这里根本没有帮助。

然后,我将为usersStringroom添加两个设置器,仅使用groovy提供的地图构造函数?您真的要阅读

this.a = a;
this.b = b;


您应该保留一些实际重要的内容。简单的作业?让groovy为您做到这一点。

而不是这个

users.stream().max(Comparator.comparingInt({it.reputation})).get();


为什么不只使用它呢?

users.*reputation.max()


它更具可读性。同样适用于min

为什么还要等待API调用完成才能在有两个以上用户的情况下引发异常?创建类时,当usersString具有两个以上用户ID时,只需停止整个操作即可。

而不是此操作

diffStr(str, max, min, "Year", {it.reputation_change_year});
diffStr(str, max, min, "Quarter", {it.reputation_change_quarter});
diffStr(str, max, min, "Month", {it.reputation_change_month});
diffStr(str, max, min, "Week", {it.reputation_change_week});
diffStr(str, max, min, "Day", {it.reputation_change_day});


这样做可能更容易理解

['year', 'quarter', 'month', 'week', 'day'].each {
   diffStr(str, max, min, it)
}


,然后像这样更改diffStr

private diffStr(StringBuilder str, max, min, String duration) {
    //Rest same
    int maxValue = max."reputation_change_$duration"
    int minValue = min."reputation_change_$duration"
    //Rest same
}


我删除了ToIntFunction但您确实意识到实际上那里是Closure<Integer>。无需在任何地方使用Java 8。并请注意,如果您不返回任何内容,则在指定访问权限的情况下就不需要空白。

关于将+用于StringBuilder以及使用Apache commons的部分已经讲过。请给我我的支持。

最后,您知道了@CompileStatic。对?除非您在Groovy中使用动态事物,否则请在各处使用它。