2.5 用上设计模式你就厉害了?

为什么用了设计模式后反而没有那么厉害?
1. 为了用而用
首先想要使用上某个设计模式,然后就修改代码让其使用上。比方说为了将采用策略模式,而修改代码使其可以使用上策略模式,这就是为了用上设计模式而用上设计模式。
假设我们有一个动物园,目前里面只有狮子和老虎,我们需要让它们发出叫声。
如果我们采取直观的方法,我们可以创建一个 Animal 类,并为每种动物定义一个子类:
这样,我们可以让每个动物发出叫声:
但是,如果我们强制使用策略模式,我们需要首先定义一个策略接口,然后为每种动物的叫声创建一个策略:
然后,我们可以像这样使用它:
虽然策略模式提供了一种更灵活的方式来管理动物的叫声,但在这种情况下,它却引入了不必要的复杂性。在这个简单的例子中,使用策略模式并没有带来实际的好处,反而使代码变得更难理解。这就是“为了使用设计模式而使用设计模式”的问题所在。
2. 过度设计
有时候,为了提高扩展性,可能会过度使用设计模式,造成“过度设计”。这可能会使代码变得冗余和复杂,反而降低了代码的可读性和可维护性。
例如说用户的点击操作记录统计功能,为了未来可能的扩展性,你可能考虑使用观察者模式,让其他对象可以在用户点击时接收到通知。
然后在主程序中,你可能这样使用:
虽然这个设计可以让你在未来很容易地添加更多的观察者,但是如果你从来不需要添加其他观察者,那么这个设计就显得过度了。其实,一个简单的整数变量就可以满足你记录点击次数的需求,无需引入观察者模式的复杂性。
3. 预先优化
经常为了未来的需求做考虑,然后基于未来的需求做抽象设计,然后引入了设计模式。必然说,需要实现一个加法运算,那你直接写一个加法函数即可,不需要为了这一个功能点直接开发一个计算器。
但是,如果过度设计,你可能会采用策略模式,来设计一个更抽象的计算器:
虽然这个设计看起来非常灵活,你可以通过修改 Calculator 的 Operation 来改变其行为,但是如果你只需要执行加法运算,这个设计显然过于复杂。过度设计往往会使代码变得更难理解和维护,所以在做设计决策时,我们需要找到复杂性和灵活性之间的平衡。
关于使用设计模式的建议
清晰的项目结构: 在考虑引入设计模式前,确保你的项目业务模型和结构是清晰的。如果你的代码是基于面向过程,缺乏适当的封装和抽象,那么引入设计模式可能会使代码更加复杂,反而不利于维护。
目标是为了可维护与可拓展: 设计模式应当被用来提高代码的可维护性和可扩展性。如果引入设计模式并未达到这样的效果,那么可能需要重新考虑你的设计。
避免过度设计和预先优化: 你应该试图满足当前的需求,而不是过度设计以满足可能的未来需求。过度追求设计模式可能导致过度设计,这可能会使代码变得不必要的复杂和难以理解。
理解设计模式的意图和原理,使用设计模式之前,确保你理解其意图和原理,而不仅仅是如何实现它。设计模式并不是代码模板,而是用来解决特定设计问题的通用解决方案。理解设计模式的背后原理,可以帮助你判断何时应该使用它,以及如何根据具体情况调整它。
设计模式应用需要适度: 设计模式的应用是为了解决实际问题,而不是为了让代码看起来“专业”或“复杂”。如果在不需要的情况下使用设计模式,或者过度使用设计模式,可能会使代码变得不必要的复杂和难以理解。
考虑团队因素: 如果你的团队成员对某种设计模式不熟悉,那么在项目中引入这种设计模式可能会带来问题。在决定使用哪种设计模式时,你需要考虑到团队成员的知识和技能。
重构是持续的过程: 在软件开发过程中,随着需求的变化和项目的发展,可能需要对代码进行重构。在这个过程中,可能会发现一些设计模式的应用场景。因此,不需要一开始就强求使用设计模式,而是在重构过程中逐步引入。
设计模式介绍
设计模式通常被归类为创建型、结构型和行为型三大类,各有不同的目标和应用场景。
以下是对这23种设计模式的归类和简单总结:
创建型模式
这类模式的主要关注点是如何创建对象,特别是在系统要求对创建过程进行更多控制和管理时。
单例模式:确保一个类只有一个实例,并提供全局访问点。
原型模式:使用复制现有对象的方式来创建新对象。
建造者模式:分离对象的构建和表示,同样的构建过程可以创建不同的表示。
工厂方法模式:定义一个创建对象的接口,但让子类决定要实例化哪个类。
抽象工厂模式:提供一个接口以创建一系列相关或依赖对象,而无需指定具体的类。
结构型模式
这类模式关注如何组织不同的对象和类以形成更大的结构,同时保持结构的灵活和高效。
适配器模式:使接口不兼容的类可以一起工作,将一个类的接口转换为客户希望的另一个接口。
桥接模式:将抽象和实现解耦,以便两者可以独立变化。
组合模式:允许将对象组合成树形结构以表示"部分-整体"的层次结构,使得用户对单个对象和组合对象的使用具有一致性。
装饰模式:动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。
外观模式:为一组接口提供一个统一的高层接口,使得使用更为方便。
享元模式:运用共享技术来有效地支持大量细粒度的对象。
代理模式:为其他对象提供一种代理以控制对这个对象的访问。
行为型模式
这类模式关注对象之间的通信,它们主要解决的是"对象如何、何时、以何种方式进行交互"的问题。
模板方法模式:定义算法的框架,而将一些步骤的实现延迟到子类。
访问者模式:为一个对象结构中的元素提供新的操作而不改变其类的结构。
迭代器模式:提供一种方法顺序访问一个聚合对象中各个元素,而又不暴露该对象的内部表示。
观察者模式:定义对象间的一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。
中介者模式:用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
命令模式:将一个请求封装为一个对象,从而使用户可以使用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。
状态模式:允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。
策略模式:定义一系列的算法,把它们一个个封装起来,并且使它们可以互相替换。
责任链模式:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止。
备忘录模式:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后恢复对象到这个状态。
解释器模式:给定一个语言,定义它的语法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。