大家好,我是码农老吴,欢迎收看极客架构师推出的架构师书房栏目。今天,我继续给大家解读《代码不朽》这本书。
上期我们一起回顾了前面五章的内容,涉及4个原则,6个重构技巧,用于重构和优化方法级别的代码。本期,我们开始分享第6章的内容,第五个原则,分离模块之间的关注点(Separate Concerns in Modules)。从本章开始,我们开始分享模块级别的原则及重构技巧。我们将站在一个新的高度,站在一个架构师的高度,看待我们的代码质量。
我们用几分钟时间,看看一个用户服务模块,如何在三次的迭代中,慢慢腐坏的,大家应该会感同身受的。代码来自一个我们java程序员比较熟悉的项目类型,web应用程序项目,服务层的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[]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 三个服务。
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[] 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身体变得健壮了,服务的对象也多了,但是也不再专一了。
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行,慢慢表现出了中年人的油腻,服务的对象,也由原来的唯一一个,增加到了三个。
UserService现在的这种处境,在代码质量专家看来,就是典型的“过大类坏味道(large class smell)”,这种过大的类,往往表现为以下特征。
如何重构过度膨胀的类,我们下期再聊。
极客架构师,专注架构师成长,我们下期见。
| 留言与评论(共有 0 条评论) “” |