重构——IdGenerator重构分析过程

这篇具有很好参考价值的文章主要介绍了重构——IdGenerator重构分析过程。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

引言

本文为《设计模式之美》的第34-38章的学习笔记,主要从一个IdGenerator类出发,一步一步将代码优化为可读性,可扩展性,可测试性优秀的代码。
主要记录优化过程以及个人思考。

相关源码可以从https://github.com/WeiXiao-Hyy/design-patterns获取,欢迎Star!

需求

在微服务开发中生成唯一请求ID的功能很常见,如下代码即是一个简单版本的ID生成器。整个ID由三个部分组成:

  1. 本机名的最后一个字段
  2. 当前的时间戳,精确到毫秒
  3. 8位的随机字符串,包含大小写字母和数字

尽管该版本生成的ID并不是唯一的,有重复的可能,但是事实上重复的可能性的概率非常低。


@Slf4j
public class IdGenerator {
    public static String generate() {
        String id = "";
        try {
            String hostName = InetAddress.getLocalHost().getHostName();
            String[] tokens = hostName.split("\\.");
            if (tokens.length > 0) {
                hostName = tokens[tokens.length - 1];
            }
            char[] randomChars = new char[8];
            int count = 0;
            Random random = new Random();
            while (count < 8) {
                int randomAscii = random.nextInt(122);
                if (randomAscii >= 48 && randomAscii <= 57) {
                    randomChars[count] = (char) ('0' + (randomAscii - 48));
                    count++;
                } else if (randomAscii >= 65 && randomAscii <= 90) {
                    randomChars[count] = (char) ('A' + (randomAscii - 65));
                    count++;
                } else if (randomAscii >= 97 && randomAscii <= 122) {
                    randomChars[count] = (char) ('a' + (randomAscii - 97));
                    count++;
                }
            }
            id = String.format("%s-%d-%s", hostName, System.currentTimeMillis(), new String(randomChars));
        } catch (UnknownHostException e) {
            log.warn("Failed to get the host name.", e);
        }
        return id;
    }
}

优化步骤

从可读性,可测试性,编写完善的单元测试,所有重构完成之后添加注释四个步骤进行优化。

可读性

从基于接口而非实现编程的角度,其主要的目的是为了方便后续灵活地替换实现类。比如未来可能会有以下场景:

  1. 需要生成微服务调用链请求唯一ID;
  2. Auth2.0中的clientId,clientSecret生成;
  3. 用户订单ID;
  4. 等等

基于以上场景考虑有如下三个接口定义方式

接口 实现类
命名一 IdGenerator LogTraceIdGenerator
命名二 LogTraceGenerator HostNameMillsIdGenerator
命名三 LogTraceGenerator RandomIdGenerator

命名一

接口实现类设计为LogTraceIdGenerator, 如果未来存在用户(UserIdGenerator),订单(OrderIdGenerator)等ID生成器,其实现类不能进行替换,所以让这三个类去实现IdGenerator接口,实际上没有意义。

命名二

接口为LogTraceGenerator没有问题,但是HostNameMillsIdGenerator暴露了太多的实现细节,只要代码稍微有所改动,就可能需要改动命名了。

命名三

对于命名三,生成的ID是一个随机ID,不是递增有序的,命名为RandomIdGenerator是比较合理的,即使内部生成算法有所改动,不需要改动命名。

最终方案

抽象出两个接口,一个是IdGenerator, 一个是LogTraceIdGenerator, LogTraceIdGenerator继承IdGenerator, 实现类实现接口LogTraceIdGenerator, 命名为RandomIdGenerator, 这样实现类可以复用到多个业务模块,比如用户,订单,OAuth等等。

基于以上优化得到如下代码:

public interface IdGenerator {
    String generate();
}
public interface LogTraceIdGenerator extends IdGenerator {
}
@Slf4j
public class RandomIdGenerator implements LogTraceIdGenerator {
    @Override
    public String generate() {
        String substrOfHostName = getLastfieldOfHostName();
        long currentTimeMillis = System.currentTimeMillis();
        String randomString = generateRandomAlphameric(8);
        String id = String.format("%s-%d-%s", 
                substrOfHostName, currentTimeMillis, randomString);
        return id;
    }
    private String getLastfieldOfHostName() {
        String substrOfHostName = null;
        try {
            String hostName = InetAddress.getLocalHost().getHostName();
            String[] tokens = hostName.split("\\.");
            substrOfHostName = tokens[tokens.length - 1];
            return substrOfHostName;
        } catch (UnknownHostException e) {
            log.warn("Failed to get the host name.", e);
        }
        return substrOfHostName;
    }
    private String generateRandomAlphameric(int length) {
        char[] randomChars = new char[length];
        int count = 0;
        Random random = new Random();
        while (count < length) {
            int maxAscii = 'z';
            int randomAscii = random.nextInt(maxAscii);
            boolean isDigit= randomAscii >= '0' && randomAscii <= '9';
            boolean isUppercase= randomAscii >= 'A' && randomAscii <= 'Z';
            boolean isLowercase= randomAscii >= 'a' && randomAscii <= 'z';
            if (isDigit|| isUppercase || isLowercase) {
                randomChars[count] = (char) (randomAscii);
                ++count;
            }
        }
        return new String(randomChars);
    }
}

可测试性

  • generate函数为静态函数,不好写测试代码(除非用PowerMock);
  • generate函数依赖时间函数、随机函数,机器的hostname所以可测试性不好;

将依赖环境或其他的函数剥离出来,单独测试其他部分

getLastfieldOfHostName分为hostname部分和getLastSubstrSplittedByDot单独测试getLastSubstrSplittedByDot即可。

将不好测试的private函数可以转化为protected+@VisibleForTesting

  • protected的作用:可以直接在单元测试中通过对象来调用两个函数进行测试。
  • @VisibleForTesting: 只起到标识作用,只是为了测试。

基于以上优化, 得到下述代码:

public class RandomIdGenerator implements LogTraceIdGenerator {
    
    @Override
    public String generate() {
        String substrOfHostName = getLastfieldOfHostName();
        long currentTimeMillis = System.currentTimeMillis();
        String randomString = generateRandomAlphameric(8);
        String id = String.format("%s-%d-%s",
                substrOfHostName, currentTimeMillis, randomString);
        return id;
    }
    
    private String getLastfieldOfHostName() {
        String substrOfHostName = null;
        try {
            String hostName = InetAddress.getLocalHost().getHostName();
            substrOfHostName = getLastSubstrSplittedByDot(hostName);
        } catch (UnknownHostException e) {
            logger.warn("Failed to get the host name.", e);
        }
        return substrOfHostName;
    }
    
    @VisibleForTesting
    protected String getLastSubstrSplittedByDot(String hostName) {
        String[] tokens = hostName.split("\\.");
        String substrOfHostName = tokens[tokens.length - 1];
        return substrOfHostName;
    }
    
    @VisibleForTesting
    protected String generateRandomAlphameric(int length) {
        char[] randomChars = new char[length];
        int count = 0;
        Random random = new Random();
        while (count < length) {
            int maxAscii = 'z';
            int randomAscii = random.nextInt(maxAscii);
            boolean isDigit = randomAscii >= '0' && randomAscii <= '9';
            boolean isUppercase = randomAscii >= 'A' && randomAscii <= 'Z';
            boolean isLowercase = randomAscii >= 'a' && randomAscii <= 'z';
            if (isDigit || isUppercase || isLowercase) {
                randomChars[count] = (char) (randomAscii);
                ++count;
            }
        }
        return new String(randomChars);
    }
}

完善单元测试

基于上述重构,目前需要测试的函数如下:

public String generate();

private String getLastfieldOfHostName();

@VisibleForTesting
protected String getLastSubstrSplittedByDot(String hostName);

@VisibleForTesting
protected String generateRandomAlphameric(int length);

对于后两个函数逻辑较为复杂, 是我们测试的重点, 单元测试代码如下:

  1. 函数命名 testgetLastSubstrSplittedByDot_nullOrEmpty(): 团队统一即可,较推荐该种写法;
  2. 注意各种边界条件, 字符串可能为null或"";
  3. 有时还需要测试函数的执行次数,而不仅仅是返回结果的某个属性;

对于generate()函数,是唯一暴露给外部使用的public方法,其依赖主机名称、随机函数、时间函数,该如何编写测试函数呢?

注意

写单元测试的时候,测试对象是函数定义的功能,而非具体的实现逻辑。这样才能做到,即使函数的实现逻辑改变了,单元测试用例仍然可以工作。

  1. generator功能定义为"生成一个随机唯一ID",那么需要测试多次调用generate生成的ID是否唯一;
  2. generator功能定义为"只包含数字、大小写字母和中划线的唯一ID",那么不仅需要测试ID的唯一性,还需要测试ID的组成是否符合预期;
  3. generator功能定义为"生成唯一ID,格式为{hostname}-{时间戳}-{8位随机数字}",那么不仅需要测试ID的唯一性,还需要测试ID的组成是否符合预期;

对于getLastfieldOfHostName()实际上这个函数不容易测试,因为它调用了一个静态函数,并且这个静态函数依赖运行环境,但是这个函数的实现非常简单, 所以我认为不需要为其单独写单元测试。

基于以上分析,写出下述的单元测试代码,同时也观察到如果传入的字符串为null或"",testGetLastSubstrSplittedByDot函数会抛出异常,也验证了写单元测试可以帮助我们review自己的代码,同时提高代码的健壮性。

@Test
public void testGetLastSubstrSplittedByDot() {
    RandomIdGenerator idGenerator = new RandomIdGenerator();
    String actualSubstr = idGenerator.getLastSubstrSplittedByDot("field1.field2.field3");
    Assert.assertEquals("field3", actualSubstr);
    actualSubstr = idGenerator.getLastSubstrSplittedByDot("field1");
    Assert.assertEquals("field1", actualSubstr);
    actualSubstr = idGenerator.getLastSubstrSplittedByDot("field1#field2#field3");
    Assert.assertEquals("field1#field2#field3", actualSubstr);
}

// 此单元测试会失败,因为我们在代码中没有处理hostName为null或空字符串的情况
@Test
public void testGetLastSubstrSplittedByDot_nullOrEmpty() {
    RandomIdGenerator idGenerator = new RandomIdGenerator();
    String actualSubstr = idGenerator.getLastSubstrSplittedByDot(null);
    Assert.assertNull(actualSubstr);
    actualSubstr = idGenerator.getLastSubstrSplittedByDot("");
    Assert.assertEquals("", actualSubstr);
}

@Test
public void testGenerateRandomAlphameric() {
    RandomIdGenerator idGenerator = new RandomIdGenerator();
    String actualRandomString = idGenerator.generateRandomAlphameric(6);
    Assert.assertNotNull(actualRandomString);
    Assert.assertEquals(6, actualRandomString.length());
    for (char c : actualRandomString.toCharArray()) {
        Assert.assertTrue(('0' <= c && c <= '9') || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'));
    }
}

// 此单元测试会失败,因为我们在代码中没有处理length<=0的情况
@Test
public void testGenerateRandomAlphameric_lengthEqualsOrLessThanZero() {
    RandomIdGenerator idGenerator = new RandomIdGenerator();
    String actualRandomString = idGenerator.generateRandomAlphameric(0);
    Assert.assertEquals("", actualRandomString);
    actualRandomString = idGenerator.generateRandomAlphameric(-1);
    Assert.assertNull(actualRandomString);
}

添加注释

注释不能太多,也不能太少,主要添加在类和函数上。好的命名可以替代明确或简单的类和函数。

注意

注释 = 做什么,为什么,怎么做,怎么用,对一些边界条件,特殊情况进行说明,以及对函数输入,输出,异常进行说明。 写好注释很关键,可以通过以下两个方式去练习:

  1. 学习javadoc
  2. 阅读jdk源码

关于注释使用英文还是中文

个人觉得要看团队的规范,毕竟代码是提供给别人看的,如果个人或团队的英文水平较弱,使用中文也是一个提高效率不错的选择(虽然左耳朵老师建议使用英文注释)。

如下是上述代码的注释样例:

/**
 * Id Generator that is used to generate random IDs.
 *
 * <p>
 * The IDs generated by this class are not absolutely unique,
 * but the probability of duplication is very low.
 */
class RandomIdGenerator implements LogTraceIdGenerator {

    /**
     * Generate the random ID. The IDs may be duplicated only in extreme situation.
     *
     * @return a random ID
     */
    @Override
    public String generate() {
        String substrOfHostName = getLastfieldOfHostName();
        long currentTimeMillis = System.currentTimeMillis();
        String randomString = generateRandomAlphameric(8);
        String id = String.format("%s-%d-%s",
                substrOfHostName, currentTimeMillis, randomString);
        return id;
    }

    /**
     * Get the local hostname and extract the last field of the name string splitted by delimiter '.'.
     *
     * @return the last field of hostname. Returns null if hostname is not obtained.
     */
    private String getLastfieldOfHostName() {
        String substrOfHostName = null;
        try {
            String hostName = InetAddress.getLocalHost().getHostName();
            substrOfHostName = getLastSubstrSplittedByDot(hostName);
        } catch (UnknownHostException e) {
            log.warn("Failed to get the host name.", e);
        }
        return substrOfHostName;
    }

    /**
     * Get the last field of {@code hostname} splitted by delemiter '.'.
     *
     * @param hostname should not be null
     * @return the last field of {@code hostname}. Returns empty string if {@code hostname} is empty string.
     */
    @VisibleForTesting
    protected String getLastSubstrSplittedByDot(String hostname) {
        String[] tokens = hostname.split("\\.");
        String substrOfHostName = tokens[tokens.length - 1];
        return substrOfHostName;
    }

    /**
     * Generate random string which
     * only contains digits, uppercase letters and lowercase letters.
     *
     * @param length should not be less than 0
     * @return the random string. Returns empty string if {@code length} is 0
     */
    @VisibleForTesting
    protected String generateRandomAlphameric(int length) {
        char[] randomChars = new char[length];
        int count = 0;
        Random random = new Random();
        while (count < length) {
            int maxAscii = 'z';
            int randomAscii = random.nextInt(maxAscii);
            boolean isDigit = randomAscii >= '0' && randomAscii <= '9';
            boolean isUppercase = randomAscii >= 'A' && randomAscii <= 'Z';
            boolean isLowercase = randomAscii >= 'a' && randomAscii <= 'z';
            if (isDigit || isUppercase || isLowercase) {
                randomChars[count] = (char) (randomAscii);
                ++count;
            }
        }
        return new String(randomChars);
    }
}

异常处理

对于上述单元测试无法通过,因为我们没有考虑异常情况:

  1. 对于generate()函数,如果本机hostname获取失败,函数返回说明?
  2. 对于getLastfieldOfHostName()函数,是否应该将UnknownHostException异常catch住并打印error日志?还是将异常继续抛出?或是将异常转化后再抛出?
  3. 对于getLastSubstrSplittedByDot和generateRandomAlphameric输入参数为NULL或空字符串或length<0,函数该如何返回?
generate函数

ID由三个部分构成: 本机hostname, 时间戳和随机数。获取hostname可能获取失败。如果对于业务能够接受null-16723733647-83Ab3uK6这种数据则可以不做处理,但更推荐将异常告知调用者(因为该情况是不希望发生,并且发生是不正常的)。所以,这里推荐抛出受检查异常。

public String generate() throws IdGenerationFailureException {
    String substrOfHostName = getLastFieldOfHostName();
    if (substrOfHostName == null || substrOfHostName.isEmpty()) {
        throw new IdGenerationFailureException("host name is empty.");
    }
    long currentTimeMillis = System.currentTimeMillis();
    String randomString = generateRandomAlphameric(8);
    String id = String.format("%s-%d-%s",
            substrOfHostName, currentTimeMillis, randomString);
    return id;
}
getLastfieldOfHostName

对于getLastfieldOfHostName()函数,是否应该将异常在函数内部吞掉并打印日志,还是将异常继续往上抛出?如果往上继续抛出,需要将异常转化嘛?

返回NULL还是异常

对于函数返回NULL还是异常,要看获取不到数据是不是正常行为,如果获取主机hostname失败之后会影响后续逻辑的处理,并不是程序期望的,所以是一种异常行为。这里最好是抛出异常,而不是返回NULL值。但比如query,select等函数,如果不存在数据通常也是一种正常行为则返回空集合也是可以的。

异常是否需要转化

关于是否需要将异常转化,要异常是否有业务相关。UnknownHostException表示主机hostname获取失败是业务相关的,所以将异常抛出即可,不需要进行异常转化。

代码重构

根据上述分析,getLastfieldOfHostName需要将异常原封不动抛出,此时generate()需要捕获该异常,在generate函数中该如何处理该异常呢?根据上述分析:

  1. generate需要通知调用者异常;
  2. UnknownHostException跟generate业务无相关,需要将异常转化后抛出;
public String generate() throws IdGenerationFailureException {
    String substrOfHostName = null;
    try {
        substrOfHostName = getLastFieldOfHostName();
    } catch (UnknownHostException e) {
        throw new IdGenerationFailureException("host name is empty.");
    }
    long currentTimeMillis = System.currentTimeMillis();
    String randomString = generateRandomAlphameric(8);
    String id = String.format("%s-%d-%s", 
            substrOfHostName, currentTimeMillis, randomString);
    return id;
}
getLastSubstrSplittedByDot

对于getLastSubstrSplittedByDot()函数,如果hostname为NULL或者空字符串,该函数应该返回什么呢?

如果上层做了参数校验,下层需要再写一遍嘛?

理论上,参数传递的正确性应该有程序员来保证,不需要再做NULL或者空字符串的判断或者特殊处理。调用者本不应该传递NULL或者空字符串。

我认为,对于private私有函数,只在类内部调用,不要传递NULL值或者空字符串即可。如果是public函数,无法掌握会被谁调用以及如何调用。为了代码的健壮性,最好在public函数中做防御操作。

@VisibleForTesting
protected String getLastSubstrSplittedByDot(String hostName) {
    if (hostName == null || hostName.isEmpty()) {
        throw IllegalArgumentException("..."); //运行时异常
    }
    String[] tokens = hostName.split("\\.");
    String substrOfHostName = tokens[tokens.length - 1];
    return substrOfHostName;
}
generateRandomAlphameric

对于generateRandomAlphameric函数,如果length<0或者length=0,函数应该返回什么?

length < 0的情况

生成长度为负数的随机字符串是不符合常规逻辑的,是一种异常行为,所以length<0应该抛出异常。

length = 0的情况

length=0的情况,需要根据业务来决定,可以将其视为一种异常行为,也可以直接返回空字符串。最关键的是需要在注释上指明length=0会返回什么样的数据。

重构后的代码

/**
 * Id Generator that is used to generate random IDs.
 *
 * <p>
 * The IDs generated by this class are not absolutely unique,
 * but the probability of duplication is very low.
 */
public class RandomIdGenerator implements IdGenerator {

    /**
     * Generate the random ID. The IDs may be duplicated only in extreme situation.
     *
     * @return an random ID
     */
    @Override
    public String generate() throws IdGenerationFailureException {
        String substrOfHostName = null;
        try {
            substrOfHostName = getLastFieldOfHostName();
        } catch (UnknownHostException e) {
            throw new IdGenerationFailureException("...", e);
        }
        long currentTimeMillis = System.currentTimeMillis();
        String randomString = generateRandomAlphameric(8);
        return String.format("%s-%d-%s", substrOfHostName, currentTimeMillis, randomString);
    }

    /**
     * Get the local hostname and
     * extract the last field of the name string splitted by delimiter '.'.
     *
     * @return the last field of hostname. Returns null if hostname is not obtained.
     */
    private String getLastFieldOfHostName() throws UnknownHostException {
        String substrOfHostName = null;
        String hostName = InetAddress.getLocalHost().getHostName();
        if (StringUtils.isBlank(hostName)) {
            throw new UnknownHostException("...");
        }
        substrOfHostName = getLastSubstrSplittedByDot(hostName);
        return substrOfHostName;
    }

    /**
     * Get the last field of {@hostName} splitted by delemiter '.'.
     *
     * @param hostName should not be null
     * @return the last field of {@hostName}. Returns empty string if {@hostName} is empty string.
     */
    @VisibleForTesting
    protected String getLastSubstrSplittedByDot(String hostName) {
        if (StringUtils.isBlank(hostName)) {
            throw new IllegalArgumentException("...");
        }
        String[] tokens = hostName.split("\\.");
        return tokens[tokens.length - 1];
    }

    /**
     * Generate random string which
     * only contains digits, uppercase letters and lowercase letters.
     *
     * @param length should not be less than 0
     * @return the random string. Returns empty string if {@length} is 0
     */
    @VisibleForTesting
    protected String generateRandomAlphameric(int length) {
        if (length <= 0) {
            throw new IllegalArgumentException("...");
        }
        char[] randomChars = new char[length];
        int count = 0;
        Random random = new Random();
        while (count < length) {
            int maxAscii = 'z';
            int randomAscii = random.nextInt(maxAscii);
            boolean isDigit = randomAscii >= '0' && randomAscii <= '9';
            boolean isUppercase = randomAscii >= 'A' && randomAscii <= 'Z';
            boolean isLowercase = randomAscii >= 'a' && randomAscii <= 'z';
            if (isDigit || isUppercase || isLowercase) {
                randomChars[count] = (char) (randomAscii);
                ++count;
            }
        }
        return new String(randomChars);
    }
}

@Test
public void testGetLastSubstrSplittedByDot() {
    RandomIdGenerator idGenerator = new RandomIdGenerator();
    String actualSubstr = idGenerator.getLastSubstrSplittedByDot("field1.field2.field3");
    Assert.assertEquals("field3", actualSubstr);
    actualSubstr = idGenerator.getLastSubstrSplittedByDot("field1");
    Assert.assertEquals("field1", actualSubstr);
    actualSubstr = idGenerator.getLastSubstrSplittedByDot("field1#field2#field3");
    Assert.assertEquals("field1#field2#field3", actualSubstr);
}

@Test
public void testGetLastSubstrSplittedByDot_nullOrEmpty() {
    RandomIdGenerator idGenerator = new RandomIdGenerator();
    String actualSubstr = idGenerator.getLastSubstrSplittedByDot(null);
    Assert.assertNull(actualSubstr);
    actualSubstr = idGenerator.getLastSubstrSplittedByDot("");
    Assert.assertEquals("", actualSubstr);
}

@Test
public void testGenerateRandomAlphameric() {
    RandomIdGenerator idGenerator = new RandomIdGenerator();
    String actualRandomString = idGenerator.generateRandomAlphameric(6);
    Assert.assertNotNull(actualRandomString);
    Assert.assertEquals(6, actualRandomString.length());
    for (char c : actualRandomString.toCharArray()) {
        Assert.assertTrue(('0' <= c && c <= '9') || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'));
    }
}

@Test
public void testGenerateRandomAlphameric_lengthEqualsOrLessThanZero() {
    RandomIdGenerator idGenerator = new RandomIdGenerator();
    String actualRandomString = idGenerator.generateRandomAlphameric(0);
    Assert.assertEquals("", actualRandomString);
    actualRandomString = idGenerator.generateRandomAlphameric(-1);
    Assert.assertNull(actualRandomString);
}

补充

LinkedIn‘s Tips for Highly Effective Code Review

  • Do I Understand the “Why”?
    在提交pr的同时需要描述本次修改的“动机”,有助于提高代码文档质量。

  • Am I Giving Positive Feedback?
    当reviewer看到优秀代码需要给出正反馈。

  • Is My Code Review Comment Explained Well?
    comment需要简洁易懂,比如"reduces duplication", "improves coverage"等等

  • Do I Appreciate the Submitter’s Effort?
    每一次pr都需要被感谢,不管结果如何,使用谢谢

  • Would This Review Comment Be Useful to Me?
    减少不必要的comment,比如代码格式有问题,开发者需要将CR意见当成有用的工具

  • Is the “Testing Done” Section Thorough Enough?
    每一次变更都需要通过单元/接口测试

  • Am I Too Pedantic in My Review?
    养成CR习惯,不要当成一种负担。养成一种心态:别人要CR我的代码,至少我的代码在自己CR下能够满意。文章来源地址https://www.toymoban.com/news/detail-854944.html

如何发现代码质量问题——常规checklist

  • 目录设置是否合理,模块划分是否清晰,代码结构是否满足“高内聚,松耦合”
  • 是否遵循经典的设计原则和设计思想(SOLID,DRY,KISS,YAGNI,LOD等)
  • 设计模式是否应用得当?是否有过度设计?
  • 代码是否容易扩展?如果要添加新功能,是否容易实现?
  • 代码是否可以复用?是否可以复用已有的项目代码或者类库?是否有重复造轮子?
  • 代码是否容易测试?单元测试是否全面覆盖各种正常和异常的情况?
  • 代码是否易读?是否符合编码规范(比如命名和注释是否恰当、代码风格是否一致等)?

如何发现代码质量问题——业务需求checklist

  • 代码是否实现了预期的业务需求?
  • 逻辑是否正确?是否处理了各种异常情况?
  • 日志打印是否得当?是否方便debug排查问题?
  • 接口是否易用?是否支持幂等、事务等?
  • 代码是否存在并发问题?是否线程安全?
  • 性能是否有优化空间,比如,SQL、算法是否可以优化?
  • 是否有安全漏洞?比如,输入输出校验是否全面?

到了这里,关于重构——IdGenerator重构分析过程的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【碎片知识点】springboot配置https与域名进行访问

    目录 步骤1:域名解析ip 步骤2:下载ssl证书 步骤3:把证书放入springboot项目里,并配置 步骤4:打包放上去服务器运行 现在云服务器添加自己的域名与子域名进行解析ip(没有域名自己去买哈) springboot的服务器是tomcat,所以要下载对应的文件    下载下来 放入文件,写入配

    2024年02月03日
    浏览(39)
  • 记一次 .NET某培训学校系统 内存碎片化分析

    前些天有位朋友微信上找到我,说他们学校的Web系统内存一直下不去,让我看下到底是怎么回事,老规矩让朋友生成一个dump文件丢给我,看一下便知。 要想看托管还是非托管,可以用 !address -summary 观察下内存段。 从卦中信息的 MEM_COMMIT 和 Heap 来看,应该就是托管内存的问题

    2024年02月14日
    浏览(40)
  • 【技术碎片】【Java】计算椭圆的外接矩形坐标

    遇到一个需要计算一般椭圆(斜椭圆)的外接矩形坐标的问题,在此记录一下 已知椭圆的中心点坐标centerX centerY,椭圆的长轴,短轴majorRadius minorRadius,和旋转角度 angle。 按理说java有原生的计算外接矩形的函数,先看看 java.awt.geom 怎么实现的。 注:java.awt.geom 是 Java 2D 图形

    2024年02月08日
    浏览(54)
  • 【三维重建】三维重构基础知识、三维数据、重建流程

    1.使用几何建模软件,通过人机交互生成人为控制下的三维:3DMAX、Maya、AutoCAD、UG 2.获取真实的物体形状:三维重构 三维图像重构: 摄像机获取图像,对图像分析处理,结合CV知识推导出现实中物体的三维信息 从二维图像到三维空间的重构(模仿生物两只眼睛观察物体产生的

    2024年02月02日
    浏览(54)
  • 从更广阔的角度看待产业互联网,它展现的是一次重构的过程

    如果产业互联网仅仅只是在传统的供求关系之下,如果产业互联网仅仅只是在传统的平衡之下,缺少了一次对于供求关系的重新建构,那么,所谓的产业互联网,依然是无法跳出以往的发展困境,依然是无法摆脱以往的发展逻辑的。站在这样一个角度来看待产业互联网,其实

    2024年02月15日
    浏览(43)
  • Java避免死锁的几个常见方法(有测试代码和分析过程)

    目录 Java避免死锁的几个常见方法 死锁产生的条件 上死锁代码 然后 :jstack 14320 jstack.text Java避免死锁的几个常见方法 Java避免死锁的几个常见方法 避免一个线程同时获取多个锁。 避免一个线程在锁内同时占用多个资源,尽量保证每个锁只占用一个资源。 尝试使用定时锁,使

    2023年04月16日
    浏览(74)
  • Java体系性能测试进阶必须了解的知识点——死锁分析和锁竞争分析

    所谓 死锁 ,是指多个进程在运行过程中因争夺资源而造成的一种僵局,当进程处于这种僵持状态时,若无外力作用,它们都将无法再向前推进。对于锁更好的理解,先要理解monitor这个概念! monitor直译过来是监视器的意思,专业一点叫管程。monitor是属于编程语言级别的,它的出现

    2024年02月07日
    浏览(42)
  • 【Java技术专题】「盲点追踪」突破知识盲点分析Java安全管理器(SecurityManager)

    Java安全应该包括两方面的内容,一是Java平台(即是Java运行环境)的安全性;二是Java语言开发的应用程序的安全性。由于我们不是Java本身语言的制定开发者,所以第一个安全性不需要我们考虑。其中第二个安全性是我们重点考虑的问题,一般我们可以通过安全管理器机制来

    2024年02月08日
    浏览(41)
  • java8-重构、测试、调试

    8.1.1 改善代码的可读性 改善代码的可读性到底意味着什么?我们很难定义什么是好的可读性,因为这可能非常主观。通常的理解是,“别人理解这段代码的难易程度”。改善可读性意味着你要确保你的代码能非常容易地被包括自己在内的所有人理解和维护。为了确保你的代码

    2024年02月20日
    浏览(38)
  • 【Java设计模式 规范与重构】 二 重构的保障:单元测试,以及如何提高代码可测试性

    其实之前的工作中强调过很多次自己做测试的重要性,例如讲单元测试的: 【C#编程最佳实践 一】单元测试实践 ,讲单元测试规范的 【阿里巴巴Java编程规范学习 四】Java质量安全规约 ,讲接口测试的: 【C#编程最佳实践 十三】接口测试实践 ,这里旧事重提就不再详细展开

    2023年04月25日
    浏览(58)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包