哪种设计模式定义最差或定义最窄?[关闭]


28

对于每个编程项目,都有过编程经验的管理人员会在为您的项目推荐一些设计模式时大放异彩。当设计模式有意义或您需要可扩展的解决方案时,我喜欢它们。例如,我以积极的方式使用了代理,观察员和命令模式,并且每天都这样做。但是,如果只有一种创建对象的方法,我真的很犹豫地说出Factory模式,因为工厂将来可能会使一切变得更容易,但是却使代码复杂且纯属开销。

因此,我的问题是关于我的未来职业以及我对经理类型的回答,这些类型周围会出现随机的模式名称:

您使用了哪些设计模式,这使您整体退缩?哪些是最糟糕的设计模式,除了在一种有意义的单一情况下,您应该考虑哪些(请阅读:哪些设计模式定义非常狭窄)?(这就像我在寻找对亚马逊整体产品的负面评价,以了解使用设计模式的人最讨厌的东西。)而且我在这里不是在谈论反模式,而是通常被认为是“好的”模式。

编辑:正如一些回答所言,问题通常是模式不是“不良”而是“使用错误”。如果您知道一些经常被滥用甚至难以使用的模式,那么它们也可以作为答案。


大多数模式使某些更改轴上的未来更改变得容易,但它们却使其他更改轴上的更改变得更加困难。四本书的帮派特别擅长说一种模式何时适用。随后的许多关于模式的书都没有提到这一点。以“访客”模式为例。它使添加新的树遍历算法变得容易,但使向树添加新种类的节点变得更加困难。它还允许遍历算法位于比树更高的层中。您需要考虑哪条变更轴最不稳定,并进行设计以使沿该轴的变更变得容易。
Theodore Norvell

“四人帮”一书中的所有内容。
Miles Rout

Answers:


40

我不相信错误的模式,我确实相信可能会错误地应用模式!

  • 恕我直言,单例是最滥用和最错误应用的模式。人们似乎患有单例疾病,并且开始在任何地方看到单例的可能性,而没有考虑其他选择。
  • 恕我直言,访客模式具有最狭窄的用途,几乎永远不会证明增加的复杂性。可以在这里得到一个不错的pdf文件。真正只有当您知道要在不事先了解所有方法的情况下对数据结构执行不同操作时要遍历的数据结构时,才为访问者模式提供一定的战斗机会。虽然很漂亮:)

对于此答案,我仅考虑了GOF模式。我不十分了解所有可能的模式,也无法将它们考虑在内。


我发现访客很少使用。单身人士...别让我开始。如果情况不适合一个人,请不要使用它。
Michael

1
访问者的用途可能很少,但是一定可以解决问题。我的理解是,Vistor对LINQ是必不可少的。
2011年

12
访客模式对于降低(或消除)软件程序中的圈复杂度至关重要。每当您拥有if / else树或switch语句时,访问者都可以替换它们的可能性很高,从而可以保证执行和编译时检查。访客是我所知道的最强大的模式之一,我们定期使用它会产生很大的效果。它也使测试成为梦想。
Les Hazlewood 2013年

@LesHazlewood难道不是不是访问者只是一种针对编程语言不足的解决方法?类似ML的语言(Haskell,SML,OCaml等)具有代数数据类型(“类固醇上的联合”)和模式匹配(“类固醇上的切换”),它们产生的代码比Visitor更为简单,但已由编译器进行了全面检查。与访问者相比,您没有很多方法需要通过对象属性手动管理其共享状态。在类似ML的语言中,所有大小写的区别必须完整,否则将无法编译。
vog

14

除了其他答复者已经提到的Singleton和Visitor,我也不知道“臭名昭著”的设计模式。恕我直言,最大的问题不是源于特定的设计模式“错误”,而是源于开发人员过于急于应用模式。

熟悉模式时,几乎每个人都会经历“模式发烧”阶段。认为模式是切成薄片的面包以来最好的东西,最初人们尝试在有可能的情况下应用它们。这导致代码被模式所掩盖,而模式本身不再有用,只会使代码更难于理解和长期维护。

最终,我们大多数人克服了这一阶段,开始学习如何使用模式来解决实际问题,而不是为了自己。模式有其代价,这增加了复杂性,只有通过帮助简化系统的其他部分来偿还增加的复杂性,才有理由应用任何特定的模式,这才是合理的,从而使代码/配置整体更易于理解和维护。如果不是这种情况,通常最好远离模式,坚持使用可能可行的最简单解决方案。


绝对。模式既不好也不邪恶,它们被很好地应用或错误地使用。

我宁愿教给别人我所学的方法:首先,面向对象,了解对象之间的关系,然后学习模式的名称。就模式而言,思考比需要做的事情容易。它们是一种通讯工具。
Michael K

模式是一种交流方式,而不是说明性清单。如果您模式上入手,则可能会误用它。如果您在设计中找到一个或一个的提示,那么模式目录可以帮助您了解您可能还需要什么。
罗布·克劳福德

14

我将在这里走出去,建议过度使用继承。绝对最适用于没有扎实的编译时多态性支持的语言,例如Java。运行时继承是一种工具,而不是一种全能的奇迹。

其他人已经表达了与我个人对Singletons的仇恨相似的内容,因此在这里我不再赘述。


10

单身人士。它位于GOF模式中,现在通常称为反模式。原因之一是Singleton使代码更难测试。


4
单例模式中存在许多基本缺陷,Steve Yegge在本文中很好地说明了这一点- 认为单例为愚蠢
ocodo 2011年

Slomojo:不错的文章。我打算把它叫做“傻瓜模式”从现在开始:-)
没有人

2
是的,因为有些傻瓜滥用了模式,完全错过了面向对象的含义(根据他提出的所有Manager类的判断),这当然是模式的错,而不是开发人员。
Dunk

为什么每个人都将Singleton的常见(麻烦的)实现与Singleton模式相关联?一个几乎总是坏的,另一个不是。
2011年

1
@qes> Singleton带来了重要的实现问题(尤其是线程问题)。这已经是一个坏点。但是,您还将发现单例是关于如何起诉该类而不是其如何工作的一种模式。该类的使用方式应由使用该类的代码处理,因此Singleton是关注点分离的中断。
deadalnix12 2012年

7

如果您知道一些经常被滥用甚至难以使用的模式,那么它们也可以作为答案。

过于严格地遵循WPFMVVM模式,例如此问题所示。有些人试图遵循这样的准则,即不应将任何代码过于严格地置于代码的后面,并提出各种外来的骇客。

当然,MVVM像地狱一样困难,对于小型短期项目来说不值得。

WPF博士在他的文章MV-poo中发表了具有讽刺意味的文章。


@Akku:您当然应该知道如何使用它,以便可以确定它是否适合您的项目。因此,是的,这对于WPF开发至关重要,并且非常有用。
Steven Jeuris 2011年

问题之一是人们误解了MVVM模式。这不是“地狱般的艰难”。人们通过认为所有其他模式(例如命令模式和介体模式)都是其中的一部分来做到这一点的。我个人认为,MVVM是最简单的模式之一。我同意,对t采取任何行动都是愚蠢的。
BK 2015年

5

GOF书中的某些模式是C ++特有的,从某种意义上说,它们在具有反射功能的语言中不太适用,例如,原型模式在Java中不太重要。

我认为口译员模式是一种“狭窄”的模式。您必须有一个一般问题,值得开发一个一般问题解决程序。它仅适用于可以发明语言,定义语法和编写口译员的特殊问题。问题实例应可表示为语法中的句子。我不认为您经常遇到这种情况。


1
GOF的大部分仅适用于基于静态类的OOP。这类语言偏离如此之小,这本书只是娱乐性的轶事。
哈维尔

5

我最经常后悔的一个(虽然不是最强烈的):当我本应该创建一个函数但是实现了一个OOP解决方案时。


1
为什么?是什么让您后悔呢?请扩大您的答案并增加您的经验。
Walter

的确,我认为具有局部变量和清晰方法的类比只能通过复制粘贴重新应用的200行函数更容易重用,而后者仅具有一个看起来丑陋且不全面的位置。
Akku的

2
KISS意味着您首先要使其成为一个函数,然后根据需要将其重构为一个类。
伊娃(Eva)2013年

5

厂。我已经看到代码实现了仅创建一种类型的代码。那是完全没用的代码IMO。在线上的许多示例都是完全人为设计的,这无济于事。披萨工厂?

也许由于依赖注入,工厂更容易测试。但是在您不需要时添加该代码对我来说毫无意义,并且当您可以使用诸如JMockit之类的模拟框架时,test参数就消失了。编写程序越简单越好。实际上,工厂只有在使用大量类型时才有意义(大的话,我的意思是至少要超过2种)。


是的,披萨工厂,感谢深入浅出设计模式〜
Niing

2

辛格尔顿

我同意辛格尔顿的其他观点。并不是说您永远不要使用它们,而应该将其限制在极少数情况下。他们经常被用作懒惰的全局变量。

线程安全是单例的一个问题。异常处理是另一种-如果单例无法正确创建-您并不总是知道是否可以安全地捕获错误,特别是如果它是“在main之前”创建的那些对象之一。然后还有清理的问题。

我倾向于使用一个单例,而将所有其他“想”单例“订阅”到您的单例。我最常见的单例用法是“喊事件”处理:您只是“广播到世界”某个事件已发生,而任何愿意处理该事件的人都可以这样做。这样,您就可以将实际发生的事件与正在监听的事件分离开。(记录,信号等)

游客

除了开发人员无法想到有意义的名称而仅调用方法visit()的事实外,我发现的丑陋之处在于,它在一个方向上增加了可扩展性,而在另一个方向上删除了它,即,它增加了额外的功能但受到限制访问者需要了解的所有对象类型(可能访问的所有对象类型)数量。

尽管很杂乱,但仍可以允许双向扩展,但这并不能完全使用常规形式的访问者模式。最常见的应用程序是打印对象:您可以通过不同的方式来打印对象和需要打印的对象。您应该能够在两个方向上进行扩展。(打印意味着将对象转换为流的任何类型:存储在文件中/写入控制台/ GUI等。)

(注意:您不应将此与文档视图体系结构混淆,后者是一种略有不同的模式)。


2
您的解决方案使用旧的将事物解耦的参数。特别是,您发布一个事件,然后由其他人处理该事件并将其记录下来。这是一件好事,因为每个人都脱钩了!错误!我曾经那样做,这变成了一种皇家痛苦。我意识到,如果我想要记录某些内容,我想在发生时立即在代码中明确地说出要记录的确切内容。没有其他任何对象找出应该记录的内容,甚至根本不会记录其他处理程序可能插入的其他事件。它不过是令人费解的过度设计。
Dunk

2

我认为某些更复杂的模式的问题在于它们上的变化太多,以至于它们失去了作为通信设备的价值。

我能想到的最严重的犯罪者是MVC模式。即使我们忽略了MVP,每个项目的角色也有太多变化,您必须在每个新框架中花费一个小时来确定边界在哪里。


我也认为MVC应该在任何地方都应使用,而不是在所有地方都以相同的方式实现。许多人不理解。第一次使用它时,我创建了三个类,分别名为Model,View(Windows窗体)和Controller。因为没有为MVC制作表格,所以情况一团糟。
Akku的

1

没有坏模式,只有坏人。

我宁愿继承清晰易读的代码,这些代码做得很清楚,但是比起一些杂乱的混搭,它是否有点冗长(队列邪恶的平民音乐)可重复使用(加油!)InheritAbstractTemplateFaucetSink<Kitchen>

可重用的代码很棒!您可能没有在编写将被重用的代码,或者为另一个应用程序重新编写类似的逻辑所花的时间少于某些疯狂地重用某些其他应用程序的代码所花的时间。

为了进一步阅读,请在posix标头或clib的合理实现中打开一些C代码,并找出模式。这段代码是由世界上一些最聪明,最敬业的程序员编写的。您知道您将看到多少个抽象工厂模式?... 没有!。更好的机会是,如果您了解发生了什么事情的其他部分,您会发现逻辑非常易于理解和跟踪。

我的观点是,大多数“模式”并不是为了使代码更好而创建的,而是为了出售书籍和建模软件而创建的。如果您擅长编程,则可能会避开其中的大多数内容,并编写清晰,简洁,设计巧妙的代码来解决您的问题。遇到其他问题时,您将编写清晰,简洁,设计巧妙的代码来解决该问题。如果您的目标是编写更少的代码,但我认为您不是一名程序员。我喜欢编写代码,并希望尽可能多地编写代码。当我重新编写我已经写过的东西时,它的执行速度快了几十倍,并且摆脱了所有我第一次都不满意的东西。

这样一来,我将为您提供计算机科学领域最好的(相关)报价。

构造软件设计的方法有两种:一种方法是使其简单到显然没有缺陷,另一种方法是使它变得如此复杂以没有明显的缺陷。第一种方法要困难得多。 。

  • Tony Hoare(快速排序的发明者,现代操作系统设计之父,Hoare逻辑的创建者,图灵奖获得者)

0

我绝对同意大多数模式都有时间,并且您可以滥用许多模式。我知道我过去最常使用的一种是“抽象模板”模式。充分来说,它被称为ASP.NET Webforms。

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.