3分钟 3次迭代 再现代码腐朽变质过程-《代码不朽》-架构师书房

大家好,我是码农老吴,欢迎收看极客架构师推出的架构师书房栏目。今天,我继续给大家解读《代码不朽》这本书。

上期我们一起回顾了前面五章的内容,涉及4个原则,6个重构技巧,用于重构和优化方法级别的代码。本期,我们开始分享第6章的内容,第五个原则,分离模块之间的关注点(Separate Concerns in Modules)。从本章开始,我们开始分享模块级别的原则及重构技巧。我们将站在一个新的高度,站在一个架构师的高度,看待我们的代码质量。


3分钟 3次迭代 再现代码腐朽变质过程-《代码不朽》-架构师书房


我们用几分钟时间,看看一个用户服务模块,如何在三次的迭代中,慢慢腐坏的,大家应该会感同身受的。代码来自一个我们java程序员比较熟悉的项目类型,web应用程序项目,服务层的UserService类。

第一版 我本单纯

服务层:UserService类

package eu.sig.training.ch06.userservice.v1;

import eu.sig.training.ch06.userservice.User;
import eu.sig.training.ch06.userservice.UserInfo;

@SuppressWarnings("unused")
// tag::UserService[]
public class UserService {
    public User loadUser(String userId) {
        // ...
        // end::UserService[]
        return new User();
        // tag::UserService[]
    }

    public boolean doesUserExist(String userId) {
        // ...
        // end::UserService[]
        return true;
        // tag::UserService[]
    }

    public User changeUserInfo(UserInfo userInfo) {
        // ...
        // end::UserService[]
        return new User();
        // tag::UserService[]
    }
}
// end::UserSerice[]

表示层UserRestAPI

package eu.sig.training.ch06.userservice.v1;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.Response;

import eu.sig.training.ch06.userservice.User;

// tag::UserRestAPI[]
// The @Path and @GET attributes are defined by the the Java REST Service API
@Path("/user")
public class UserRestAPI {

    private final UserService userService = new UserService();

    // ...
    // end::UserRestAPI[]
    public Response toJson(@SuppressWarnings("unused") User u) {
        return Response.accepted().build();
    }
    // tag::UserRestAPI[]

    @GET
    @Path("/{userId}")
    public Response getUser(@PathParam(value = "userId") String userId) {
        User user = userService.loadUser(userId);
        return toJson(user);
    }
}
// end::UserRestAPI[]

点评

第一版代码,我们的故事主人公,UserService类,还是一个翩翩少年,单纯,善良,而且专一。

全心全意的给表示层的UserRestAPI类,提供loadUser(),doesUserExist,changeUserInfo 三个服务。

第二次迭代 成长了,有二心了

服务层:UserService类

package eu.sig.training.ch06.userservice.v2;

import java.util.ArrayList;
import java.util.List;

import eu.sig.training.ch06.userservice.NotificationType;
import eu.sig.training.ch06.userservice.User;
import eu.sig.training.ch06.userservice.UserInfo;

@SuppressWarnings("unused")
// tag::UserService[]
public class UserService {
    public User loadUser(String userId) {
        // ...
        // end::UserService[]
        return new User();
        // tag::UserService[]
    }

    public boolean doesUserExist(String userId) {
        // ...
        // end::UserService[]
        return true;
        // tag::UserService[]
    }

    public User changeUserInfo(UserInfo userInfo) {
        // ...
        // end::UserService[]
        return new User();
        // tag::UserService[]
    }

    public List getNotificationTypes(User user) {
        // ...
        // end::UserService[]
        return new ArrayList();
        // tag::UserService[]
    }

    public void registerForNotifications(User user, NotificationType type) {
        // ...
    }

    public void unregisterForNotifications(User user, NotificationType type) {
        // ...
    }
}
// end::UserSerice[]

表示层NotificationRestAPI

package eu.sig.training.ch06.userservice.v2;

import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.Response;

import org.apache.http.HttpStatus;

import eu.sig.training.ch06.userservice.NotificationType;
import eu.sig.training.ch06.userservice.User;

// tag::NotificationRestAPI[]
@Path("/notification")
public class NotificationRestAPI {
    private final UserService userService = new UserService();

    // ...
    // end::NotificationRestAPI[]
    public Response toJson(@SuppressWarnings("unused") int status) {
        return Response.accepted().build();
    }
    // tag::NotificationRestAPI[]

    @POST
    @Path("/register/{userId}/{type}")
    public Response register(@PathParam(value = "userId") String userId,
        @PathParam(value = "type") String notificationType) {
        User user = userService.loadUser(userId);
        userService.registerForNotifications(user, NotificationType.fromString(notificationType));
        return toJson(HttpStatus.SC_OK);
    }

    @POST
    @Path("/unregister/{userId}/{type}")
    public Response unregister(@PathParam(value = "userId") String userId,
        @PathParam(value = "type") String notificationType) {
        User user = userService.loadUser(userId);
        userService.unregisterForNotifications(user, NotificationType.fromString(notificationType));
        return toJson(HttpStatus.SC_OK);
    }
}
// end::NotificationRestAPI[]

点评

项目在发展,增加了消息功能(notification),用户可以注册自己感兴趣的消息,对于已经注册的消息,也可以取消。因为是给用户提供这些服务的。这个模块的开发人员,首先想到了我们的主人公UserService类,让他承担更多的责任。给表示层的NotificationRestAPI,提供了三个方法,getNotificationTypes(),registerForNotifications(),and unregisterForNotifications.

主人公UserService身体变得健壮了,服务的对象也多了,但是也不再专一了。

第三次迭代,臃肿了,油腻了

服务层:UserService类

package eu.sig.training.ch06.userservice.v3;

import java.util.ArrayList;
import java.util.List;

import eu.sig.training.ch06.userservice.NotificationType;
import eu.sig.training.ch06.userservice.User;
import eu.sig.training.ch06.userservice.UserInfo;

@SuppressWarnings("unused")
// tag::UserService[]
public class UserService {
    public User loadUser(String userId) {
        // ...
        // end::UserService[]
        return new User();
        // tag::UserService[]
    }

    public boolean doesUserExist(String userId) {
        // ...
        // end::UserService[]
        return true;
        // tag::UserService[]
    }

    public User changeUserInfo(UserInfo userInfo) {
        // ...
        // end::UserService[]
        return new User();
        // tag::UserService[]
    }

    public List getNotificationTypes(User user) {
        // ...
        // end::UserService[]
        return new ArrayList();
        // tag::UserService[]
    }

    public void registerForNotifications(User user, NotificationType type) {
        // ...
    }

    public void unregisterForNotifications(User user, NotificationType type) {
        // ...
    }

    public List searchUsers(UserInfo userInfo) {
        // ...
        // end::UserService[]
        return new ArrayList();
        // tag::UserService[]
    }

    public void blockUser(User user) {
        // ... 
    }

    public List getAllBlockedUsers() {
        // ...
        // end::UserService[]
        return new ArrayList();
        // tag::UserService[]
    }
}
// end::UserSerice[]

点评

项目在发展,客户又有了新的需求,需要提供新的功能,包含搜索用户,锁定用户,以及列举所有被被锁定的用户。用户,用户,还是用户,程序员接到这个需求,二话没说,把这些责任,又交给了我们的主人公UserService。

现在,UserService的身体臃肿了,自身代码行超过了300行,慢慢表现出了中年人的油腻,服务的对象,也由原来的唯一一个,增加到了三个。

过大类坏味道(large class smell)

UserService现在的这种处境,在代码质量专家看来,就是典型的“过大类坏味道(large class smell)”,这种过大的类,往往表现为以下特征。

  • 类的代码多
  • 类的功能多而杂
  • 类被依赖多

如何重构过度膨胀的类,我们下期再聊。

极客架构师,专注架构师成长,我们下期见。

发表评论
留言与评论(共有 0 条评论) “”
   
验证码:

相关文章

推荐文章