在前文中,我们深入探讨了设计模式与面向对象编程的关系,了解了面向对象编程的基础概念。
本篇文章将进一步深入,系统介绍在软件工程中广泛使用的23种设计模式及其分类,包括创建型模式、结构型模式和行为型模式。我们将探讨这些模式的核心要素,如模式名称、适用场景、解决方案和效果,帮助我们理解如何在实际开发中应用这些模式来优化设计和提高系统的灵活性与复用性。
面向对象与面向过程对比
比如,将大象关到冰箱需要哪些步骤? 打开冰箱门,把大象放入冰箱,关闭冰箱门。这是面向过程,先干什么再干什么然后干什么。每个步骤都必须手动完成,并且代码会从头到尾依次执行。强调的是操作流程,通过函数或流程去执行明确的步骤。
而面向对象编程则关注的是对象及其行为。面向对象是不需要关注细节,知道怎么去使用就可以。在这个例子里,冰箱和大象可以看作两个对象。每个对象都有它自己的属性和方法。我们并不需要知道冰箱内部是如何工作的,只需要调用它的功能。
class Refrigerator {
open() {
console.log("冰箱门打开了");
}
close() {
console.log("冰箱门关上了");
}
put(elephant) {
console.log(`${elephant.name} 被放进冰箱`);
}
}
class Elephant {
constructor(name) {
this.name = name;
}
}
const fridge = new Refrigerator();
const elephant = new Elephant("大象");
fridge.open();
fridge.put(elephant);
fridge.close();
在这个例子里,我们不再关心每一步具体的实现细节,只需要知道调用哪个方法。这样,代码更加模块化和可维护,因为冰箱对象已经封装了如何打开和关闭冰箱的逻辑,外部代码不需要关心这些细节。如果你在现实中扩展这个例子,可以说面向对象更像是我们在使用微波炉、电脑、甚至手机。你并不需要了解它们内部的运作原理,只需要知道如何使用它们就好。这种模式带来了更高的抽象度和可扩展性,同时减少了对底层细节的依赖。简单来说,面向对象编程的优势就是通过封装、继承和多态等机制,使得代码可以更容易地扩展、复用,并且更易于管理。
优势
面向对象有继承、封装、多态的特性。容易维护、容易扩展、容易复用、减少心智负担。
面向对象设计 OOD
是设计分析模型和实现相应源代码,设计问题域的解决方案,与技术相关。00D同样应遵循抽象、信息隐蔽、功能独立、模块化等设计准则。
面向对象的分析模型
面向对象的分析模型主要由顶层架构图、用例与用例图、领域概念模型构成。设计模型则包含以包图表示的软件体系结构图、以交互图表示的用例实现图、完整精确的类图、针对复杂对象的状态图和用以描述流程化处理过程的活动图
等。
面向对象的设计原则
常用的五种。
(1)单一责任原则。就一个类而言,应该仅有一个引起它变化的原因。即,当需要修改某个类的时候原因有且只有一个,让一个类只做一种类型责任。
(2)开放-封闭原则。软件实体(类、模块、函数等)应该是可以扩展的,即开放的;但是不可修改的,即封闭的。
(3)里氏替换原则。子类型必须能够替换掉他们的基类型。 即,在任何父类可以出现的地方,都可以用子类的实例来赋值给父类型的引用。
(4)依赖倒置原则。抽象不应该依赖于细节,细节应该依赖于抽象。 即,高层模块不应该依赖于低层模块,二者都应该依赖于抽象。
(5)接口分离原则。 不应该强迫客户依赖于它们不用的方法。接口属于客户不属于它所在的类层次结构。即:依赖于抽象,不要依赖于具体,同时在抽象级别不应该有对于细节的依赖。这样做的好处就在于可以最大限度地应对可能的变化。
◇ 架构模式
软件设计中的高层决策,例如 C/S架构就属于架构模式, 架构模式反映了开发软件系统过程中所做的基本设计决策。
◇ 设计模式
简单来说就是一种解决方案。在编程实践中针对某种业务场景下的, 好的解决方案总结下来形成设计模式。
每一个设计模式描述了一个在我们周围不断重复发生的问题,以及该问题的解决方案的核心。这样,你就能一次又一次地使用该方案而不必做重复劳动。设计模式的核心在于提供了相关问题的解决方案,使得人们可以更加简单方便的复用成功的设计和体系结构。
四个基本要素: 模式名称、问题(应该在何时使用模式)、解决方案(设计的内容)、效果(模式应用的效果) 。
◇ 惯用法
是最底层的模式, 关注软件系统的设计与实现, 实现时通过某种特定的程序设计语言来描述构件与构件之间的关系。每种编程语言都有它自己特定的模式,即语言的惯用法。例如引用-计数就是C++语言中的一种惯用法。
◇ 23 种设计模式
设计模式有 23 种, 可以分成三种类型, 创建型模式、结构型模式、行为型模式。
创建型模式
主要关注对象的创建方式,旨在通过特定方法创建对象以提高灵活性和复用性。
抽象工厂模式 (Abstract Factory)
提供一个接口,用于创建一系列相关或相互依赖的对象,而无需指定它们的具体类。常用于跨平台系统的界面设计,创建适配不同操作系统的控件。
构建器模式 (Builder)
将复杂对象的构建过程与其表示分离,允许通过相同的构建步骤生成不同的表示。常见于游戏开发中的角色定制,比如分步骤创建角色的身体、面部、发型等。
工厂方法模式 (Factory Method)
定义一个创建对象的接口,但由子类决定具体实例化哪个类,推迟实例化到子类。适用于需要动态决定对象类型的场景,如日志系统中不同日志类型的处理。
原型模式 (Prototype)
通过复制现有的原型实例来创建新对象,而不是直接实例化。适用于需要快速创建多个相似对象的场景,例如对象的缓存与克隆。
单例模式 (Singleton)
确保一个类只有一个实例,并提供全局访问点。例如在Vue.js应用中,Vuex用于管理应用的全局状态,只存在一个实例。
工厂模式和抽象工厂模式的区别?
比如我们是一个生产鼠标的工厂,那么我们这个工厂只能生产鼠标, 但是可以生产不同种类、不同品牌的鼠标, 比如可以生产惠普的、联想的、罗技的等各种各样的。通过一个工厂创建不同类型的对象。
抽象工厂是将工厂这个概念再进一步抽象, 比如我们有一个生产惠普鼠标的工厂, 那么这个工厂只生产惠普的鼠标, 如果想生产联想的,那么我们要再创建一个工厂。抽象工厂如果想生产多个不同的对象的时候, 它需要通过创建对应的工厂来实现。满足闭合原则, 想增加功能时支持扩展, 对扩展开放, 对修改封闭。工厂模式如果想支持其它种类, 要进行内部修改增加, 而不是扩建。
结构型模式
处理类和对象的组合方式,关注如何构建关系和结构以简化代码和提升系统的扩展性。
适配器模式 (Adapter)
将一个类的接口转换为另一种接口,使不兼容的接口可以一起工作。常用于需要对接老旧系统与新系统的接口兼容时。关键词: 转换,兼容接口。
桥接模式 (Bridge)
将抽象部分与实现部分分离,使它们可以独立变化。适用于跨平台应用的开发,抽象出平台无关的部分与具体实现的分离。关键词: 抽象和实现分离。
组合模式 (Composite)
将对象组合成树形结构,以表示整体与部分的层次结构,使用户可以一致地操作单个对象和组合对象。常见于文件系统中的文件与文件夹结构。关键词:整体-部分, 树形结构。
装饰模式 (Decorator)
动态地为对象增加职责,提供比继承更灵活的扩展方式。常用于GUI系统中,动态地为控件添加额外的功能,如滚动条或边框。关键词: 附加职责。
外观模式 (Facade)
提供一个高层接口,简化子系统的复杂性,统一外部访问。比如智能家居系统的场景设置,通过一个接口控制多种设备。关键词: 对外统一接口。
享元模式 (Flyweight)
通过共享细粒度对象,减少内存消耗。适用于大量相似对象的场景,例如文字处理软件中的字符对象复用。关键词:细粒度, 共享。
代理模式 (Proxy)
为另一个对象提供代理,以控制对它的访问。常用于远程服务调用(如RPC)或虚拟代理控制资源访问。关键词: 代理控制。
行为型模式
侧重于对象之间的交互和职责分配,确保对象协同工作并提升系统的响应与灵活性。
职责链模式 (Chain of Responsibility)
将多个处理请求的对象串成一条链,逐个传递请求,直到某个对象处理它,减少发送者与接收者的直接耦合。应用于OA系统中的审批流程,如请假和报销。关键字:请求传递、解耦。
命令模式 (Command)
将请求封装为对象,使请求参数化、排队或记录日志,支持撤销操作。适用于操作记录和撤销场景,如文本编辑器的撤销/重做功能。关键字:请求封装、撤销操作。
解释器模式 (Interpreter)
定义一种语言的语法规则并通过解释器解析句子。常用于规则引擎和游戏中的指令解析,如红警的脚本解释。关键字:语法解析、解释器。
迭代器模式 (Iterator)
提供顺序访问聚合对象元素的方法,不暴露其内部结构。常见于集合遍历,如Java中的Iterator接口。关键字:顺序访问、隐藏内部结构。
中介者模式 (Mediator)
通过中介者封装对象交互,避免对象之间的直接引用,降低耦合性。适用于需要集中管理多个对象通信的场景,如企业服务总线(ESB)。关键字:中介对象、解耦。
备忘录模式 (Memento)
在不破坏封装的前提下保存对象的状态,允许后续恢复。应用于游戏存档和事务回滚场景。关键字:状态保存、恢复。
观察者模式 (Observer)
定义一对多依赖,当一个对象状态改变时,自动通知依赖它的对象。常用于消息通知系统,如微信公众号。关键字:通知、自动更新。
状态模式 (State)
允许对象在其内部状态改变时改变行为。适用于会员系统,根据会员等级调整操作权限。关键字:状态切换、行为变化。
策略模式 (Strategy)
定义一系列算法,并将它们封装起来,算法可根据不同条件动态替换。常见于支付系统,根据不同渠道切换支付方式。关键字:算法封装、动态替换。
模板方法模式 (Template Method)
定义算法的骨架,将某些步骤延迟到子类实现,确保算法结构一致。常用于框架设计中允许自定义的部分功能。关键字:算法骨架、步骤延迟。
访问者模式 (Visitor)
为对象结构中的元素定义新操作,数据和操作分离。常用于数据结构固定、操作频繁变化的系统,如编译器的语法树遍历。关键字:操作扩展、数据与操作分离。