《代码之丑》

61 阅读4分钟

根据《代码之丑》系列内容示例代码:

1. 长函数

问题代码

public void executeTask() {
    ObjectMapper mapper = new ObjectMapper();
    CloseableHttpClient client = HttpClients.createDefault();
    List<Chapter> chapters = this.chapterService.getUntranslatedChapters();
    for (Chapter chapter : chapters) {
        // 发送章节
        SendChapterRequest sendChapterRequest = new SendChapterRequest();
        sendChapterRequest.setTitle(chapter.getTitle());
        sendChapterRequest.setContent(chapter.getContent());
        
        HttpPost sendChapterPost = new HttpPost(sendChapterUrl);
        CloseableHttpResponse sendChapterHttpResponse = null;
        String chapterId = null;
        try {
            String sendChapterRequestText = mapper.writeValueAsString(sendChapterRequest);
            sendChapterPost.setEntity(new StringEntity(sendChapterRequestText));
            sendChapterHttpResponse = client.execute(sendChapterPost);
            // ... 更多HTTP处理代码
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            // ... 资源清理代码
        }
        
        // 翻译章节
        // ... 类似的重复HTTP处理代码
    }
}

重构后代码

public void executeTask() {
    ObjectMapper mapper = new ObjectMapper();
    CloseableHttpClient client = HttpClients.createDefault();
    List<Chapter> chapters = this.chapterService.getUntranslatedChapters();
    for (Chapter chapter : chapters) {
        String chapterId = sendChapter(mapper, client, chapter);
        translateChapter(mapper, client, chapterId);
    }
}

private String sendChapter(ObjectMapper mapper, CloseableHttpClient client, Chapter chapter) {
    SendChapterRequest request = asSendChapterRequest(chapter);
    return executeHttpRequest(mapper, client, request, sendChapterUrl, SendChapterResponse.class);
}

private SendChapterRequest asSendChapterRequest(Chapter chapter) {
    SendChapterRequest request = new SendChapterRequest();
    request.setTitle(chapter.getTitle());
    request.setContent(chapter.getContent());
    return request;
}

2. 大类

问题代码

public class User {
    private long userId;
    private String name;
    private String nickname;
    private String email;
    private String phoneNumber;
    private AuthorType authorType;        // 作者相关
    private ReviewStatus authorReviewStatus; // 作者相关  
    private EditorType editorType;        // 编辑相关
    // ... 更多混合字段
    
    // 用户基本行为
    public void changePassword() { ... }
    
    // 作者行为
    public void submitBook() { ... }
    
    // 编辑行为  
    public void reviewChapter() { ... }
}

重构后代码

public class User {
    private long userId;
    private String name;
    private String nickname;
    private Contact contact;
    
    public void changePassword() { ... }
}

public class Contact {
    private String email;
    private String phoneNumber;
}

public class Author {
    private long userId;
    private AuthorType authorType;
    private ReviewStatus reviewStatus;
    
    public void submitBook() { ... }
}

public class Editor {
    private long userId; 
    private EditorType editorType;
    
    public void reviewChapter() { ... }
}

3. 长参数列表

问题代码

public void createBook(final String title,
                       final String introduction,
                       final URL coverUrl,
                       final BookType type,
                       final BookChannel channel,
                       final String protagonists,
                       final String tags,
                       final boolean completed) {
    // ... 参数太多,难以维护
}

重构后代码

public void createBook(final NewBookParameters parameters) {
    Book book = parameters.newBook();
    this.repository.save(book);
}

public class NewBookParameters {
    private String title;
    private String introduction;
    private URL coverUrl;
    private BookType type;
    private BookChannel channel;
    private String protagonists;
    private String tags;
    private boolean completed;
    
    public Book newBook() {
        return Book.builder()
            .title(title)
            .introduction(introduction)
            // ... 其他字段
            .build();
    }
}

4. 滥用控制语句

问题代码(嵌套过深)

public void distributeEpubs(final long bookId) {
    List<Epub> epubs = this.getEpubsByBookId(bookId);
    for (Epub epub : epubs) {
        if (epub.isValid()) {
            boolean registered = this.registerIsbn(epub);
            if (registered) {
                this.sendEpub(epub);
            }
        }
    }
}

重构后代码(卫语句)

public void distributeEpubs(final long bookId) {
    List<Epub> epubs = this.getEpubsByBookId(bookId);
    for (Epub epub : epubs) {
        this.distributeEpub(epub);
    }
}

private void distributeEpub(final Epub epub) {
    if (!epub.isValid()) {
        return;
    }
    if (!this.registerIsbn(epub)) {
        return;
    }
    this.sendEpub(epub);
}

问题代码(重复switch)

public double getBookPrice(User user, Book book) {
    switch (user.getLevel()) {
        case SILVER: return book.getPrice() * 0.9;
        case GOLD: return book.getPrice() * 0.8;
        case PLATINUM: return book.getPrice() * 0.75;
        default: return book.getPrice();
    }
}

public double getEpubPrice(User user, Epub epub) {
    switch (user.getLevel()) {
        case SILVER: return epub.getPrice() * 0.95;
        case GOLD: return epub.getPrice() * 0.85;
        case PLATINUM: return epub.getPrice() * 0.8;
        default: return epub.getPrice();
    }
}

重构后代码(多态)

interface UserLevel {
    double getBookPrice(Book book);
    double getEpubPrice(Epub epub);
}

class SilverUserLevel implements UserLevel {
    public double getBookPrice(Book book) {
        return book.getPrice() * 0.9;
    }
    public double getEpubPrice(Epub epub) {
        return epub.getPrice() * 0.95;
    }
}

// 使用
public double getBookPrice(User user, Book book) {
    return user.getUserLevel().getBookPrice(book);
}

5. 缺乏封装

问题代码(火车残骸)

String name = book.getAuthor().getName();

重构后代码

class Book {
    public String getAuthorName() {
        return this.author.getName();
    }
}

String name = book.getAuthorName();

问题代码(基本类型偏执)

public class Order {
    private double total;  // 直接用基本类型
    
    public void setTotal(double total) {
        if (total < 0) {
            throw new IllegalArgumentException("价格不能为负");
        }
        this.total = total;
    }
}

重构后代码

public class Order {
    private Money total;
    
    public Order(Money total) {
        this.total = total;
    }
}

public class Money {
    private final double amount;
    private final String currency;
    
    public Money(double amount, String currency) {
        if (amount < 0) {
            throw new IllegalArgumentException("金额不能为负");
        }
        this.amount = amount;
        this.currency = currency;
    }
    
    public double getDisplayPrice() {
        // 统一格式化逻辑
        return BigDecimal.valueOf(amount)
            .setScale(2, RoundingMode.HALF_UP)
            .doubleValue();
    }
}

6. 可变数据

问题代码(setter滥用)

public void approve(final long bookId) {
    Book book = repository.findById(bookId);
    book.setReviewStatus(ReviewStatus.APPROVED);  // 暴露内部状态
    repository.save(book);
}

重构后代码

public void approve(final long bookId) {
    Book book = repository.findById(bookId);
    book.approve();  // 封装状态变更
    repository.save(book);
}

class Book {
    private ReviewStatus reviewStatus;
    
    public void approve() {
        this.reviewStatus = ReviewStatus.APPROVED;
    }
    
    // 或者设计为不变类
    public Book approve() {
        return new Book(..., ReviewStatus.APPROVED, ...);
    }
}

7. 重复代码

问题代码(结构重复)

@Task
public void sendBook() {
    try {
        this.service.sendBook();
    } catch (Throwable t) {
        this.notification.send(new SendFailure(t)));
        throw t;
    }
}

@Task
public void sendChapter() {
    try {
        this.service.sendChapter();
    } catch (Throwable t) {
        this.notification.send(new SendFailure(t)));
        throw t;
    }
}

重构后代码

private void executeTask(Runnable task) {
    try {
        task.run();
    } catch (Throwable t) {
        this.notification.send(new SendFailure(t)));
        throw t;
    }
}

@Task
public void sendBook() {
    executeTask(this.service::sendBook);
}

@Task
public void sendChapter() {
    executeTask(this.service::sendChapter);
}

8. 不一致的代码

问题代码(命名不一致)

enum DistributionChannel {
    WEBSITE,      // 没有_ONLY
    KINDLE_ONLY,  // 有_ONLY
    ALL
}

重构后代码

enum DistributionChannel {
    WEBSITE,  // 统一命名
    KINDLE,   // 去掉_ONLY
    ALL
}

问题代码(方案不一致)

// 混合使用新旧日期API
public String nowTimestamp() {
    DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    Date now = new Date();  // 旧API
    return format.format(now);
}

重构后代码

public String nowTimestamp() {
    LocalDateTime now = LocalDateTime.now();  // 新API
    return now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
}

9. 依赖混乱

问题代码(缺少防腐层)

@PostMapping("/books")
public NewBookResponse createBook(final NewBookRequest request) {
    // 直接传递接口层对象到业务层
    boolean result = this.service.createBook(request);
    // ...
}

重构后代码

@PostMapping("/books")
public NewBookResponse createBook(final NewBookRequest request) {
    // 转换为业务参数
    NewBookParameter parameter = request.toNewBookParameter();
    boolean result = this.service.createBook(parameter);
    // ...
}

public class NewBookRequest {
    public NewBookParameter toNewBookParameter() {
        return new NewBookParameter(this.title, this.introduction, ...);
    }
}

10. 变量声明与赋值分离

问题代码

EpubStatus status = null;  // 假初始化
CreateEpubResponse response = createEpub(request);
if (response.getCode() == 201) {
    status = EpubStatus.CREATED;
} else {
    status = EpubStatus.TO_CREATE;
}

重构后代码

final CreateEpubResponse response = createEpub(request);
final EpubStatus status = toEpubStatus(response);

private EpubStatus toEpubStatus(final CreateEpubResponse response) {
    return response.getCode() == 201 ? 
        EpubStatus.CREATED : EpubStatus.TO_CREATE;
}

问题代码(集合初始化)

List<Permission> permissions = new ArrayList<>();  // 未完成初始化
permissions.add(Permission.BOOK_READ);
permissions.add(Permission.BOOK_WRITE);
check.grantTo(Role.AUTHOR, permissions);

重构后代码

List<Permission> permissions = List.of(
    Permission.BOOK_READ,
    Permission.BOOK_WRITE
);
check.grantTo(Role.AUTHOR, permissions);

11. 使用新语言特性

问题代码(空指针风险)

String name = book.getAuthor().getName();  // 可能NPE

重构后代码(Optional)

class Book {
    public Optional<Author> getAuthor() {
        return Optional.ofNullable(this.author);
    }
}

String name = book.getAuthor()
    .map(Author::getName)
    .orElse("Unknown");

问题代码(传统循环)

public ChapterParameters toParameters(List<Chapter> chapters) {
    List<ChapterParameter> parameters = new ArrayList<>();
    for (Chapter chapter : chapters) {
        if (chapter.isApproved()) {
            parameters.add(toChapterParameter(chapter));
        }
    }
    return new ChapterParameters(parameters);
}

重构后代码(函数式)

public ChapterParameters toParameters(List<Chapter> chapters) {
    List<ChapterParameter> parameters = chapters.stream()
        .filter(Chapter::isApproved)
        .map(this::toChapterParameter)
        .collect(Collectors.toList());
    return new ChapterParameters(parameters);
}

这些示例展示了如何识别和修复各种代码坏味道,核心思想是:封装、单一职责、减少重复、提高表达性。每个重构都让代码更易于理解、测试和维护。