许多人似乎都同意,Singleton模式有许多缺点,有些甚至建议完全避免这种模式.这里有一个很好的讨论.请将有关Singleton模式的任何评论指向该问题.
我的问题:是否有其他设计模式,应该避免或小心使用?
应谨慎使用所有设计模式.在我看来,你应该在有正当理由的情况下对模式进行重构,而不是立即实现模式.使用模式的一般问题是它们增加了复杂性.过度使用模式使得给定的应用程序或系统进一步开发和维护变得麻烦.
大多数情况下,有一个简单的解决方案,您不需要应用任何特定模式.一个好的经验法则是每当代码片段被替换或需要经常更改时使用模式,并准备在使用模式时采用复杂代码的警告.
请记住,如果您发现支持代码更改的实际需要,那么您的目标应该是简单并采用模式.
如果它们显然会导致过度设计和复杂的解决方案,那么使用模式似乎是没有实际意义的.然而,对于程序员来说,阅读有关大多数模式奠定基础的设计技术和原则更为有趣.事实上,我最喜欢的一本关于"设计模式"的书籍强调了这一点,重申了哪些原则适用于所讨论的模式.它们很简单,在相关性方面比模式更有用.只要您可以构建代码模块,一些原则通常足以涵盖面向对象编程(OOP),例如Liskov替换原则.
有许多设计原则,但GoF书第一章中描述的那些原则非常有用.
编程为"接口",而不是"实现".(Gang of Four 1995:18)
赞成'对象组合'而不是'类继承'.(Gang of Four 1995:20)
让那些沉沦在你身上一段时间.应该注意的是,当编写GoF时,接口意味着任何抽象(也意味着超类),不要将接口与Java或C#中的类型混淆.第二个原则来自观察到的过度使用继承,这在今天仍然很常见.
从那里你可以阅读由罗伯特塞西尔马丁(又名鲍勃叔叔)所知的SOLID原则.Scott Hanselman在一个关于这些原则的播客中采访了鲍勃叔叔:
小号英格尔责任原则
O pen封闭原则
L iskov替代原则
我接口隔离原则
d ependency倒置原则
这些原则是您与同行一起阅读和讨论的良好开端.您可能会发现这些原则相互交织,并与其他过程交织在一起,例如关注点分离和依赖注入.在做了一段时间的TDD之后,你也可能会发现这些原则在实践中很自然,因为你需要在某种程度上遵循这些原则才能创建孤立和可重复的单元测试.
设计模式的作者自己最担心的是"访客"模式.
这是一个"必要的邪恶" - 但经常被过度使用,对它的需求经常会在你的设计中揭示出一个更为根本的缺陷.
"访问者"模式的另一个名称是"Multi-dispatch",因为当您希望使用单一类型的调度OO语言根据两种类型选择要使用的代码时,访问者模式就是您最终的结果. (或更多)不同的对象.
经典的例子是你有两个形状之间的交集,但是有一个更简单的情况经常被忽视:比较两个异构对象的相等性.
无论如何,通常你最终得到这样的东西:
interface IShape { double intersectWith(Triangle t); double intersectWith(Rectangle r); double intersectWith(Circle c); }
这个问题是你把所有的"IShape"实现结合在了一起.您暗示无论何时您希望向层次结构添加新形状,您都需要更改所有其他"形状"实现.
有时,这是正确的最小设计 - 但要仔细考虑.您的设计是否真的要求您需要分发两种类型?你是否愿意撰写多种方法的组合爆炸?
通常,通过引入另一个概念,您可以减少实际需要编写的组合数量:
interface IShape { Area getArea(); } class Area { public double intersectWith(Area otherArea); ... }
当然,这取决于 - 有时你真的需要编写代码来处理所有这些不同的情况 - 但是在采取倾向和使用访问者之前,值得暂停和思考.以后它可能会为你节省很多痛苦.
单身人士 - 使用单身人士X的一个阶段依赖于它,很难看到并难以隔离进行测试.
它们经常被使用,因为它们方便且易于理解,但它们确实使测试复杂化.
看单身人士是病理性的谎言.
我认为模板方法模式通常是一种非常危险的模式.
很多时候,由于"错误的原因",它会占用你的继承层次结构.
基类有一种充满各种无关代码的倾向.
它会迫使您在开发过程中很早就锁定设计.(在很多情况下过早锁定)
在稍后阶段改变这一点变得越来越难.
我不认为你应该避免设计模式(DP),我认为在规划你的架构时你不应该强迫自己使用DP.我们应该只在他们自然规划出现时才使用DP.
如果我们从一开始就定义我们想要使用给定的DP,我们的许多未来设计决策将受到该选择的影响,并不能保证我们选择的DP适合我们的需求.
我们也不应该做的一件事是将DP视为不可变的实体,我们应该根据我们的需要调整模式.
所以,sumarizing,我认为我们不应该避免使用DP,我们应该在它们已经在我们的架构中形成时接受它们.
我认为Active Record是一种过度使用的模式,它鼓励将业务逻辑与持久性代码混合在一起.它不能很好地从模型层隐藏存储实现并将模型绑定到数据库.有许多替代方案(在PoEAA中描述),例如表数据网关,行数据网关和数据映射器,它们通常提供更好的解决方案,当然有助于为存储提供更好的抽象.此外,您的模型不需要存储在数据库中; 如何将它们存储为XML或使用Web服务访问它们呢?改变模型的存储机制有多容易?
也就是说,Active Record并不总是坏的,对于其他选项过度的简单应用来说是完美的.
这很简单......避免您不熟悉的设计模式或您感觉不舒服的设计模式.
举几个...
有一些不切实际的模式,例如:
Interpreter
Flyweight
还有一些难以掌握,例如:
Abstract Factory
- 具有创建对象族的完整抽象工厂模式并不像看起来那样轻而易举
Bridge
- 如果抽象和实现被划分为子树,可能会过于抽象,但在某些情况下是非常有用的模式
Visitor
- 双重调度机制的理解实际上是必须的
并且有一些模式看起来非常简单,但由于与其原理或实施相关的各种原因而不是那么明确的选择:
Singleton
- 不是真的完全糟糕的模式,只是过度使用(通常在那里,它不适合)
Observer
- 伟大的模式...只是使代码更难阅读和调试
Prototype
- 交易编译器检查动态(可能是好还是坏......取决于)
Chain of responsibility
- 经常被强制/人为地推入设计中
对于那些"不实用的",在使用它们之前应该考虑一下,因为在某些地方通常会有更优雅的解决方案.
对于"更难掌握"的......当它们在合适的地方使用并且实施得很好时,它们真的很有帮助......但是当它们使用不当时,它们就是噩梦.
现在,下一步是什么......
首先设计模式是必须的
来源制造是"急救"
我希望我不会为此受到太多打击.Christer Ericsson 在他的实时碰撞检测博客中撰写了两篇关于设计模式主题的文章(一篇,两篇).他的语气相当苛刻,也许有点挑衅,但是男人知道他的东西,所以我不会把它当成疯子的狂欢.
有人说服务定位器是一种反模式.