我有一个关于代码重复和重构的问题,希望它不是太笼统。假设您有一小段代码(~5行),这是一系列函数调用,它们不是非常低级别的。这个代码在几个地方重复,因此在这里提取方法可能是个好主意。然而,在这个特定的例子中,这个新的功能将遭受低内聚(其中,其中,通过难以找到该功能的良好名称)表现出来。原因可能是因为这个重复的代码只是更大算法的一部分 - 并且很难将其划分为命名良好的步骤。
在这种情况下你会建议什么?
编辑:
我希望将问题保持在一般水平,以便更多人可能发现它有用,但显然最好用一些代码示例来支持它。这个例子可能不是有史以来最好的(它在很多方面都有气味),但我希望它能够完成它的工作:
class SocketAction {
private static class AlwaysCreateSessionLoginHandler extends LoginHandler {
@Override
protected void onLoginCorrect(SocketAction socketAction) throws IllegalAccessException, IOException {
Server.checkAllowedDeviceCount(socketAction._sess.getDeviceID());
socketAction.registerSession();
socketAction._sess.runApplication();
}
}
private static class AutoConnectAnyDeviceLoginHandler extends LoginHandler {
@Override
protected void onLoginCorrect(SocketAction socketAction) throws IllegalAccessException, IOException {
if (Server.isUserRegistered(socketAction._sess.getUserLogin())) {
Log.logSysInfo("Session autoconnect - acquiring list of action threads...");
String[] sa = Server.getSessionList(socketAction._sess.getUserID());
Log.logSysInfo("Session autoconnect - list of action threads acquired.");
for (int i = 0; i < sa.length; i += 7) {
socketAction.abandonCommThreads();
Server.attachSocketToSession(sa[i + 1], socketAction._commSendThread.getSock());
return;
}
}
Server.checkAllowedDeviceCount(socketAction._sess.getDeviceID());
socketAction.registerSession();
socketAction._sess.runApplication();
}
}
private static class OnlyNewSessionLoginHandler extends LoginHandler {
@Override
protected void onLoginCorrect(SocketAction socketAction) throws IllegalAccessException, IOException {
socketAction.killOldSessionsForUser();
Server.checkAllowedDeviceCount(socketAction._sess.getDeviceID());
socketAction.registerSession();
socketAction._sess.runApplication();
}
}
}
答案 0 :(得分:8)
问题太笼统,不能说,但作为练习:
假设你抽象它。想想想要改变最终的5行功能的可能原因是什么。您是否希望进行适用于所有用户的更改,或者您是否最终必须编写与旧版本稍有不同的新功能,每次某些调用者都有理由要进行更改?
如果您想为所有用户更改它,那么这是一个可行的抽象。现在给它一个不好的名字,你以后可能会想到一个更好的名字。
如果您最终将此功能拆分为许多类似版本,而您的代码将来会发展,那么它可能不是一个可行的抽象。您仍然可以编写该函数,但它更像是一个代码保存的“辅助函数”,而不是正式问题模型的一部分。这不是很令人满意:重复这段代码有点令人担忧,因为它表明应该在那里是一个可行的抽象。
也许5条线中的4条可以被抽象出来,因为它们更具凝聚力,而第五条线正好恰好与它们闲在一起。然后你可以编写两个新函数:一个是这个新的抽象,另一个是调用新函数然后执行第5行的帮助器。其中一个函数可能比另一个具有更长的预期使用寿命。
答案 1 :(得分:2)
对我来说,石蕊测试是:如果我需要在一个地方对这段代码进行更改,(例如添加一行,更改顺序),我是否需要进行相同的更改其他地方?
如果答案是肯定的,那么它是一个逻辑的“原子”实体,应该重构。如果答案是否定的,那么这是一系列恰好在多种情况下运作的操作 - 如果重构,将来可能会给您带来更多麻烦。
答案 2 :(得分:1)
我最近在考虑这个问题,而且我完全理解你的目标。在我看来,这是一个实现抽象而不是任何东西,因此如果你可以避免更改接口,那么它更适合。例如,在C ++中,我可能会在不触及标题的情况下将函数提取到cpp中。这有点减少了函数抽象对于类用户的良好形式和有意义的要求,因为它们在它们真正需要它之前是不可见的(当添加到实现时)。
答案 3 :(得分:1)
对我来说,操作词是“门槛”。另一个词可能是“闻”。
事情始终处于平衡状态。听起来像(在这种情况下)平衡的中心是凝聚力(酷);因为你只有少量重复,所以不难管理。
如果你发生了一些重大的“事件”并且你去了“1000”重复,那么余额就会变得很狡猾,所以你可能会接近。
对我来说,一些可管理的副本不是重构的信号(还);但是我会留意它。
答案 4 :(得分:0)
继承是你的朋友!
不要重复代码。即使单行代码很长或很困难,也要将其重构为具有独特名称的单独方法。把它想象成会在一年内阅读您的代码的人。如果你将这个函数命名为“blabla”,下一个人会不知道这个函数在没有读取代码的情况下做了什么?如果没有,您需要更改名称。经过一周的思考,你会习惯它,你的代码将 12%更具可读性! ;)
class SocketAction {
private static abstract class CreateSessionLoginHandler extends LoginHandler {
@Override
protected void onLoginCorrect(SocketAction socketAction) throws IllegalAccessException, IOException {
Server.checkAllowedDeviceCount(socketAction._sess.getDeviceID());
socketAction.registerSession();
socketAction._sess.runApplication();
}
}
private static class AlwaysCreateSessionLoginHandler extends CreateSessionLoginHandler;
private static class AutoConnectAnyDeviceLoginHandler extends CreateSessionLoginHandler {
@Override
protected void onLoginCorrect(SocketAction socketAction) throws IllegalAccessException, IOException {
if (Server.isUserRegistered(socketAction._sess.getUserLogin())) {
Log.logSysInfo("Session autoconnect - acquiring list of action threads...");
String[] sa = Server.getSessionList(socketAction._sess.getUserID());
Log.logSysInfo("Session autoconnect - list of action threads acquired.");
for (int i = 0; i < sa.length; i += 7) {
socketAction.abandonCommThreads();
Server.attachSocketToSession(sa[i + 1], socketAction._commSendThread.getSock());
return;
}
}
super.onLoginCorrect(socketAction);
}
}
private static class OnlyNewSessionLoginHandler extends CreateSessionLoginHandler {
@Override
protected void onLoginCorrect(SocketAction socketAction) throws IllegalAccessException, IOException {
socketAction.killOldSessionsForUser();
super.onLoginCorrect(socketAction);
}
}
}