代码不仅要能正确运行,更要易于阅读、维护和扩展。本文将分享我在两年工作中养成的具体代码优化习惯,包括命名规范、函数设计、消除重复、注释写法、测试策略等实用技巧,每个习惯都配有具体的"Before/After"代码示例。
1. 命名:名副其实,见名知意
坏味道:
// 模糊的命名
public List<int[]> getData() {
List<int[]> list = new ArrayList<>();
for (int[] x : theList) {
if (x[0] == 4) {
list.add(x);
}
}
return list;
}优化后:
// 清晰的命名
public List<Cell> getFlaggedCellsOnGameBoard() {
List<Cell> flaggedCells = new ArrayList<>();
for (Cell cell : gameBoard) {
if (cell.isFlagged()) {
flaggedCells.add(cell);
}
}
return flaggedCells;
}命名原则实践:
- 变量名要能表达"是什么",而不是"怎么做"
- 避免使用
data、info、tmp等模糊词汇 - 方法名用动词开头,表达"做什么"
- 布尔变量/方法用
is、has、can等前缀
2. 函数:短小精悍,单一职责
坏味道(长函数,多重职责):
public void processUserOrder(String filePath) {
// 读取文件
try (BufferedReader br = new BufferedReader(new FileReader(filePath))) {
String line;
while ((line = br.readLine()) != null) {
String[] data = line.split(",");
if (data.length >= 5) {
String orderId = data[0];
String userId = data[1];
BigDecimal amount = new BigDecimal(data[2]);
String status = data[3];
Date createTime = parseDate(data[4]);
// 验证数据
if (isValidOrderId(orderId) && isValidAmount(amount) && isValidStatus(status)) {
// 保存订单
Order order = new Order(orderId, userId, amount, status, createTime);
orderMapper.insert(order);
// 更新用户统计
userStatsMapper.incrementOrderCount(userId);
// 发送订单创建事件
eventPublisher.publishEvent(new OrderCreatedEvent(order));
// 记录操作日志
operationLogService.logOrderCreation(order);
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}优化后(分解为单一职责的小函数):
public void processUserOrder(String filePath) {
List<String> fileLines = readAllLinesSafely(filePath);
fileLines.stream()
.map(this::parseOrderFromCsvLine)
.filter(Optional::isPresent)
.map(Optional::get)
.filter(this::isValidOrder)
.forEach(this::processValidOrder);
}
private List<String> readAllLinesSafely(String filePath) {
try {
return Files.readAllLines(Paths.get(filePath));
} catch (IOException e) {
log.error("读取文件失败: {}", filePath, e);
return Collections.emptyList();
}
}
private Optional<Order> parseOrderFromCsvLine(String csvLine) {
try {
String[] data = csvLine.split(",");
if (data.length >= 5) {
Order order = Order.builder()
.orderId(data[0])
.userId(data[1])
.amount(new BigDecimal(data[2]))
.status(data[3])
.createTime(parseDate(data[4]))
.build();
return Optional.of(order);
}
} catch (Exception e) {
log.warn("解析CSV行失败: {}", csvLine, e);
}
return Optional.empty();
}
private boolean isValidOrder(Order order) {
return isValidOrderId(order.getOrderId())
&& isValidAmount(order.getAmount())
&& isValidStatus(order.getStatus());
}
private void processValidOrder(Order order) {
saveOrderToDatabase(order);
updateUserOrderStatistics(order.getUserId());
publishOrderCreatedEvent(order);
logOrderCreationOperation(order);
}
// 每个函数只做一件事
private void saveOrderToDatabase(Order order) {
orderMapper.insert(order);
}
private void updateUserOrderStatistics(String userId) {
userStatsMapper.incrementOrderCount(userId);
}
private void publishOrderCreatedEvent(Order order) {
eventPublisher.publishEvent(new OrderCreatedEvent(order));
}
private void logOrderCreationOperation(Order order) {
operationLogService.logOrderCreation(order);
}3. 消除重复:DRY原则的实际应用
坏味道(重复的校验逻辑):
@Service
public class UserService {
public void createUser(User user) {
if (user.getUsername() == null || user.getUsername().trim().isEmpty()) {
throw new IllegalArgumentException("用户名不能为空");
}
if (user.getUsername().length() < 3 || user.getUsername().length() > 20) {
throw new IllegalArgumentException("用户名长度必须在3-20之间");
}
if (!user.getUsername().matches("^[a-zA-Z0-9_]+$")) {
throw new IllegalArgumentException("用户名只能包含字母、数字和下划线");
}
// ... 重复的邮箱校验逻辑
// ... 重复的手机号校验逻辑
}
public void updateUser(User user) {
// 重复的用户名校验逻辑
if (user.getUsername() == null || user.getUsername().trim().isEmpty()) {
throw new IllegalArgumentException("用户名不能为空");
}
// ... 其他重复逻辑
}
}优化后(使用注解校验和统一处理):
// 使用Validation注解定义规则
@Data
public class User {
@NotBlank(message = "用户名不能为空")
@Size(min = 3, max = 20, message = "用户名长度必须在3-20之间")
@Pattern(regexp = "^[a-zA-Z0-9_]+$", message = "用户名只能包含字母、数字和下划线")
private String username;
@Email(message = "邮箱格式不正确")
@NotBlank(message = "邮箱不能为空")
private String email;
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
private String phone;
}
// 统一的校验工具类
@Component
public class ValidationUtils {
public void validate(Object object) {
Set<ConstraintViolation<Object>> violations = Validation.buildDefaultValidatorFactory()
.getValidator()
.validate(object);
if (!violations.isEmpty()) {
String errorMessage = violations.stream()
.map(ConstraintViolation::getMessage)
.collect(Collectors.joining("; "));
throw new ValidationException(errorMessage);
}
}
}
// 简洁的业务代码
@Service
@Validated
public class UserService {
@Autowired
private ValidationUtils validationUtils;
public void createUser(@Valid User user) {
// Spring会自动校验@Valid注解的参数
userMapper.insert(user);
}
public void updateUser(User user) {
validationUtils.validate(user); // 手动校验
userMapper.update(user);
}
}4. 注释的艺术:解释为什么,而不是是什么
坏注释:
// 计算面积
public double calculateArea(double radius) {
return 3.14 * radius * radius; // πr²
}
// 设置用户状态
public void setUserStatus(int status) {
this.status = status;
}好注释:
/**
* 使用蒙特卡洛方法估算π值
* 这种方法在精度要求不高但计算资源有限时使用
* 参考: https://en.wikipedia.org/wiki/Monte_Carlo_method
*/
public double estimatePi(int sampleCount) {
// 实现细节...
}
/**
* 设置用户状态,注意:
* 1. 状态从0变为1时会触发欢迎流程
* 2. 状态从1变为2时需要检查是否满足升级条件
* 3. 状态变为99时不可逆转
*/
public void setUserStatus(UserStatus status) {
UserStatus oldStatus = this.status;
this.status = status;
publishStatusChangeEvent(oldStatus, status);
}5. 测试策略:可测试的代码设计
难以测试的代码:
@Service
public class OrderService {
@Autowired
private EmailService emailService;
public void createOrder(Order order) {
// 直接依赖具体实现,难以mock
if (order.getAmount().compareTo(BigDecimal.ZERO) > 0) {
emailService.sendOrderConfirmation(order.getEmail(), order);
}
orderMapper.insert(order);
}
}易于测试的代码:
// 定义接口
public interface NotificationService {
void sendOrderConfirmation(String email, Order order);
}
@Service
public class EmailService implements NotificationService {
@Override
public void sendOrderConfirmation(String email, Order order) {
// 具体实现
}
}
// 通过构造函数注入依赖
@Service
public class OrderService {
private final OrderMapper orderMapper;
private final NotificationService notificationService;
// 构造函数注入,依赖关系明确
public OrderService(OrderMapper orderMapper, NotificationService notificationService) {
this.orderMapper = orderMapper;
this.notificationService = notificationService;
}
public void createOrder(Order order) {
if (shouldSendConfirmation(order)) {
notificationService.sendOrderConfirmation(order.getEmail(), order);
}
orderMapper.insert(order);
}
// 将条件判断提取为方法,便于测试
boolean shouldSendConfirmation(Order order) {
return order.getAmount().compareTo(BigDecimal.ZERO) > 0;
}
}
// 单元测试
@ExtendWith(MockitoExtension.class)
class OrderServiceTest {
@Mock
private OrderMapper orderMapper;
@Mock
private NotificationService notificationService;
@InjectMocks
private OrderService orderService;
@Test
void shouldSendConfirmationWhenOrderAmountIsPositive() {
// Given
Order order = Order.builder()
.amount(new BigDecimal("100.00"))
.email("test@example.com")
.build();
// When
orderService.createOrder(order);
// Then
verify(notificationService).sendOrderConfirmation("test@example.com", order);
verify(orderMapper).insert(order);
}
@Test
void shouldNotSendConfirmationWhenOrderAmountIsZero() {
// Given
Order order = Order.builder()
.amount(BigDecimal.ZERO)
.email("test@example.com")
.build();
// When
orderService.createOrder(order);
// Then
verify(notificationService, never()).sendOrderConfirmation(anyString(), any());
verify(orderMapper).insert(order);
}
}6. 使用现代Java特性简化代码
使用Stream API替代循环:
// 传统方式
List<String> activeUserNames = new ArrayList<>();
for (User user : users) {
if (user.isActive() && user.getCreateTime().after(lastMonth)) {
activeUserNames.add(user.getName().toUpperCase());
}
}
// Stream方式
List<String> activeUserNames = users.stream()
.filter(User::isActive)
.filter(user -> user.getCreateTime().after(lastMonth))
.map(User::getName)
.map(String::toUpperCase)
.collect(Collectors.toList());使用Optional避免空指针:
// 传统方式
public String getManagerName(Department department) {
if (department != null) {
Employee manager = department.getManager();
if (manager != null) {
return manager.getName();
}
}
return "未知";
}
// Optional方式
public String getManagerName(Department department) {
return Optional.ofNullable(department)
.map(Department::getManager)
.map(Employee::getName)
.orElse("未知");
}7. 代码审查清单:我常用的检查点
在代码审查时,我通常会检查以下方面:
- [ ] 命名是否清晰表达了意图?
- [ ] 函数是否短小且只做一件事?
- [ ] 是否有重复代码可以抽取?
- [ ] 异常处理是否完善?
- [ ] 日志记录是否恰当?
- [ ] 测试是否覆盖了主要场景?
- [ ] 是否有明显的性能问题?
- [ ] 是否考虑了线程安全?
- [ ] 配置参数是否可外部化?
- [ ] 文档注释是否完善?
总结:
两年的编程实践让我深刻认识到,编写可工作的代码只是基础,编写可维护、可测试、可扩展的代码才是专业程序员的追求。这些代码优化习惯不是一蹴而就的,而是需要在日常开发中不断实践和反思。每次代码审查、每次生产问题排查,都是改进代码质量的机会。良好的代码习惯就像肌肉记忆一样,需要持续锻炼才能形成。