tommwq的博客

我眼中的优雅代码

· [tommwq@126.com]

下面的内容主要是关于桌面或服务器上普通应用程序的开发,不涉及实时系统、嵌入式、类库或其他有特殊要求的程序。其次,主要关注类和函数级别的代码,不讨论架构和设计模式方面的内容。关于架构方面可以参考这篇文章《架构漫谈(八):从架构的角度看如何写好代码》https://www.infoq.cn/article/an-informal-discussion-on-architecture-part08

对于代码是否优雅,我的判断标准是:是否清晰明了。原因有两点:第一,可读性好,易于维护;第二,代码产生了静态(参考UML类图)、动态(参考UML时序图)两个模型,这两个模型的质量决定了软件的质量,而好的模型应该是清晰明了的。

基于这个标准,我在编写代码时通常会

  • 选择语义明确的命名。

示例:

// Go语言的regexp包
func Compile(expr string) (*Regexp, error) // 失败时返回error
func MustCompile(expr string) *Regexp      // 失败时panic
  • 将读/写操作分离,并避免在写操作中返回操作结果。

示例:

public class Calculator {
    private int result = 0;
    public void add(int x, int y) {
        result = x + y;
    }

    public int getResult() {
        return result;
    }
}
  • 将复杂逻辑拆分成小函数。函数或方法原则上不超过50行。
  • 避免嵌套逻辑判断,将程序逻辑流程尽量安排成“非”字右半边的格式。

示例(修改前):

if (conditionX) {
    if (conditionY) {
        return doA();
    } else {
        return doB();
    }
} else {
    throw new RuntimeException("something goes wrong");
}

示例(修改后):

if (!conditionX) {
    throw new RuntimeException("something goes wrong");
}

if (conditionY) {
    return doA();
}

return doB();
  • 函数入参数量超过3个时,每个参数占单独一行。
  • 避免返回null、nullptr、NULL。

示例(修改前):

public Properties readLocalConfig() {
    Properties result;
    try {
        result = // ...
        return result;
    } catch (IOException e) {
        return null;
    }
}

示例(修改后):

public Properties readLocalConfig() throws IOException {...}
public Optional<Properites> tryReadLocalConfig() {
    Properties result;
    try {
        result = readLocalConfig();
    } catch (IOException e) {
        // ignore
    }

    return Optional.ofNullable(result);
}
  • 注意维护对象在整个生命周期内的不变性,包括多线程环境和异步信号处理下的不变性。

示例(修改前):

public class SomeResource {
    public SomeResource(String parameter) throws InvalidArgumentException {...}
    public void doSomething();
}

public class Agent {
    private static Agent instance;
    private SomeResource resource;

    private Agent(String parameter) {
        resource = new SomeResource(parameter);
    }

    public static Agent getInstance(String parameter) {
        try {
            if (instance == null) {
                synchronized (Agent.class) {
                    if (instance == null) {
                        instance = new Agent(parameter);
                    }
                }
            }
        } catch (Exception e) {
            logger.warn(e.getMessage());
        }

        return instance;
    }

    public void doSomething() {
        if (resource != null) {
            resource.doSomething();
        }
    }
}

示例(修改后):

public class Agent {
    private static Agent instance = new Agent();
    private SomeResource resource;
    private volatile boolean initialized;

    private Agent() {}
    private void initialize(String parameter) {
        try {
            resource = new SomeResource(parameter);
            initialized = true;
        } catch(InvalidArgumentException e) {
            logger.warn(e.getMessage());
        }
    }

    private void initializeInNeed(String parameter) {
        if (initialized) {
            return;
        }

        synchronized (Agent.class) {
            if (!initialized) {
                instance.initialize(parameter);
            }
        }
    }

    public static Agent getInstance(String parameter) {
        instance.initializeInNeed(parameter);
        return instance;
    }

    public void doSomething() {
        if (!initialized) {
            return;
        }

        resource.doSomething();
    }
}