更新时间:2025-04-07
设计模式(Design Pattern) 是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。
设计模式代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。
每个模式都描述了一个在我们的环境中不断出现的问题,然后描述了该问题的解决方案的核心,通过这种方式,我们可以无数次地重用那些已有的解决方案,无需再重复相同的工作。即模式是在特定环境中解决问题的一种方案 。
其目的就是一方面教你如何利用真实可靠的设计来组织代码的模板。简单地说,就是从前辈们在程序设计过程中总结、抽象出来的通用优秀经验。
但是,不应该过于注重程序的“设计模式”。有时候,写一个简单的算法,要比引入某种模式更容易。在多数情况下,程序代码应是简单易懂,甚至清洁工也能看懂。不过呢在大项目或者框架中,没有设计模式来组织代码,别人是不易理解的。
一个软件设计模型也仅仅只是一个引导。它必须根据程序设计语言和你的应用程序的特点和要求而特别的设计。
开发人员的共同平台
设计模式提供了一个标准的术语系统,且具体到特定的情景。例如,单例设计模式意味着使用单个对象,这样所有熟悉单例设计模式的开发人员都能使用单个对象,并且可以通过这种方式告诉对方,程序使用的是单例模式。
最佳的实践
设计模式已经经历了很长一段时间的发展,它们提供了软件开发过程中面临的一般问题的最佳解决方案。学习这些模式有助于经验不足的开发人员通过一种简单快捷的方式来学习软件设计。
模式名称(pattern name)
一个设计模式的标识(模式名称)是重要的,因为它会让其他的程序员不用进行太深入的学习就能立刻理解你的代码的目的(至少通过这个标识程序员会很熟悉这个模式)。没有这个模式名,我们便无法与其他人交流设计思想及设计结果。
问题(problem)
描述了应该在何时使用模式。它解释了设计问题和问题存在的前因后果,它可能描述了特定的设计问题,如怎样用对象表示算法等。也可能描述了导致不灵活设计的类或对象结构。有时候,问题部分会包括使用模式必须满足的一系列先决条件。
解决方案(solution)
描述了设计的组成成分,它们之间的相互关系及各自的职责和协作方式。因为模式就像一个模板,可应用于多种不同场合,所以解决方案并不描述一个特定而具体的设计或实现,而是提供设计问题的抽象描述和怎样用一个具有一般意义的元素组合(类或对象组合)来解决这个问题。
效果(consequences)
描述了模式应用的效果及使用模式应权衡的问题。尽管我们描述设计决策时,并不总提到模式效果,但它们对于评价设计选择和理解使用模式的代价及好处具有重要意义。软件效果大多关注对时间和空间的衡量,它们也表述了语言和实现问题。因为复用是面向对象设计的要素之一,所以模式效果包括它对系统的灵活性、扩充性或可移植性的影响,显式地列出这些效果对理解和评价这些模式很有帮助。
- 一个好的设计模式的论述应该覆盖使用这个模型的优点和缺点。
- 一个模式是解决特定问题的有效方法。一个设计模式不是一个库(能在你的项目中直接包含和使用的代码库)而是一个用来组织你的代码的模板(Java bean)。事实上,一个代码库和一个设计模式在应用上是有很多不同的。
- 比如,你从店铺里面买的一件衬衫是一个代码库,它的颜色,样式和大小都由设计师和厂商决定,但它满足了你的需求。 然而,如果店里面没有什么衣服适合你,那你就能自己创建自己的衬衫(设计它的形状,选择布料,然后裁缝在一起)。但是如果你不是一个裁缝,你可能会发现自 己很容易的去找一个合适的模式然后按着这个模式去设计自己的衬衫。使用一个模型,你可以在更少的时间内得到一个熟练设计的衬衫。
- 回到讨论软件上来,一个数据提取层或者一个CMS(content management system)就是一个库——它是先前设计好而且已经编码好了的,如果它能准确的满足你的需要那它就是一个好的选择。但如果你正在读这本书《设计模式》,可能你会发现 库存的(原有的)解决方案并不是总是对你有效。至今你知道什么是你所要的,而且你能够实现它,你仅仅需要一个模型来引导你。
- 最后一个想法:就象一个裁缝模型,一个设计本身而言是没有什么用处的。毕竟,你不可能穿一个服装模型——它仅仅是由很薄的纸拼凑起来的。类似的,一个软件设计模型也仅仅只是一个引导。它必须根据程序设计语言和你的应用程序的特点和要求而特别的设计。
根据其目的(模式是用来做什么的)可分为 创建型 , 结构型 和 行为型 三种:
根据范围,即模式主要是用于处理类之间关系还是处理对象之间的关系,可分为类模式和对象模式两种:
这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用 new 运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。
子类 | 模式名称(中文) | 模式名称(英文) | 核心作用 |
---|---|---|---|
类模式 | 工厂方法模式 | Factory Method Pattern | 通过子类决定实例化对象的类型,实现对象创建与使用的解耦 |
对象模式 | 抽象工厂模式 | Abstract Factory Pattern | 创建一组相关或依赖对象的家族,无需指定具体类 |
对象模式 | 建造者模式 | Builder Pattern | 分步骤构造复杂对象,支持不同配置和表示形式 |
对象模式 | 原型模式 | Prototype Pattern | 通过克隆现有对象创建新对象,避免重复初始化成本 |
对象模式 | 单例模式 | Singleton Pattern | 确保一个类只有一个实例,并提供全局访问入口 |
这些设计模式关注类和对象的组合。继承的概念被用来组合接口和定义组合对象获得新功能的方式。
子类 | 模式名称(中文) | 模式名称(英文) | 核心作用 |
---|---|---|---|
类模式 | 类适配器模式 | Adapter Pattern (Class) | 通过继承适配接口,将类的接口转换为目标接口 |
对象模式 | 对象适配器模式 | Adapter Pattern (Object) | 通过对象组合适配接口,实现更灵活的接口转换 |
对象模式 | 桥接模式 | Bridge Pattern | 分离抽象部分与实现部分,允许两者独立扩展 |
对象模式 | 组合模式 | Composite Pattern | 以树形结构统一处理单个对象和组合对象,简化客户端逻辑 |
对象模式 | 装饰器模式 | Decorator Pattern | 动态地为对象添加职责,避免因继承导致的子类膨胀 |
对象模式 | 外观模式 | Facade Pattern | 为复杂子系统提供统一的简化接口,降低调用复杂度 |
对象模式 | 享元模式 | Flyweight Pattern | 共享大量细粒度对象,减少内存占用 |
对象模式 | 代理模式 | Proxy Pattern | 为其他对象提供代理,控制访问(如延迟加载、权限校验) |
这些设计模式特别关注对象之间的通信。
子类 | 模式名称(中文) | 模式名称(英文) | 核心作用 |
---|---|---|---|
类模式 | 解释器模式 | Interpreter Pattern | 定义语法规则并解释特定领域语言(如正则表达式、数学公式) |
对象模式 | 责任链模式 | Chain of Responsibility | 将请求沿处理链传递,直到有对象处理它 |
对象模式 | 命令模式 | Command Pattern | 将请求封装为对象,支持撤销、重做和事务操作 |
对象模式 | 迭代器模式 | Iterator Pattern | 提供统一的接口遍历聚合对象元素,隐藏底层实现细节 |
对象模式 | 中介者模式 | Mediator Pattern | 集中管理对象间交互,减少直接依赖 |
对象模式 | 备忘录模式 | Memento Pattern | 捕获并外部化对象内部状态,支持状态恢复 |
对象模式 | 观察者模式 | Observer Pattern | 定义一对多依赖关系,对象状态变化时自动通知观察者 |
对象模式 | 状态模式 | State Pattern | 允许对象在内部状态改变时改变行为,替代条件语句 |
对象模式 | 空对象模式 | Null Object Pattern | 用空对象替代 null ,避免显式的空值检查 |
对象模式 | 策略模式 | Strategy Pattern | 封装算法族,使其可互换且独立于客户端 |
对象模式 | 模板方法模式 | Template Method Pattern | 在父类定义算法框架,子类实现具体步骤 |
对象模式 | 访问者模式 | Visitor Pattern | 在不修改类结构的前提下,为其添加新操作 |
1. [设计模式的核心原则]开闭原则(Open - Closed Principle):对扩展开放,对修改关闭。
将程序艺术化是我们的目标!
2. 里氏代换原则(Liskov Substitution Principle):任何基类可以出现的地方,子类也可以出现。
3. 依赖倒转原则(Dependence Inversion Principle):要依赖抽象,而不要依赖具体的实现。
4. 合成/聚合复用原则(CARP):要尽量使用合成/聚合原则,而不是继承关系达到软件复用的目的。
合成/聚合复用原则(Composite/Aggregate ReusePrinciple或CARP) 经常又叫做 合成复用原则(Composite ReusePrinciple或CRP),就是在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分;新对象通过向这些对象的委派达到复用已有功能的目的。简而言之,要尽量使用合成/聚合,尽量不要使用继承。
要尽量使用合成/聚合原则,而不是继承关系达到软件复用的目的。此原则和里氏代换原则氏相辅相成的,两者都是具体实现"开-闭"原则的规范。违反这一原则:就无法实现"开-闭"原则。先来看看什么是合成,什么是聚合。
合成:是指一个整体对依托他而存在的关系。
例如:一个人对他的房子和家具,其中他的房子和家具是不能被共享的,因为那些东西都是他自己的。并且人没了,这个也关系就没了。这个例子就好像,乌鸡百凤丸这个产品,它是有乌鸡和上等药材合成而来的一样。也比如网络游戏中的武器装备合成一样,多种东西合并为一种超强的东西一样。
聚合:聚合是比合成关系的一种更强的依赖关系,聚合是一个整体对个体的部分。
例如,一个奔驰S360汽车,对奔驰S360引擎,奔驰S360轮胎的关系。这些关系就是带有聚合性质的。因为奔驰S360引擎和奔驰S360轮胎他们只能被奔驰S360汽车所用,离开了奔驰S360汽车,它们就失去了存在的意义。在我们的设计中,这样的关系不应该频繁出现。这样会增大设计的耦合度。
明白了合成和聚合关系,再来理解合成/聚合原则应该就清楚了。要避免在系统设计中出现,一个类的继承层次超过3次。如果这样的话,可以考虑重构你的代码,或者重新设计结构。当然最好的办法就是考虑使用合成/聚合原则。
5. 迪米特法则(Law of Demeter或简写LoD):系统中的类,尽量不要与其他类互相作用,减少类之间的耦合度。
系统中的类,尽量不要与其他类互相作用,减少类之间的耦合度,因为在你的系统中,扩展的时候,你可能需要修改这些类,而类与类之间的关系,决定了修改的复杂度,相互作用越多,则修改难度就越大,反之,如果相互作用的越小,则修改起来的难度就越小。例如A类依赖B类,则B类依赖C类,当你在修改A类的时候,你要考虑B类是否会受到影响,而B类的影响是否又会影响到C类。如果此时C类再依赖D类的话……我想这样的修改有的受了。
6. 接口隔离法则(Interface Segregation Principle):
设计模式是从许多优秀的软件系统中总结出的成功的、能够实现可维护性复用的设计方案,使用这些方案将避免我们做一些重复性的工作,而且可以设计出高质量的软件系统。
设计模式的主要优点如下:
设计模式融合了众多专家的经验,并以一种标准的形式供广大开发人员所用,它提供了一套通用的设计词汇和一种通用的语言以方便开发人员之间沟通和交流,使得设计方案更加通俗易懂。对于使用不同编程语言的开发和设计人员可以通过设计模式来交流系统设计方案,每一个模式都对应一个标准的解决方案,设计模式可以降低开发人员理解系统的复杂度。
设计模式使人们可以更加简单方便地复用成功的设计和体系结构,将已证实的技术表述成设计模式也会使新系统开发者更加容易理解其设计思路。设计模式使得重用成功的设计更加容易,并避免那些导致不可重用的设计方案。
设计模式使得设计方案更加灵活,且易于修改。
设计模式的使用将提高软件系统的开发效率和软件质量,且在一定程度上节约设计成本。
设计模式有助于初学者更深入地理解面向对象思想,一方面可以帮助初学者更加方便地阅读和学习现有类库与其他系统中的源代码,另一方面还可以提高软件的设计水平和代码质量。
最后,设计模式不是学出来的,是用出来的。 为了学习设计模式而学习,效果可能不是很好。
一般框架都会使用设计模式。如PHP的ZF用来很多设计模式,框架里面的类名或者目录名,都以某种设计模式的名称命名,这样大家一看到这个类名或者文件名,就知道它的代码组织结构了。如果精通了语言,剩下的编码自然是很简单,随着编码经验积累,对设计模式和原则的理解也就越透彻,其过程就是山穷水复疑无路,而结果柳暗花明又一村。
熟练模式后,切勿因模式而去模式。
如同著名数学家华罗庚谈到读书的三个境界所说,“读书是由薄到厚,再由厚到薄的过程”。
[1] 设计模式简介
https://www.runoob.com/design-pattern/design-pattern-intro.html
[2] 设计模式概论
https://blog.csdn.net/hguisu/article/details/7496819
[3] 优秀程序设计的18大原则 Christopher Diggins
https://www.artima.com/weblogs/viewpost.jsp?thread=331531
[4] 《Head First设计模式》
Freeman, 中国电力出版社
声明
- 本文版权归
CSDN
用户Allen Wurlitzer
所有,遵循CC-BY-SA
协议发布,转载请注明出处。