过度使用或滥用的编程技术


38

您是否发现编程中有任何过度使用(IE过度使用的技术)或滥用技术,或者对所有事物都使用了一点技术,而又不能很好地解决人们试图解决的许多问题解决它。

它可以是正则表达式,某种设计模式或算法,也可以是完全不同的东西。也许您认为人们滥用多重继承等。


33
我同意IE的使用方式比应有的方式过多,但这主要是由非程序员使用的。。。
埃里克·威尔逊

7
@FarmBoy-我认为他的意思是IE:“ Ie”仅表示“即”,即用拉丁文完整写出的是“ id est”。
拉斐尔

立即修复:)
Anto

4
任何技术都可以(并且经常被滥用),它只能作为下一个灵丹妙药来呈现,您会发现有人将每个问题都视为钉子(混合隐喻)。
克里斯·

8
@Rapheal-我当然在开玩笑。感谢您的教育。
埃里克·威尔逊

Answers:


73

代码注释

我只是希望大学教授能够理解,他们不需要教学生写10行注释,说下面的代码是一个for循环,从1到x的数量进行迭代。我可以在代码中看到!

先教他们编写自我记录代码,然后再对不能充分自我记录的内容进行适当的注释。软件世界将是一个更好的地方。


26
自我记录代码不是。教授们也这样做是为了教学生如何概念化问题以及如何解决它。另外,我从未见过有人抱怨太多文档。请注意,该文档是正确的。
Woot4Moo 2011年

24
@ Woot4Moo:如果您阅读Clean Code,Fowler清楚地指出,过时的文档比没有文档更糟糕,并且所有注释都趋向于过时并从其文档中迁移出来。整本书都是关于如何编写自我记录代码的。
Scott Whitlock,

12
教他们用代码替换注释将是一个不错的开始……
Shog9

15
+1。我实际上已经在现实世界的代码中看到以下内容:“ loopVar ++; //递增loopVar”。AAAAAAAAAAAAARRRRRGH!
Bobby

7
@Bill:我的观点是,即使通用逻辑和控制结构相对复杂,您也无需记录它们。基本上,任何了解该语言的优秀开发人员都应该能够通过分析代码来解决任何问题,而无需评论。例如。一组嵌套的for循环,其中包含一些中断,等等。但是应该注释的是代码为什么做出这些决定的特定于应用程序的推理:“如果明天有新的程序员开始在公司工作并查看代码,那么在没有注释提示的情况下,它有意义吗?”
鲍比表

57

单例设计模式。

当然,这是一个有用的模式,但是每当新程序员了解此模式时,他便开始尝试将其应用于所创建的每个类。


我每天都使用这个设计不当的借口作为框架。codeigniter.com/user_guide/libraries/email.html他们认为OOP使用前缀函数$this->。PHP是一种烂语言,所以我不能指望这些框架是优秀的。
Keyo

7
我认为任何实施单例并且不悔改的程序员都会为自己(或她)所犯的罪恶而火上浇油。

2
我在职业生涯中(在多任务环境中)曾经实施过一次单身,并且我不得不通过重写来为自己的罪恶悔改。
Spoike 2011年

3
单例在某些方面很有用,但几乎总是在不该使用的情况下使用。这并不意味着永远不应该使用单例-仅因为滥用了某些内容并不意味着它不能正确使用。
配置器

2
@Rapahel:这就是为什么我不认为学习设计模式是一件好事的原因。
配置器

53

保护愚蠢的编程是最邪恶的事情。将所有内容放入尝试捕获区,不进行任何处理

        try
        {
            int total = 1;
            int avg = 0;
            var answer = total/avg;
        }
        catch (Exception)
        {

        }

当我看到一个不执行任何操作且旨在处理愚蠢逻辑的try catch时,我会发抖。通常,尝试捕获已被过度使用,但是在某些情况下非常有用。


4
有趣的是-我刚刚编辑了一个文件,该文件执行了数百次,并且有充分的理由。这是一个单元测试文件,用于测试各种方法在预期时会引发异常。try块包含一个调用和“未发生预期的抛出”报告。该异常是预期的且正确的,因此没有纠正措施。显然,单元测试是一个特例,但是我在真实代码中也遇到过类似的情况。危险信号,是的,但不是绝对规则。
Steve314 2011年

5
@Steve您描述的情况是一种罕见的边缘情况。我能想到的另一个是日志记录代码-如果日志记录本身失败,则尝试记录异常或中断应用程序是没有意义的。
dbkk 2011年

1
@dbkk-在边缘条件上达成了一致,我要补充一点,当您确实需要捕获异常但不需要采取纠正措施时,请在catch块中添加注释以说明这仅是为了平息该异常。维护者。在单元测试中并不是很罕见,因此在这种情况下,我会放弃评论。
Steve314,2011年

5
在错误恢复下一个
JK。

2
@ jk01-异常不是(不一定)错误。任何条件是否为错误都取决于上下文。例如,如果不需要文件,则“未找到文件”不是错误。也许这只是一个可能存在或可能不存在的可选设置文件,如果文件不存在,则可以保留默认值(因此,在捕获后无需采取纠正措施)。
Steve314 2011年

47

依靠StackOverflow解决您的问题,而不是想办法。

新的程序员(经常)发现关于SO的丰富知识,然后再进行思考和尝试。

在我这一天,我们不得不从书籍,没有互联网,没有SO进行编码。现在下车我的草坪。


28
我必须承认,我确实对SO的某些问题感到胆怯。要么是因为每天都要问他们很多次,但显然根本没有搜索过;更糟糕的是,他们将社区视为免费的外包服务。
2011年

2
@dbkk:stackoverflow.com/questions/4665778/…- >第一条评论“您尝试过吗?”
Matthieu M.

5
我从书本上开始学习,但是一旦有了基础,我就可以从在线论坛讨论中学到几乎所有其他内容。不是通过提问,而是主要(首先是专门)阅读。这种方法教会了我许多编程语言,使我深深着迷(有很多弯角和缝隙),我认为这根本不是一个坏方法。
康拉德·鲁道夫

1
@Konrad Rudolph:这种方法既明智又明智,可以阅读问题并进行搜索。当询问者具有理解答案的框架时,最好进行询问。人们常常问一些他们尚未理解的问题。
2011年

3
问题是,通过鼓励/回答非常简单的问题(可以通过简单的Google搜索回答)来提供很多代表,从而鼓励这样做。人们之所以回答非常棘手的问题,仅仅是因为他们真的想要。
IAdapter 2011年

36

面向对象 它非常有价值,但是如果使用不当,它很容易掩盖否则会清除清晰的代码(让我想到R中的S4编程的一些示例),并且会产生不必要的巨大开销,并且会花费大量时间在性能上。

另一件事是,(例如,在Perl中)OOP通常归结为用另一种方式编写相同的东西:有时这result = $object ->function(pars)不是result = function(object, pars)很有意义,但是当与过程编程混合使用时,它会使阅读代码变得非常混乱。除此之外,您只会引入更多无法改善设计的开销。这也取决于关于单例模式的说法:应该使用,但不能在所有内容上使用。

我本人确实使用OOP,但是在我的领域(科学计算)中,性能非常重要,并且由于当今所有学生都在使用Java,因此OOP经常被滥用。这对于处理更大的问题,概念化等等非常有用。但是,如果您在BioPerl中使用DNA序列,那么每次使用对象并与吸气剂和吸气剂保持一致可以使计算时间增加十倍。当您的代码运行几天而不是几个小时时,这种区别确实很重要。


6
@Craige:我自己也使用OOP,但是在我的领域(科学计算)的性能非常重要,并且OOP经常被滥用,因为现在所有的学生都使用Java。这对于处理更大的问题,概念化等等非常有用。但是,如果您在BioPerl中使用DNA序列,那么每次使用对象并与吸气剂和吸气剂保持一致可以使计算时间增加十倍。当您的代码运行几天而不是几个小时时,这种区别确实很重要。
Joris Meys 2011年

1
@Craige:方法链接(如Foo.DoX().DoY().DoZ())很好,但是,它绝对不是解决问题的唯一方法。功能组合物(像doZ . doY . doX $ foo是完全有效的替代方案。
。匿名

1
@Joris:我一直想知道的事情(我自己是一名生物信息学家):如果性能如此重要,为什么要首先使用Perl而不是C ++?如果您担心要从运行时中减少10分(正当的担心!),那么切换到C ++可以使您立即满意。当然,这种语言很烂……但是Perl也是如此……并且有很好的C ++生物信息学库。
康拉德·鲁道夫

1
@Konrad:这取决于。BioPerl可以说是为所有语言的生物信息学家提供的软件包最多(我认为Python正在赶上来)。另外,在Perl中已经完成了许多工作,并且改编脚本比用C ++重写所有内容更容易。最后,当脚本执行时,它不能用来编写完整的应用程序。猜猜这就是为什么Perl仍然那么多。老实说,我不介意,我喜欢这种语言。对于字符串和文件工作,这仍然是我所了解的最好的。计算显然是用其他语言完成的,并且可以链接回去(这很容易)。
Joris Meys 2011年

1
@Konrad:这取决于如何做到这一点。可以在Perl中完成所有操作,但是可以将Perl轻松链接到其他工具,尤其是在Linux上。我经常将Perl用作高级bash脚本,具有强大的字符串处理能力,并且易于构建要放入其他应用程序的数据集。
Joris Meys 2011年

27

在ASP.NET Webforms中使用了数年之后,我必须明确地说:

智能用户界面

尽管完全有可能在Web窗体中创建可测试的分层应用程序,但Visual Studio使开发人员仅需单击即可轻松地形成与数据源紧密绑定的窗体,而应用程序逻辑则遍布所有UI代码。

史蒂夫·桑德森(Steve Sanderson)的反模式比我在其Pro ASP.NET MVC书中所能解释的要好得多:

要构建智能UI应用程序,开发人员首先通常通过将一系列UI小部件拖到画布上来构建UI,然后为每个可能的按钮单击或其他UI事件填写事件处理程序代码。所有应用程序逻辑都位于以下事件处理程序中:用于接受和验证用户输入,执行数据访问和存储以及通过更新UI提供反馈的逻辑。整个应用程序由这些事件处理程序组成。本质上,当您将新手放在Visual Studio的前面时,默认情况下倾向于出现这种情况。在此设计中,没有任何关注点分离。一切都融合在一起,仅根据可能发生的不同UI事件进行排列。当逻辑或业务规则需要在多个处理程序中应用时,通常会复制并粘贴代码,或某些随机选择的段被分解为静态实用程序类。由于许多明显的原因,这种设计模式通常称为反模式。


1
GUI是至少必须遵循的某种规范代码。我认为他介绍的程序员如果显示空白文本文件不会更好。

4
@qpr,我对此一无所知。如果他们有一个空白的文本文件,他们可能将无能为力(这可能只是一个奖励)。
肯·亨德森

使用WPF的众多好处之一是,实施MVVM可以将这种反模式粉碎为铁匠铺。
罗伯特·罗斯尼

2
这个的一千倍。我什至看到有经验的.NET开发人员都依赖于这种“模式”,因为它比理解或关心问题分离要容易得多。即使他们不使用拖放向导,.NET WebForms开发中最常见的“模式”也是在代码隐藏事件中执行所有操作,并根据需要复制/粘贴代码。
韦恩·莫利纳

23

我偶尔会看到这种情况,这通常是由于不完全了解布尔表达式引起的。

if (someCondition == true)

16
它的确具有使代码更明确的优势
Pemdas 2011年

7
我想看看这个问题是为了:programmers.stackexchange.com/questions/12807/...
gablin

5
我不这样做,但是,认真地克服它。有一个更糟糕的地狱。:)
MetalMikester 2011年

4
如果您正确命名了变量,例如boolis或开头的has,则无需使用来使代码更明确= true
没人

3
@DisgruntledGoat,因为它根本不应该使用
Corey

19

重新实现核心(或以其他方式提供)的功能,可能是由于对其存在的无知或自定义的需求。


2
学习是一个例外。学习任何东西时,“重新发明轮子”通常是有益的。
2011年

可以肯定-我应该指定它仅在创建生产软件时才有意义。
l0b0 2011年

1
在实施与安全性相关的任何事情时,这尤其糟糕。良好的安全软件可以防止大多数人从未想到的攻击。
David Thornley

我认为这种方式太普遍了。我们需要X,让我们编写我们自己的!但是开源软件包Y呢?不,我们需要与我们行业中其他所有人一样的超级机密商业机密的定制软件,我们不能使用常见的垃圾软件!
韦恩·莫利纳

18

遗产。

不要在没有意义的地方执行is -a。


6
问题在于,直观上“ is-a”似乎很有意义(特别是因为每个实现/合同关系都可以用口头表达为“ is-a”)。它需要对OOP有扎实的了解,才能意识到这实际上是一个错误,并且继承和接口实现是根本不同的。
康拉德·鲁道夫

@Konrad-绝对同意。我试图鼓励人们从构图开始,进入接口并最后考虑继承。(当然,根据经验)。但这也许是我VB背景的话题... ;-)
Mike Woodhouse

1
但是,这在很大程度上是一个语言设计问题。在Java或C#之类的语言中,您应该“首选组合而不是(实现)继承”的原因是因为实现继承很烂。有人批评Bertrand Meyer的面向对象软件构造过度使用了继承,但是当您实际看一下Eiffel(本书中使用的语言)时,您会意识到它是为实现继承设计的,因此实际上可以使用继承组成。在这种情况下,至少。
约尔格W¯¯米塔格

14

定制现成的软件。

虽然我可以承认这样做很有用,但我确实想知道为完成可疑收益而花很少的钱做些小事情。在这里,公司可能会花费数十万美元(如果不是数百万美元)来​​购买某些软件,然后对其进行定制,因为它具有可配置性,可编辑性和灵活性,以至于默认情况下它实际上并不会做很多事情。最后,这是一个自定义应用程序,因为所有新代码都添加了最初购买的内容。


自定义应用程序难道不是这种情况吗?
JeffO 2011年

13

使用编译器作为调试器。

忽略编译器警告或将其完全关闭。

全局变量。


18
“将编译器用作调试器”有什么问题?拥有一个能够在运行时成为问题之前指出代码错误的编译器是一个巨大的优势。
梅森惠勒

3
我依靠编译器来捕获语法错误和不正确的类型匹配错误。对于行为错误,我依靠测试用例。
gablin 2011年

5
问题是当您开始依赖编译器来确保程序正确时。编译了吗?不,让我修改这一小节,看看是否可以解决。它编译了吗?不,让我在这里和那里添加一个*,看看是否可以编译。...等 显然,它对于纠正错别字很有用
Pemdas 2011年

2
您说的好像是编码员在黑暗中盲目地尝试着做某事(任何事情)以某种方式编译代码。这不是IME的工作方式;编译器会为您提供有用的错误消息,并指出错误的位置,通常,如果未进行编译,则是您自己编写的新代码,因此您很清楚错误的地方以及一旦指出错误后如何修复出来。(尽管如果您随机抛出* s,则可能是在C ++中工作,其中已知编译器错误无济于事,所以我所说的内容可能不适用。)
Mason Wheeler

3
给定足够强大的类型系统和良好类型的代码库,依靠编译器可以很好地工作。通常,类型系统可以对重要的约束进行建模,而这些约束通常是您必须为其编写测试的(例如,在弱类型的系统中)。因此,明智地使用编译器(尤其是它的类型检查器)是测试和调试的重要资产,并且依赖它没有错。特别是说编译器只能显示语法错误是错误的。
Konrad Rudolph

11

所有设计模式。

它们就像盐或糖。食物中的一些使食物变得很棒,许多食物使您的食物变得糟透了。

不要误会我的意思。设计模式是很棒的技术。我经常使用它们。但是有时候,我发现程序员(其中有些是我的前任老板)拥有这些“您必须使用设计模式”,即使与问题不符!!!

一个示例是“访客模式”,它更直接地针对“线性/顺序集合”。我不得不一次使用树数据结构。为了“访问”每个项目,我编写了一种非设计模式的方法,并且前任老板坚持使用或改编“访客模式”。然后,我浏览网络,以征询第二意见。好吧,其他程序员也有相同的情况和解决方案。并将其称为“树/分层访客模式”。

我希望在“设计模式”书的新版本中将其作为现有模式的新模式添加。


3
我从未使用过设计模式,尽管有时它们会自然地出现在我的代码中。
配置器

俗话怎么说?您在不知道正在使用它们的情况下开始使用模式,然后了解了它们并在各处使用它们,然后它们成为了第二天性,因此您在不知道正在使用它们的情况下开始使用它们?
韦恩·莫利纳

2
@configurator当我了解设计模式时;我还发现我和其他开发人员已经在使用其中的一些;-)
umlcat 2011年

9

使用异常作为流控制。有点类似于此答案,但有所不同。

吞咽异常是一种不好的形式,因为它引入了代码中难以发现的神秘错误。另一方面,将异常用作流控制是不好的,因为它使代码效率大大降低,并且从概念上讲是很草率的。

异常处理仅应用于处理真正的异常(duh),不可预见的情况,而不是诸如用户在期望数字的字母字符中键入之类的东西。这种异常滥用在Java世界中的不良程序员中非常普遍。


产生低效率代码的异常取决于您的语言-创建堆栈框架等。在Squeak中,堆栈已完全规范化,因此使用异常与不使用异常之间没有区别。(换句话说,异常是库的一部分,而不是语言的一部分。)但是,使用异常控制的控制流会令人困惑lshift.net/blog/2011/01/04/try-again-with-exceptions显示我最近发现的循环使用异常。
Frank Shearar

关于最后一段,同样取决于您的语言。Common Lisp中的异常(“条件”)是大多数语言的异常思想的严格超集。在此处查看示例:gigamonkeys.com/book/…与所有内容一样,您可能走得太远!
Frank Shearar 2011年

可读性和可维护性胜过性能。我会使用异常来取得成功的情况(我假设您指的是“流控制”)很少见,但是它们可以在例如复杂的递归搜索中提供更清晰的代码。大致上,我同意。例如,我有一个带有迭代器方法的容器,这些方法会因超出范围而返回false(在迭代结束时很常见),但是如果迭代器为“ null”(某种内部不一致的可能标志),则会抛出异常
Steve314 2011年

7

我将通过过多的数据隐藏来保持灵活性。

我们都知道抽象和隐藏实现是好的,但是更多并不总是更好。过大了,您可能会得到无法应对需求变化的僵化结果。要处理更改,您不仅必须修改需要处理该更改的类,而且还必须创建一种方法,使尽管隐藏了数据但仍可以访问以前从未需要的信息。

问题是,就像为每个上下文找到正确的平衡问题一样,这需要经验。



6

可变状态和循环。您几乎永远不需要它们,并且没有它们几乎总是可以获得更好的代码。

例如,这是直接从StackOverflow线程获取的:

// ECMAScript
var thing, things_by_type = {};
for (var i = 0; i < things.length; i++) {
    thing = things[i];
    if(things_by_type[thing.type]) {
        things_by_type[thing.type].push(thing);
    } else {
        things_by_type[thing.type] = [thing];
    }
}

# Ruby
things_by_type = {}
things.each do |thing|
  (things_by_type[thing.type] ||= []) << thing
end

他们俩都在做同一件事。但我不知道是什么,他们在做什么。幸运的是,该问题实际上解释了它们在做什么,因此我能够如下重写它们:

// ECMAScript
things.reduce(function (acc, thing) {
    (acc[thing.type] || (acc[thing.type] = [])).push(thing);
    return acc;
}, {});

# Ruby
things.group_by(&:type)

// Scala
things groupBy(_.type)

// C#
from thing in things group thing by thing.Type // or
things.GroupBy(thing => thing.Type);

没有循环,没有可变状态。好吧,好的,没有显式循环,也没有循环计数器。

代码变得更短,更简单,更像是对代码应该执行的操作的描述(特别是在Ruby情况下,它几乎直接说出“按类型对事物进行分组”),并且不易出错。由于没有循环索引和终止条件,因此不会出现循环末尾,栅栏错误或循环索引和终止条件出现一对一错误的危险。


我确实相信您的“固定” ECMAScript第二行应该读入(acc[thing.type] || (acc[thing.type] = [])),而不是, unless you want to add 两次将“ = [thing] thing” 放到列表中……还是我错过了什么?
奥斯汀·海德

@奥斯汀·海德(Austin Hyde):对。
约尔格W¯¯米塔格

1
ecmascript示例对于许多程序员来说是难以理解的。它依赖太多的语法技巧。对于初级开发人员而言,循环具有明显的优势。
Joeri Sebrechts

6

嘲笑。当过多的过程或功能设计可能是您的问题的更简单解决方案时,它为代码中的过多解耦引入了过多的人为要求,导致过度设计的馄饨代码并迫使OO风格的设计陷入困境。


理论上同意;嘲笑是有用的,但你不具备有他们。
韦恩·莫利纳

嘲讽的侵入性取决于您使用的语言。在C#中,它可能具有很高的侵入性,并会添加大量不需要的接口。
Frank Hileman 2014年

3

过去两年来,全世界的Delphi程序员一直在发现由于整个Unicode转换,使用字符数组存储字节有多糟糕。

而且,“很快”,我们将了解到要将整数更改为64位时将整数存储在列表中有多糟糕


2
我认为,如果Borland声明一个与AnsiString基本相同但专门用于blob的DataString类型,然后确保人们对此有所了解,则绝大部分Unicode问题都不会解决。
梅森惠勒

2

属性。我知道这是有争议的,但是我觉得.net中的属性被过度使用了。

这两行有什么区别?

public string Property { get; set; }

public string Field;

对于开发人员而言,区别在于:绝对没有。编译形式是一点点不同,所以如果你正在写一个库,并且该库将在程序进行升级,并且如果你正在写一个接口插件不能重新编译程序本身(例如该应该可以在您的软件的各个版本上使用),那么您不应使用公共字段,因为您不能将它们替换为属性。在任何其他情况下,使用字段或不带修饰符的自动属性之间绝对没有区别


1
同意 如果您知道100%的财产,那么您将不需要做任何事情来获得/设置“财产”,那么就没有理由拥有财产。但是,如果有机会需要做一些事情(例如记录一个值已更改),则应该使用属性。个人而言,我没有看到一个足够大的差异使用属性,以防万一你必要再进行修改(我知道,我知道,YAGNI但我觉得偏离在这种情况下,没有任何费用)
韦恩·莫利纳

2
@Wayne:如何正在改变;{ get { ... } set { ... } }任何不是改变更多的工作{ get; set; }{ get { ... } set { ... } }
配置器

现在我想到了,这是一个公平的观点。
韦恩·莫利纳
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.