您遇到的最糟糕的反模式[关闭]


9

作为程序员,您在职业生涯中遇到过的最糟糕的反模式是什么?

我主要参与Java,尽管它可能与语言无关。

我认为最糟糕的是我所说的主要反模式。这意味着程序由包含所有逻辑的单个非常大的类(有时伴随一对小类)组成。通常情况下,这是一个包含所有业务逻辑的大循环,有时会有成千上万的代码行。

Answers:


38

注释掉代码。它的块,也许几百行。从理论上讲,嘿,它被注释掉了,它没有任何危害,也许我们将来会需要它。


7
好吧,至少您可以安全地删除它。与我合作过的人不同,他经常会重命名过程并使死代码可访问。布莱克
杰伊

@Cyrena,你也和Eric合作过吗?!?
CaffGeek

我曾经有一堆使用#ifdef COMMENT锁定代码的代码。在有人定义“ COMMENT”之前,它的工作非常出色。
Michael Kohne'2

9
这是使用版本控制获得回报的另一种情况。您可以删除代码,而不必询问将来是否需要它,因为可以通过几次按键再次检索它。
詹森·B

5
我认为已注释掉的代码有一个地方,如果您在注释前面加上注释,以解释其原因。有时,我会留下一个注释框,代表我正在考虑的一条可能的道路,并在注释中留下为什么没有这样做的注释。@Jason,我肯定听说过这是版本控制的角色,但是在版本控制中删除的代码不是很容易被发现。
nlawalker

19

我将用“ copy-pasta”打另一个明显的例子。复制几乎与您想要的代码相同的代码,然后进行一些更改(而不是提取到方法中)。

这在90年代后期的某些功能和API测试代码中尤其普遍:从字面上看,成百上千个(甚至上千个)几乎完全相同的测试用例,可以分解成几个带有3个或4个参数的功能-甚至更好,由数据驱动。我大学毕业后的第一份工作实际上是8个月的重写和重构使用过时方法的数千行复制粘贴。到我完成时,测试文件还不到其原始大小的十分之一,并且更易于维护(并且可读!)。


16

我想我可以写很多有关Pattern Mania和解决方案的文章,这些解决方案可以用更少的精力解决,但我想指出的是我最近读过的一篇很棒的文章,上面有一个很好的例子说明了如何简化简单的解决方案。

如何(不)用Java编写阶乘,还是我们在您的算法中加入了工厂


3
太棒了!现在,FactorialWebService在哪里?:P
FrustratedWithFormsDesigner

1
我称它为“模式狂热”……每个人都可以在职业生涯的某一时刻得到它。我们当中的更好者超越了它。我接受了一次采访,那个家伙问我何时应该考虑模式。我告诉他,我让他们发展成为系统。他说:“不,您应该从一开始就使用模式。”
迈克尔·布朗

FactoryFactories只是想要尽可能多地推迟实际选择的一种征兆,但最终还是要么硬编码它,要么将外部字符串值映射到一段代码。依赖注入是一个更好的解决方案。

模式是设计良好的工件-应该将其同样对待。我喜欢将它们描述为定义而不是解决方案。它们在交流时很有用,但在设计中不一定有用。
Michael K'2

对此表示感谢,祝您阅读愉快。
Syg 2011年


14

地区

在C#中,您可以定义可在IDE中折叠的代码区域,从而将其隐藏,除非您要处理该代码。我一直在(目前正在进行)一个项目,该区域跨越数百条线(希望我夸大了),千行功能中有多个区域(再次希望我在开玩笑)。

从好的方面来说,成为该地区的开发商在识别区域内的特定功能方面做得很好。如此之多,以至于我能够对该区域执行提取方法并继续前进。

地区鼓励开发人员“隐藏”他们的废话。


15
区域+1鼓励开发人员“隐藏”他们的废话。但是,如果使用得当,我发现区域可以在逻辑上将代码分组在一起,并在以后轻松找到。
达伦·杨

长期以来,折叠编辑器一直存在这个问题。我上次在OCCAM中编程时曾经做过同样的事情。
Michael Kohne'2

2
我喜欢地区...但仅用于组织机构...不隐藏大量的代码。
IAbstract

3
+1。这就是为什么使用区域违反StyleCop规则SA1124的原因DoNotUseRegions
2011年

1
我有一些在SQL中处理大量字段的项目,而更新/创建它的代码却很多行。一个区域#region SQL Update使其可折叠,因此需要较少的滚动。
JYelton'2


9

不重载的方法:

// Do something
public int doSomething(Data d);

// Do same thing, but with some other data
public int doSomething2(SomeOtherData d);

清楚地表明程序员不了解重载。



5

软编码,即当程序员竭尽所能避免进行硬编码并最终导致规模的另一面时,即“硬编码”依赖性。


4

在客户端保持状态。

一旦与所有状态都保存在浏览器中的Web应用程序一起使用。(为确保无故障可伸缩性,所有异常均被忽略)。


您能详细说明一下吗?恕我直言,对于Web应用程序,当唯一的状态是在浏览器(即cookie)中并且服务器“不共享任何内容”时,这很好。还是在“应用程序数据库”中表示“状态”?
keppla 2011年

状态:按实际数据进行操作。

OO,你有我最深的同情。
keppla

4

大量签到

我讨厌看到开发人员在一个星期内没有办理入住手续。这意味着他要么卡住了,没有寻求帮助,要么他将一大堆功能集中到一个大型登机手续中。(我没有考虑最坏的情况,他只是什么都没做。这很容易解决...听起来像两个字,就像您被录用了。)

如果要进行大量检查,则会失去SCM的许多好处,例如能够将变更集链接到特定功能。另外,您可能需要进行很多合并,而且不一定很容易就正确。


1
不做任何事情并不总是最坏的情况。有时最好是从头开始编写,而不是重新编写代码……
Malachi

4

当前,我正在处理一段旧代码,并且我喜欢以前的编码器获取列表的第一个元素的方式:

String result;
for(int i = 0; i < someList.size(); i++) {
    result = someList.get(i);
    break;
}

但是我在这段代码中看到的最糟糕的情况是定义内联JSP页面的类,使用scriptlet和out.println编写所有HTML,CSS和Javascript:print(


4

我见过的最严重的行为反模式之一是一家商店,该商店仅在生产后才允许将代码检入版本控制。加上VSS中的独占签出,这导致了一个繁琐的过程,即如果需要在下一个发行版之前纠正生产缺陷,则要取消签出,更不用说需要多个人为下一个发行版更改给定的文件。

这是部门政策,这一事实使其比单个开发人员的行为甚至更糟。


3

锁定字符串文字

synchronized("one") { /* block one A*/ }

synchronized("one") { /* block one B*/ }

类名很长。(在JRE中)

com.sun.java.swing.plaf.nimbus.
InternalFrameInternalFrameTitlePaneInternalFrameTitlePaneMaximizeButtonWindowNotFocusedState

继承结构不良

com.sun.corba.se.internal.Interceptors.PIORB extends
com.sun.corba.se.internal.POA.POAORB extends
com.sun.corba.se.internal.iiop.ORB extends
com.sun.corba.se.impl.orb.ORBImpl extends
com.sun.corba.se.spi.orb.ORB extends
com.sun.corba.se.org.omg.CORBA.ORB extends 
org.omg.CORBA_2_3.ORB extends
org.omg.CORBA.ORB

不是的异常。

public interface FlavorException { }

无意义和神秘的错误处理

if (properties.size() > 10000)
   System.exit(0);

不必要的对象创建

Class clazz = new Integer(0).getClass();
int num = new Integer(text).intValue();

为其他目的引发异常

try {
    Integer i = null;
    Integer j = i.intValue();
} catch (NullPointerException e) {
    System.out.println("Entering "+e.getStackTrace()[0]);
}

将实例对象用于静态方法。

Thread.currentThread().sleep(100);

在非最终字段上同步

synchronized(list) {
   list = new ArrayList();
}

常量字符串的无意义副本

String s = new String("Hello world");

对String.toString()的无意义调用

String s = "Hello";
String t = s.toString() + " World";

调用System.gc()释放一些内存

将局部变量设置为null以释放一些内存

    // list is out of scope anyway.
    list = null;
}

出于性能原因(或任何其他微优化)使用++ i代替i ++


这些不一定是反模式,只是不好的编码。
Gary Willoughby

@Gary,或我见到的糟糕的发展模式。也许不在严格意义上的反模式定义之内。
彼得·劳瑞

1
@Peter:“调用System.gc()以释放一些内存”,我见过一种情况,除非显式调用GC,否则.NET会因OutOfMemory异常而爆炸。当然,这更多的是例外而不是规则。
编码器

3

代码中撒满:

// TODO: 

要么

// TODO: implement feature 

没有其他信息。


2

假人迭代器:

Iterator iCollection = collection.iterator();
for(int i = 0 ; i < collection.size() ; i++ ){
    if(collection.get(i) == something){
       iCollection.remove();
    }
 }

Singletono工厂:

public class SomeObject{
   private SomeObject() {}
   public static SomeObject getInstance(){
       return new SomeObject();
   }
}

if-else驱动的开发(也称为开放式关闭原则-进行修改时开放,以便于理解):

if (sth1){
...  
}else if(sth2){
..
}
...
..
else if(sth1000000000000){
...
}

StringPattern(也称为StringObject):

a) sendercode
if(sth1)
   str+="#a";
if(sth2)
   str+="#b";
...
if(sth1000)
   str+="#n";

b) receiver
   regexp testing if str contains #a, #b, ... #n

不过,那else if通常是完成工作的唯一方法。我正在考虑弦乐;不能使用开关。使它有用的条件应相同。
Michael

恕我直言,每个if / else都可以替换为简单的映射或访问者模式。如果是字符串,即您可能具有以下属性文件:someString1 = some.package.ObjectToHandleSomeString1
Marcin Michalski

2
单身人士一家工厂。该代码没有错。唯一可能是错误的是,外部代码会做出这样的假设:每次调用都getInstance返回相同的实例。这种假设将破坏封装,实际上是最常见的反模式之一。
back2dos

正是这就是为什么如此令人困惑的原因:)如果该方法被称为create(),或者每次一切都很好时它将返回相同的实例
Marcin Michalski

2

它不是一种编码模式,而是一种行为模式,但这很糟糕:修改代码中的某些内容(假设要求已更改),然后调整所有单元测试,直到代码通过。调整或简单地从测试方法中删除所有测试代码,但将方法保留在那里。

不过,这与更通用 的代码(That'll Do模式)相关,这是该代码的代表性代码行:

int num = new Integer( stringParam ).parseInt( stringParam );

毕竟,它有效。


2

我绝对鄙视抽象反转,或者在高级基元之上重塑低级基元。但是,有时这是由语言设计者而不是程序员造成的。例子:

  1. 使用没有成员变量的单个方法类和相应的接口(根据函数指针的表实现),而不是函数指针。请注意,在Java之类的语言中,您别无选择。

  2. 在MATLAB和R中,坚持认为一切都是向量/矩阵,而不是基元。

  3. 当我们抨击MATLAB时,它没有整数这一事实如何解决,因此当您需要整数时,必须使用双精度。

  4. 纯函数式语言,您必须使用函数调用来编写循环。


1

肯定是复制/粘贴,因为它,牛仔编码以及由此产生的一切,我已经看到了很多不好的代码。(神类,超大型方法,思想欠佳的算法等)

如果允许使用设计模式,我会说:将项目作为计划中的一组动作来进行,而无需进行任何设计或领域分析。


1

多用途Java bean-

具有很多变量的Java bean,用于几种不同的操作中。每个操作都使用bean变量的任意子集,而忽略其他子集。gui状态的一些变量,在组件之间传递的一些变量,甚至可能不再使用的一些变量。最佳示例没有文档,这会妨碍对模式的理解。

而且,不能忘记心爱的人

try{ 
   ... //many lines of likely dangerous operations
}
catch(Exception e){}

恕我直言,异常臭。它们太难使它们正确,并且增加了错误的安全感。
编码器

无论您对异常的臭味有何看法,无声地吞下一个臭味都必须使它更臭。
史蒂夫·B。

1

一位前同事习惯于通过覆盖对象的属性来重用对象,而不是简单地创建新对象。我绝对无法想象实现新功能时会引起多少问题。


1

引起我极大痛苦的是“天空中的大地图”模式。扔在地图上而不是使用适当的对象。您不知道未经调试就包含什么“变量”,并且不追溯代码就不知道它可能包含什么。通常,将字符串映射为对象,或将字符串映射为字符串,您可能应该将其解析为基元。


听起来有点像依靠ASP.NET中的Session和ViewState在页面之间传递数据块,这通常是令人遗憾的……
Wayne Molina

1

我最喜欢的开发反模式之一是使用数据库“设计”,该设计需要不断地以编程方式向一个或多个表添加其他列。这是“设计”的表亲,它为实体的每个实例创建一个新表。两者都不可避免地会受到数据库服务器的限制,但通常要等到系统投入生产一段时间后才能实现。


0

我认为我见过的最糟糕的反模式之一是使用数据库表作为临时存储,而不是使用计算机内存。

问题域是专有的,不允许我对其进行解释,但是不必了解基本问题。这是一个用Java编写的带有后端数据库的GUI应用程序。它是要获取某些输入数据,对其进行处理,然后将处理后的数据提交给数据库。

我们的项目有一个相当复杂的算法,该算法会保存中间值以供以后处理。没有将临时对象封装在...对象中,而是创建了一个数据库表,例如“ t_object”。每次计算值时,都会将其添加到此表中。算法完成工作后,它将选择所有中间值并将它们全部处理在一个大型Map对象中。在完成所有处理之后,标记为要保存的其余值将添加到实际数据库模式中,并且“ t_object”表中的临时条目将被丢弃。

该表也像唯一列表一样使用,数据只能存在一次。如果我们在表上实现了约束,这可能是设计的一个不错的功能,但是最终我们遍历整个表以查看数据是否存在。(不,我们甚至没有使用在CONTAINS中使用where子句的查询)

由于这种设计,我们遇到的一些问题是专门调试的。该应用程序的构建是为了对数据进行管道传输,因此将有多个GUI在数据到达此算法之前对其进行预处理。调试过程是处理一个测试用例,然后在完成上述部分后立即暂停。然后,我们将查询数据库以查看该表中包含哪些数据。

我们发现的另一个问题是,没有从该临时表中正确删除数据,这将在将来干扰运行。我们发现这是由于未正确处理异常,因此应用程序无法正确退出并且没有删除其控制的表中的数据。

如果我们使用了面向对象的基本设计并将所有内容都保留在内存中,那么上述这些问题将永远不会发生。首先,调试会很简单,因为我们可以轻松地在应用程序中设置断点,然后检查堆栈和堆中的内存。其次,从应用程序异常退出后,java内存将自然被清除,而不必担心从数据库中删除它。

注意:我并不是说这种模式本质上是不好的,但是在本示例中,我发现当基本的OO原理就足够时,它是不必要的。

我不确定这个反模式的名字,因为这是我第一次看到这样的东西。你们能想到这种模式的好名字吗?


我不会称其为总括性问题。这取决于要存储多少临时数据以及如何处理它。
GrandmasterB

我应该在帖子中添加更多内容,但是主要的问题是,不是使用修改对象和从内存访问数据,而是使用hack sql代码对数据进行了处理。这导致增加的复杂性和严重的性能问题。
jluzwick 2011年

2
您没有提到问题域,但总不是一件坏事,将这样的状态保持在其他位置可以使进程在较长的运行时间内进行扩展和自省,也有助于调试。数据库访问是否引起性能问题或担忧?
JE队列

我已经对文章进行了编辑,以更好地反映在项目中如何使用它,但是在调试时我们遇到了许多问题,特别是因为我们必须查询数据库以查看临时变量。另外,由于不断查询数据库中的数据并检查是否已存在新数据,我们还遇到了与性能相关的大问题。
jluzwick 2011年

0

马车前-又名YouMightNeedIt

例如:

  • 创建具有抽象概念,交叉引用的RDMBs架构-本质上是过度概括的数据立方体... 然后围绕“一切皆有” 模型编写功能。

那将是YAGNI邪恶的孪生兄弟,对吗?
韦恩·莫利纳

0

IMO,我所见过的最糟糕的反模式是“我们不需要任何臭皮的模式”。反模式:设计模式的想法是浪费时间,您可以通过将其倾斜并复制/来更快地编写代码。根据需要粘贴。

值得一提的是,有使用旧的VB6样式从数据库中加载对象的代码:

Foobar oFoo = new Foobar();
oFoo.FooID = 42;
if (oFoo.Load()) { 
    // do something with oFoo
}

本身并不是真正的反模式,但显示出缺乏利用适当架构和关注点分离的优势。

另外,这样的事情:

// this name is misleading, we may not always want to stand in fire,
// we may want to stand in slime or voidzones or ice patches...
public Foobar StandInFire() { }

// why is this here???
public string BeatWithNerfBat(string whom) { }

// ????
public int GivePony(string to) { }
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.