Oli*_*ins 19 refactoring anti-patterns class god-object
有谁知道重构上帝对象的最佳方法?
它并不像将它分成许多较小的类那样简单,因为有很高的方法耦合.如果我拿出一种方法,我通常会把所有其他方法拉出去.
Fab*_*ler 34
就像Jenga.你需要耐心和稳定的手,否则你必须从头开始重新创造一切.这本身并不坏 - 有时需要丢掉代码.
其他建议:
Jen*_*der 12
我假设"上帝对象"意味着一个巨大的类(用代码行来衡量).
基本思想是将其部分功能提取到其他类中.
为了找到你可以寻找的那些
通常一起使用的字段/参数.他们可能会一起搬进一个新班级
在类中只使用一小部分字段的方法(或方法的一部分),可能会移动到只包含那些字段的类中.
原始类型(int,String,boolean).它们在出现之前通常是真正的价值对象.一旦它们成为价值对象,它们通常会吸引方法.
看一下神对象的用法.是否有不同的客户使用不同的方法?那些可能会进入单独的界面.这些接口可能反过来具有单独的实现.
要实际执行这些更改,您应该根据命令使用一些基础结构和工具:
测试:准备好(可能生成的)详尽的测试集,您可以经常运行.如果没有测试你做的改变要非常小心.我这样做,但将它们限制为像抽取方法这样的东西,我可以用一个IDE动作完全完成.
版本控制:您希望拥有一个允许您每2分钟提交一次的版本控制,而不会让您失望.SVN并没有真正起作用.Git呢.
Mikado方法:Mikado方法的想法是尝试改变.如果它工作得很好.如果没有注意到什么是破坏,请将它们作为依赖项添加到您开始的更改中.回滚你的变化.在结果图中,使用尚无依赖关系的节点重复该过程.http://mikadomethod.wordpress.com/book/
根据 Lanza 和 Marinescu 所著的《Object Oriented Metrics in Practice》一书,God Class 的设计缺陷是指倾向于集中系统智能的类。上帝类自己执行太多工作,仅将次要细节委托给一组琐碎的类并使用其他类的数据。
神级的检测基于三个主要特征:
重构上帝类是一项复杂的任务,因为这种不和谐通常是方法级别发生的其他不和谐的累积效应。因此,执行这样的重构需要有关类的方法的附加且更细粒度的信息,有时甚至需要有关其继承上下文的信息。第一种方法是识别捆绑在一起的方法和属性的集群,并将这些岛提取到单独的类中。
《面向对象的重新设计模式》一书中的拆分上帝类方法建议逐步将上帝类的职责重新分配给其协作类或从上帝类中拉出的新类。
《Working effective with Legacy Code》一书介绍了一些技术,例如 Sprout Method、Sprout Class、Wrap Method,以便能够测试可用于支持 God Class 重构的遗留系统。
我要做的就是对 God 类中的方法进行分组,这些方法利用相同的类属性作为输入或输出。之后,我会将类拆分为子类,其中每个子类将保存子组中的方法以及这些方法使用的属性。
这样,每个新类都会更小、更一致(这意味着它们的所有方法都将适用于类似的类属性)。此外,我们生成的每个新类的依赖性都会减少。之后,我们可以进一步减少这些依赖关系,因为我们现在可以更好地理解代码。
一般来说,我会说根据具体情况有几种不同的方法。举个例子,假设您有一个名为“LoginManager”的上帝类,它验证用户信息,更新“OnlineUserService”,以便将用户添加到在线用户列表中,并返回特定于登录的数据(例如欢迎屏幕和一次)提供)给客户。
所以你的类看起来像这样:
import java.util.ArrayList;
import java.util.List;
public class LoginManager {
public void handleLogin(String hashedUserId, String hashedUserPassword){
String userId = decryptHashedString(hashedUserId);
String userPassword = decryptHashedString(hashedUserPassword);
if(!validateUser(userId, userPassword)){ return; }
updateOnlineUserService(userId);
sendCustomizedLoginMessage(userId);
sendOneTimeOffer(userId);
}
public String decryptHashedString(String hashedString){
String userId = "";
//TODO Decrypt hashed string for 150 lines of code...
return userId;
}
public boolean validateUser(String userId, String userPassword){
//validate for 100 lines of code...
List<String> userIdList = getUserIdList();
if(!isUserIdValid(userId,userIdList)){return false;}
if(!isPasswordCorrect(userId,userPassword)){return false;}
return true;
}
private List<String> getUserIdList() {
List<String> userIdList = new ArrayList<>();
//TODO: Add implementation details
return userIdList;
}
private boolean isPasswordCorrect(String userId, String userPassword) {
boolean isValidated = false;
//TODO: Add implementation details
return isValidated;
}
private boolean isUserIdValid(String userId, List<String> userIdList) {
boolean isValidated = false;
//TODO: Add implementation details
return isValidated;
}
public void updateOnlineUserService(String userId){
//TODO updateOnlineUserService for 100 lines of code...
}
public void sendCustomizedLoginMessage(String userId){
//TODO sendCustomizedLoginMessage for 50 lines of code...
}
public void sendOneTimeOffer(String userId){
//TODO sendOneTimeOffer for 100 lines of code...
}}
Run Code Online (Sandbox Code Playgroud)
现在我们可以看到这个类将是庞大而复杂的。根据书本定义,它还不是上帝类,因为类字段现在在方法中普遍使用。但为了论证方便,我们可以将其视为神类,开始重构。
解决方案之一是创建单独的小类,将其用作主类中的成员。您可以添加的另一件事是分离不同接口及其各自类中的不同行为。通过将这些方法设置为“私有”来隐藏类中的实现细节。并使用主类中的这些接口来执行其命令。
所以最后,RefactoredLoginManager 将如下所示:
public class RefactoredLoginManager {
IDecryptHandler decryptHandler;
IValidateHandler validateHandler;
IOnlineUserServiceNotifier onlineUserServiceNotifier;
IClientDataSender clientDataSender;
public void handleLogin(String hashedUserId, String hashedUserPassword){
String userId = decryptHandler.decryptHashedString(hashedUserId);
String userPassword = decryptHandler.decryptHashedString(hashedUserPassword);
if(!validateHandler.validateUser(userId, userPassword)){ return; }
onlineUserServiceNotifier.updateOnlineUserService(userId);
clientDataSender.sendCustomizedLoginMessage(userId);
clientDataSender.sendOneTimeOffer(userId);
}
}
Run Code Online (Sandbox Code Playgroud)
解密处理程序:
public class DecryptHandler implements IDecryptHandler {
public String decryptHashedString(String hashedString){
String userId = "";
//TODO Decrypt hashed string for 150 lines of code...
return userId;
}
}
public interface IDecryptHandler {
String decryptHashedString(String hashedString);
}
Run Code Online (Sandbox Code Playgroud)
验证处理程序:
public class ValidateHandler implements IValidateHandler {
public boolean validateUser(String userId, String userPassword){
//validate for 100 lines of code...
List<String> userIdList = getUserIdList();
if(!isUserIdValid(userId,userIdList)){return false;}
if(!isPasswordCorrect(userId,userPassword)){return false;}
return true;
}
private List<String> getUserIdList() {
List<String> userIdList = new ArrayList<>();
//TODO: Add implementation details
return userIdList;
}
private boolean isPasswordCorrect(String userId, String userPassword)
{
boolean isValidated = false;
//TODO: Add implementation details
return isValidated;
}
private boolean isUserIdValid(String userId, List<String> userIdList)
{
boolean isValidated = false;
//TODO: Add implementation details
return isValidated;
}
}
Run Code Online (Sandbox Code Playgroud)
这里需要注意的重要一点是,interfaces() 只需包含其他类使用的方法。所以 IValidateHandler 看起来就像这样简单:
public interface IValidateHandler {
boolean validateUser(String userId, String userPassword);
}
Run Code Online (Sandbox Code Playgroud)
在线用户服务通知程序:
public class OnlineUserServiceNotifier implements
IOnlineUserServiceNotifier {
public void updateOnlineUserService(String userId){
//TODO updateOnlineUserService for 100 lines of code...
}
}
public interface IOnlineUserServiceNotifier {
void updateOnlineUserService(String userId);
}
Run Code Online (Sandbox Code Playgroud)
客户端数据发送者:
public class ClientDataSender implements IClientDataSender {
public void sendCustomizedLoginMessage(String userId){
//TODO sendCustomizedLoginMessage for 50 lines of code...
}
public void sendOneTimeOffer(String userId){
//TODO sendOneTimeOffer for 100 lines of code...
}
}
Run Code Online (Sandbox Code Playgroud)
由于这两个方法都是在 LoginHandler 中访问的,因此接口必须包含这两个方法:
public interface IClientDataSender {
void sendCustomizedLoginMessage(String userId);
void sendOneTimeOffer(String userId);
}
Run Code Online (Sandbox Code Playgroud)