我眼中的优雅代码
·
[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();
}
}