复制并粘贴长而直接的代码而不是将它们包装到类或函数中是否可以接受?


29

假设我有一段代码可以连接到互联网并显示类似的连接结果:

HttpRequest* httpRequest=new HttpRequest();
httpRequest->setUrl("(some domain .com)");
httpRequest->setRequestType(HttpRequest::Type::POST);
httpRequest->setRequestData("(something like name=?&age=30&...)");
httpRequest->setResponseCallback([=](HttpClient* client, HttpResponse* response){
    string responseString=response->getResponseDataString();
        if(response->getErrorCode()!=200){
            if(response->getErrorCode()==404){
                Alert* alert=new Alert();
                alert->setFontSize(30);
                alert->setFontColor(255,255,255);
                alert->setPosition(Screen.MIDDLE);
                alert->show("Connection Error","Not Found");
            }else if((some other different cases)){
                (some other alert)
            }else
                Alert* alert=new Alert();
                alert->setFontSize(30);
                alert->setPosition(Screen.MIDDLE);
                alert->setFontColor(255,255,255);
                alert->show("Connection Error","unknown error");
            }
        }else{
            (other handle methods depend on different URL)
        }
}

代码很长,很常用,但是上面的代码不需要任何其他东西,例如自定义函数和类(默认情况下,框架都提供了HttpRequest和Alert),尽管代码很长,但是简单,不复杂(很长一段时间,因为有很多设置,例如url,字体大小...),并且代码段在类之间几乎没有变化(例如:url,请求数据,错误代码句柄情况,普通句柄)情况...)

我的问题是,复制并粘贴长而直接的代码,而不是将它们包装在函数中以减少代码的依赖性,这是否可以接受?


89
假设您在该代码中有一个错误,例如不释放分配的对象。(您的框架会释放Alert对象吗?)现在想象您必须找到该代码的每个复制实例来修复该错误。现在想象不是必须要做的,而是一个疯狂的斧头杀人犯,他知道您是首先创建所有这些副本的人。
塞巴斯蒂安·雷德尔

8
而且,顺便说一句,将网络和错误显示混合在一个地方已经是很大的禁忌,恕我直言。
sleske '16

11
没有永不。完全不能接受。如果您在我的项目中,那么您将不再在我的项目中,而将参加培训计划或PIP。
nhgrif

10
随之而来的主管说:“我屏幕中间的警告框绝对是恐怖。我正在看猫跳动,每次弹出时都会挡住我的视线。请将其移到右上角”。3周后, “您到底做了什么?!我无法再关闭猫夹,因为您的弹出窗口遮盖了右上角的X,请修复它。”
MonkeyZeus

11
我想这里的每个人似乎都认为这是个坏主意。但是要解决这个问题,为什么不将这段代码放在单独的类或函数中呢?
Karl Gjertsen

Answers:


87

您需要考虑变更成本。如果您想更改连接方式怎么办?这有多容易?如果您有很多重复的代码,那么查找所有需要更改的位置可能会非常耗时且容易出错。

您还需要考虑清晰度。最有可能的是,仅需查看30行代码就不会像对“ connectToInternet”函数的单个调用那样容易理解。当需要添加新功能时,尝试理解代码会浪费多少时间?

在某些罕见的情况下,复制不是问题。例如,如果您正在做一个实验,而代码将在一天结束时被丢弃。但是总的来说,复制的成本要比节省不必要的代码节省的时间少得多,因为不必将代码提取到单独的函数中

另请参阅https://softwareengineering.stackexchange.com/a/103235/63172


19
...谁知道这30行与您之前看过的其他行是否真的相同,或者有人出于某种原因而切换到其副本中的其他端口或IP地址。隐含的假设“ 以开头的大约30行的任何一束HttpRequest都是相同的 ”很容易造成谬误。
空值

@null非常好。我曾经写过一些代码,其中复制并粘贴了数据库连接连接,但是其中一些设置存在细微的差异。我不知道这些是重要的,故意的更改还是仅仅是随机的差异
user949300 2016年

54

没有。

实际上,即使您的“简单”代码也应拆分为较小的部分。至少两个。

一个进行连接并处理正常的200响应。例如,如果在某些情况下从POST更改为PUT怎么办?如果您要建立大量的这些连接并且需要一些多线程或连接池怎么办?将代码放在一个位置,并带有方法的参数将使此操作变得更加容易

同样,另一个处理错误。例如,如果您更改警报的颜色或字体大小。或者您遇到间歇性连接问题,并希望记录错误。



你也可以引述SRP:具有代码块只有一个目的使得它如此更容易理解和维护..
罗兰·特普

这是DRY和SRP实际上对齐的一种情况。有时他们没有。
user949300 '16

18

复制粘贴是否可以接受...

没有。

对我而言,决定性的论据是:

...它是常用的...

如果您在多个地方使用了一段代码,那么它更改时,您必须在多个地方进行更改,否则开始出现不一致的地方-“奇怪的事情”开始发生(即,引入了Bug)。

它简单明了而不复杂...

因此,应该更容易将其重构为功能。

...有很多设置,例如网址,字体大小...

用户喜欢改变什么?字体,字体大小,颜色等。

现在; 您必须在几个地方更改同一段代码,以使它们再次具有相同的颜色/字体/大小?(推荐答案:仅一个)。

...该代码段在类之间几乎没有变化(例如:url,请求数据,错误代码句柄情况,普通句柄情况...)

变化=>功能参数。


“用户喜欢更改什么?字体,字体大小,颜色等”就这样解决。您是否真的要在数十个位置进行更改?
2013年

8

这实际上与复制和粘贴没有任何关系。如果您从其他地方获取代码,那么第二步您将获取代码是您的代码和您的责任,因此,无论是完全复制还是自己编写,都无济于事。

在警报中,您需要做出一些设计决策。应该为所有警报做出最可能的类似设计决策。因此,您可能应该在“ ShowAlertInAStyleSuitableForMyApplication”某处或稍短一些的地方使用一个方法,并且应该调用该方法。

您将有许多具有类似错误处理的http请求。您应该不应该一次又一次地重复错误处理,而应该提取常见的错误处理。尤其是如果您的错误处理更加复杂(关于超时错误,401等)。


6

在某些情况下可以复制。但是这不是。该方法太复杂了。有一个下限,当复制比“提出”方法更容易时。

例如:

def add(a, b)
    return a + b
end

是愚蠢的,只需执行a + b。

但是,当您变得稍微复杂一点时,通常您会越过界限。

foo.a + foo.b

应该成为

foo.total
def foo
    ...
    def total
        return self.a + self.b
    end
end

就您而言,我看到了四个“方法”。大概在不同的班级。一个发出请求,一个获得响应,一个显示错误,并在响应返回以处理响应后调用某种回调。我个人可能会在此基础上添加一个“包装器”,以使调用更加容易。

最后,要发出Web请求,我希望呼叫看起来像这样:

Web.Post(URI, Params, ResponseHandler);

那一行就是我在代码中的全部内容。然后,当我需要对“我如何获得东西”进行更改时,我可以用更少的精力快速地做到这一点。

这还将使代码保持干燥,并有助于SRP


0

在任何大小/复杂性的项目中,我都希望能够在需要时出于以下目的而查找代码:

  1. 坏了时修复
  2. 更改功能
  3. 重用它。

加入一个进行中的项目或继续从事一个项目几年会不是很可爱,而由于有一个好的设计,而不是依靠整个代码来搜索httprequest?无论如何,使用Google可能更容易找到它。

不用担心,我是新手,我将重构此代码块,因为我对于以糟糕的代码库加入这个无能为力的团队感到非常沮丧,或者我现在正承受着其他团队的巨大压力您,并将其复制并粘贴。至少它将使老板不为所动。然后,当该项目真正被确定为灾难时,我将第一个建议我们通过将其复制并粘贴到我们都不了解的最新,最出色的框架中来重写它。


0

至少在我看来,类和/或函数更好。一次,它使文件变小,如果您处理Web应用程序或用于存储量很少的设备的应用程序(物联网,旧手机等),这将是一个很大的收获。

显然,最好的一点是,如果由于新协议等原因需要更改某些内容,则只需更改函数的内容即可,无数次地将此函数放置在某个位置,该位置甚至可能位于不同的文件中,从而使它们变得更难寻找和改变。

我编写了一个完整的SQL解释器,因此我可以从PHP 更好地从MySQL切换到MySQLi,因为我只需要更改解释器,一切就可以了,尽管这是一个极端的例子。


-1

要确定是应该复制代码还是将代码移到调用两次的函数,请尝试确定哪个更有可能:

  1. 有必要以相同的方式更改代码的两种用法。

  2. 有必要更改代码的至少一种用法,以使它们有所不同。

在第一种情况下,最好使用一个函数来处理这两种用法。在后一种情况下,针对这两种用法使用单独的代码可能会更好。

在确定应一次性使用一段代码还是将其插入到另一函数中时,请弄清楚如何充分描述该函数所需的行为。如果对功能所需行为的完整而准确的描述将与代码本身一样长或更长,那么将代码移到单独的功能中可能会使事情变得更难于理解,而不是更加容易。如果第二个调用者很有可能需要使用相同的功能,并且将来对该功能所做的任何更改都将影响两个调用者,那么仍然值得这样做,但是如果没有这种考虑,可读性将有利于拆分事物到代码及其所需行为的描述将大约同样长的程度。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.