这并不意味着我会这样做。如果我觉得这段代码在不久的将来可能会变得更大,那么我会考虑添加抽象,将此功能移到其他地方等等。我追求的目标是保持平均复杂度与更改前相同。
从代码的角度来看,我认为这是一个好主意-我的代码永远不够长,很容易理解不同实体的含义,例如类,方法以及类与对象之间的关系。
问题是,这花费了太多时间,而且我经常觉得这样会更好如果我只是按原样实现该功能。这只是“三行代码”与“新接口+两个用于实现该接口的类”。
从产品的角度(当我们谈论结果时),我所做的事情毫无意义我知道,如果我们要开发下一个版本,那么拥有良好的代码真的很棒。但另一方面,您可能花费了使代码“良好”的时间来实现一些有用的功能。只能执行A的代码要比可以执行A,B,C和D的错误代码更糟糕。浪费时间吗?
#1 楼
只能执行A的好代码要比可以执行A,B,C和D的不好的代码差。在不知道(或至少合理确定)您的客户将需要功能B,C和D的情况下,您不必要地使您的设计过于复杂。从长远来看,更复杂的代码很难理解和维护。仅通过有用的额外功能才能证明额外的复杂性。但是我们通常很难预测未来。我们认为将来可能需要的大多数功能在现实生活中永远都不会被要求。简单,干净地执行一件事)比可以执行A,B,C,D的错误代码更好(将来可能会需要其中一些)。
评论
+1表示“我们通常很难预测未来”用户很可能会要求E :)
–MichałPiaskowski
2011年9月6日上午11:57
+1无法同意。我最近在一家公司完成了一个工作学期,我认为这是我学到的最重要的一课。
– Wipqozn
2011年9月6日下午13:22
@Michał,这是一个预测吗? ;-)
–彼得·托克(PéterTörök)
2011年9月6日下午13:27
即使他们要求您提供D,他们的思维模式可能也会有所不同,并且会要求您的抽象不支持的D类型稍有不同。
–克里斯·伯特·布朗
2011年9月6日下午16:40
“如果一个问题没有被完全理解,那么最好根本不提供任何解决方案”或“除非实现者没有它不能完成一个真实的应用程序,否则不要添加新功能。”在这里适用。通过en.wikipedia.org/wiki/X_Window_System#Principles
– nwahmaet
2012年4月23日在20:20
#2 楼
轶事时间:我有两个开发人员为我工作,他们倾向于以这种方式进行过度工程。停止,尤其是在启动新项目时。最特别的是,如果该项目从本质上来说是相当简单的。最终,我们需要的是一个现在可以运行的软件。这太糟糕了,以至于我不得不放他走。这样,尽管有所有多余的代码,他的交付速度仍然比大多数人快。但是,既然他继续前进,我经常会因为需要修改(完全不必要的)抽象层等来添加功能而需要进行大量额外工作而感到烦恼。道德是,过度设计会消耗掉更多的时间,而这些时间本可以花在做有用的事情上。不仅是您自己的时间,而且还包括那些必须使用您的代码的人的时间。
所以不要。
您希望您的代码尽可能简单(而不是简单)。处理“ maybes”并没有使事情变得更简单,如果您猜错了,您将使代码更加复杂,而没有任何实际收获。
评论
+1越简单越好,但再简单不过了。
–朱利安
2011年9月6日上午11:17
“设计师知道他已经实现了完美,不是没有什么可添加的,而是什么都没有带走的。”安托万·德·圣艾修伯里
– Arkh
2011年9月6日下午12:16
避免像瘟疫一样重复,您不会出错。
–凯文
2011年9月6日15:33
@arkh ...我要使用相同的引用:P
–迈克尔·布朗(Michael Brown)
2011年9月6日于20:44
我这样说:每一行代码都有很高的成本。因此,通过最小化代码来最小化成本。删除不必要的代码比编写新代码具有更高的生产率(甚至可能更高)。
–克里斯托弗·约翰逊(Kristopher Johnson)
2011年9月7日于1:10
#3 楼
SOLID和KISS / YAGNI原理是相反的。有人会告诉您,如果不能将doSomething()证明是调用它正在执行的类的工作的组成部分,则它应该属于松散耦合和注入的另一类。另一个人会告诉您,如果这是一个使用doSomething的地方,那么即使将其提取到一个方法中也可能是过大的杀伤力。 “适当的”结构是逐案的基础,需要了解当前的代码库,程序的未来路径以及程序背后的业务需求。我喜欢遵循此结构简单的三步法。
在第一遍,使其正常工作。
在第二遍,使其清洁。
在第三遍,使其稳定。
基本上,这就是将KISS与SOLID融合在一起的方式。当您第一次编写一行代码时,就您所知,这将是一次性的。它只是必须工作,并且没有人关心它,所以不要花哨。第二次将光标移到该行代码中时,您已经证明了最初的假设。在重新访问此代码时,您可能会对其进行扩展或从其他地方插入。现在,您应该清理一下;提取常用花絮的方法,减少或消除复制/粘贴编码,添加一些注释等,等等。第三次回到该代码时,它现在是程序执行路径的主要交叉点。现在,您应该将其视为程序的核心部分,并在可行的情况下应用SOLID规则。
示例:您需要在控制台中编写一行简单的文本。第一次发生这种情况时,Console.WriteLine()很好。在新的要求还要求将同一行写入输出日志之后,您可以返回此代码。在这个简单的示例中,可能没有很多重复的“复制/粘贴”代码(或者也许有),但是您仍然可以进行一些基本清理,也许提取一两个方法以避免将IO操作内联到更深的业务逻辑中。然后,当客户端希望将相同的文本输出到监视服务器的命名管道时,您将返回。现在,这个输出例程很重要。您正在以三种方式广播相同的文本。这是可以从复合模式中受益的算法的教科书示例;使用Write()方法开发一个简单的IWriter接口,实现该接口以创建ConsoleWriter,FileWriter和NamedPipeWriter类,并再创建一个“ MultiWriter”复合类,然后将IWriter依赖项公开给您的类,设置MultiWriter结合了三个实际的编写器,然后将其插入。现在,它非常坚固。从这一点出发,只要需求要求输出应该在任何地方都可以,您只要创建一个新的IWriter并将其插入MultiWriter,就无需触摸任何现有的工作代码。
评论
同意,但是通常一旦您走过第一步,您就再也不会回到第二或第三步了,因为现在该功能是“实时”的,并且还有更多功能即将发布,因此您无法返回并修复第一个功能。您会发现,您可以做的只是所有功能的第一步,然后是沼泽。
–韦恩·莫利纳(Wayne Molina)
2011年9月6日在17:25
@Wayne M-在瀑布式SDLC或短时间场景中肯定会发生这种情况。在这种情况下,最重要的是在截止日期前按照原始规范完成工作,而不是代码的可维护性。如果您想评估源代码的质量,则只需在计划中花费时间进行重构。就像您在任何涉及书面信息生产的工作中重视内容的质量一样,您会花时间进行校对和编辑。
– KeithS
2011年9月6日19:19
您的开头语具有误导性-您说他们反对,然后描述将它们一起使用的最佳方法。我不知道该否赞成糟糕的行话还是赞成好的建议。
– Sean McMillan
2011年9月13日12:30在
我认为我并不矛盾。它们是近极相反。宗教对一个或另一个的坚持必然意味着对另一个的拒绝。但是,这并不意味着它们是互斥的。您不必选择两种方法都100%遵循。这就是其余答案的重点。当涉及到每个原则的结论的取舍时,您如何平衡?
– KeithS
2011-09-14 19:46
真是个不错的过程:可行,干净,可靠。我想知道这是否必然是“在没有首先破解原型的情况下不要尝试和设计任何东西”。
–史蒂夫·贝内特(Steve Bennett)
2012年5月2日4:30
#4 楼
能做A的好代码比能做A,B,C,D的坏代码差。
如果我觉得这段代码可能在最近的将来变得更大,我会考虑添加抽象,移动2)如果您计划将代码用于A,B,C和D,则客户最终会要求您提供E。
您的代码应该执行应做的事情,您现在不应该考虑将来的实现,因为您将永远不会持续更改代码,更重要的是,您将过度设计代码。由于当前的功能,您应该在需要时立即重构代码,而不要为尚未完成的工作而苦苦准备,除非您当然将其计划为项目体系结构的一部分。
我建议您读一本好书:《实用程序员》。它将打开您的视野,并教您应该做什么和不应该做什么。
#5 楼
很时候我需要编写一段简单的代码,我思考未来
也许是问题所在。
在早期,您不知道最终产品是什么。或者,如果您拥有它,那就错了。当然。就像一个14岁的男孩,几天前在Programmers.SE上问他是否必须为自己的未来职业在Web应用程序之间进行选择,而我不记得了:在几年中,很明显他喜欢会改变,他会对其他领域感兴趣,等等。您将获得一个难以维护且难以阅读的代码,因为对于每行有用的代码,您都有两行不需要的代码。不算XML文档,单元测试等。
考虑一下:如果我想知道代码中如何实现某个功能,那么对我来说,阅读二十行代码会更容易,或者必须打开数十个半空类和接口来找出哪个使用哪个以及它们之间的关系等等,这样会更快更容易。
请记住:更大的代码库意味着更多的维护。如果可以避免,请不要编写更多代码。
您的方法也对其他方面有害:比浪费时间去理解几十个类之间的依赖关系更容易弄清楚使用特定的20行方法的地方吗?您更喜欢阅读数十行内容,以弄清什么叫什么?
总而言之,这似乎类似于过早的优化。您正在尝试解决问题,甚至不确定一开始是否存在问题以及它在哪里。当使用产品的版本1时,实现您现在需要实现的功能;不要考虑您希望在两年内在版本14中实现的东西。
评论
“数十个半空类”还具有另一个不足,即没有足够的材料来理解这些类的优点。
– gnasher729
17年1月15日在19:40
#6 楼
编写很多(可能)永远不会使用的代码是发布P45的一种很好的方法。您没有水晶球,也不知道开发的最终方向,因此花时间在这些功能上只会花钱而无法回报。#7 楼
试图预测未来代码的需求通常会导致不必要的过度设计(我目前正试图摆脱的习惯)。我要说的就是三行。当需要出现时(而不是之前),进行重构。这样一来,您的代码始终可以完成所需的工作,而不会过于复杂,并且可以通过重构自然地形成良好的结构。#8 楼
我经常说编码就像部队的明暗侧一样-“明暗”侧需要更多的努力,但会产生更大的结果。 “阴暗”的一面是快速而容易的,可立即带来更多好处,但会破坏您的发展。一旦您走上了黑暗的道路,它将永远统治您的命运。我一直都遇到这个问题,在我曾经做过的每一项工作中,这就像是我无法逃脱的诅咒。公司文化始终是阴暗的一面,快速的破解/修复以推出新功能,而我对重构和编写代码的要求和呼声充耳不闻,如果这不会导致我终止“试图改变事物”(我曾经开过几次笑话,因为我想介绍设计模式并摆脱快速的黑客攻击)。暗面方式是您必须踩的方式,您只需要确保轻踩即可。我已经慢慢地和悲哀地意识到,那些了解正确编写软件方式(即遵循SOLID,使用设计模式,遵循SoC等)的程序员比笨拙的会写
if
语句来修复错误的笨拙的程序员要少得多,当出现更多错误时,只需在该语句上添加内容,而不用考虑“也许有更好的方法可以这样做”,并将代码重构为可适当扩展。评论
if比IAbstractBugFixerFactory中的IAbstractBugFixer维护容易得多。如果和,如果要加上第二个if,那么该重构了。设计模式和SOLID在体系结构阶段非常重要,但在产品已经运行并且以所有团队成员都同意的一种样式编写时,则不是。
–编码器
2011年9月6日下午13:24
@Coder尽量不要假设体系结构随时都不能更改。它可以并且可以。
–瑞奇·克拉克森(Ricky Clarkson)
2012年3月26日0:31
韦恩·M,我很同情您的工作情况。留在原力。 :)
–珍妮佛S
2012年6月20日18:08
#9 楼
意识到可能发生的事情(未来)永远不会坏。思考什么是做某事的更好方法是使您擅长工作的一部分。最困难的部分是确定花费的时间:还款比率是否合理。我们都看到过这样的情况,人们会“轻松地”停止立即流血(和/或大吼大叫),并且随着这些加总,您将获得令人困惑的代码。我们中的许多人还经历了过高的抽象,一旦原始编码器继续前进,这就是一个谜,这也会产生混乱的代码。我会看一下您的情况并提出以下问题:
这些代码对任务至关重要吗,并且会更重要吗?重新编码是否稳定?用外科手术的话来说,这是重构生命吗,还是仅仅是美容和美容?
我是否正在考虑将在6个月内替换的重构代码?
我愿意花尽可能多的时间来记录设计和我对将来的开发人员的推理,就像我要花大量的时间进行
重构吗?
用户要求更改的代码是每周一次,还是这是我今年第一次触摸?
有时候YAGNI和KISS赢得比赛但在有些日子里,根本的改变会使您摆脱疯狂的下降螺旋式上升。只要您不仅对自己想要的东西,而且对他人必须做的工作进行评估,以保持自己的工作,那么您将能够更好地确定哪种情况。哦,别忘了写下您所做的事情以及原因。这样不仅可以保存跟随您的人,还可以保存您以后需要回来的人。
#10 楼
在第二版的Stroustrups“ C ++编程语言”中(我没有可用的页面),我读了不要自发地添加代码。
,我遵循了建议。当然,需要权衡取舍,但您必须找到一个平衡点,但是比起大的意大利面烂摊子,较短的碎片更易于测试。
我经常体验到,在将一个案例区分为两个案例的同时,如果您将2个案例视为n个案例,则会为许多新的可能性打开一扇大门,而这是您可能没有想到的。
但是您必须问YAGNI问题:值得吗?真的有用吗?有经验的人意味着您很少会犯错,而作为初学者,您常常会犯错。
您应该足够关键,以识别模式,并检测是否由于过多的抽象而难以维护代码,还是因为一切都已解决就难以维护。
解决方案不是这个或那个,而是要考虑一下。 :)
#11 楼
“仅能执行A的好代码要比能执行A,B,C和D的不好的代码差。”这在产品开发中可能有意义;但是,大多数IT专业人员都是在“项目”而不是产品开发中工作。到那时可能不会运行5年或10年,而业务场景可能已经过时,并且新的项目/ ERP产品可能已经取代了它。在这5/10年的寿命中,除非麻烦是代码中的缺陷,否则没人会注意到它的存在,而您最好的想法的优点也不会被忽略! (除非您擅长大声吹打自己的小号!)他们的代码!
#12 楼
许多关于精益和/或敏捷开发的书籍将有助于加强这种方法:立即做必要的事情。如果您知道要构建框架,请添加抽象。否则,在您需要之前不要添加复杂性。我建议精益软件开发,它将介绍许多其他可以大大提高生产力的概念。#13 楼
人们如何谈论正确的做事方式是很有趣的。同时,编程任务仍然非常艰巨,并且无法为编写大型复杂系统提供良好的解决方案。编写复杂的软件。在此之前,我建议您始终先从“愚蠢”的原型实现开始,然后再花足够的时间进行重构,以便您的大学可以遵循您的代码。
评论
在大多数情况下,您永远不会有一些特殊的时间进行重构-这可能是所有这些的主要原因“我们必须重新实现才能实现它;-)您要么从一开始就以正确的方式进行操作,否则您始终会以错误的方式进行操作。
–安德烈·阿吉巴洛夫(Andrey Agibalov)
2011年9月6日14:07在
@ loki2302:请记住,编写新代码总是更容易。对愚蠢的代码进行原型制作时,您的速度将是原来的两倍,在此之后,您的生产率将下降一半左右,降至零。因此,最后,您仍然可以和程序员尝试正确设计方式一样快。
–AareP
2011年9月6日14:25
#14 楼
看到过早的泛化设计根本不符合后来出现的实际需求,我为我设计了一条规则:对于假设性要求,只写假设性代码。
那是:建议您考虑以后可能发生的更改。但是,仅使用这些见识为代码选择设计,如果确实提出了这些要求,就可以轻松地对其进行更改和重构。在这种情况下,您甚至可能会想写一些代码(假设代码),但不要编写任何实际代码!
#15 楼
我认为将对您有所帮助的思路是,始终为编码问题而不是抽象的解决方案而努力。仅当抽象实际上有助于简化代码库时(例如,允许您使代码库更小),才应添加抽象。烘干他们的代码。设计模式和最佳实践可帮助您找到这样做的机会,但本身并不是值得追求的目标。#16 楼
我认为过度设计通常是由于编写代码的不安全感所致。所有抽象的原则和模式都应视为可为您提供帮助的工具。
经常发生的情况是,它们被视为标准,必须遵守。
我相信与公理相比,程序员总是处在决定如何抽象的位置上的更好位置。
评论
我认为获取代码编写自我安全性的一种方法是在Linux系统上以root身份进行编码。如果键入willy-nilly,boom,则只需重新映像VM。真正快速地传授各种良好习惯。确保您的盒子位于真实的互联网中,并确保您查看日志。 (ssh,http)那些也很有趣!
–克里斯托弗·马汉(Christopher Mahan)
2011年9月7日在22:52
我的版本是:忽略您不了解的原理。如果您决定使用它们,请不要比锻炼更认真地对待它们。
–sabof
2011年9月7日23:16
#17 楼
问问自己,好的设计的优点是什么:易于理解
易于维护
长期保持有用
轻松添加新功能
现在,问问自己,添加所有这些抽象层是否真的增加了上述任何要点。如果没有,则说明您执行了WRONG错误。 lang-c prettyprint-override“>
if (condition()) {
doSomething()
}
然后请这样做。这表明您以前的设计很好并且很容易适应。只有在您的类开始增长到一定程度之后,才可以使用重构来拆分函数并可能提取新的类。您可以事先大致掌握(例如,实施需要花费超过1天或半天的时间)。
从那里开始,仅在代码大小增加时添加抽象层。然后,您就可以重构!过了一会儿,当您需要再设计一点,或者走一条捷径时,对您来说甚至很自然。每次添加新功能或在新位置调用旧代码时,都是时候查看旧代码并查看是否可以对其进行一些改进,然后再重新添加。这样,热代码将逐渐变得更加清晰,而无关紧要的部分将慢慢腐烂,从而不会占用您的宝贵时间。 (如果您使用一些旧代码,但又无法快速理解它,这可能是对其进行改进的动力,但请记住,这也是一个警告,建议您进行一些清理/重新设计)。如果您像这样工作,您将永远不会过度设计任何东西。当您想添加一些新内容时,可能需要一些纪律才能回到旧代码,或者比您喜欢的还要留下一些丑陋的新代码,但是您将朝着富有成效的方向努力,而不是过度设计。
评论
我会看到您的SOLID,然后抚养您YAGNIK和KISS爆头,爆头。
“过度设计”的问题有时是需求捕获过程无法正常运行的体现。如果您在“假设”的情况下苦苦挣扎,然后在没有利益相关者/客户互动的情况下自己回答问题,那么您将可以预测未来。也许要付出一些努力,然后在将更多复杂性引入代码之前,重新投入到对需求的理解中?
也许只有我一个人,但我会认为“愚蠢”使我的客户/雇主花钱购买他们不想要/想要的东西:S
将来在实际需要时添加功能几乎从来不会变得如此困难。您现在做不到任何事情。