软件设计模式的个人理解
-
- 前提
- 软件设计模式的简单分类
- 面向对象的设计的7项原则简介
-
- 1. 开闭原则
- 2. 里氏替换原则
- 3. 依赖倒置原则
- 4. 单一职责原则
- 5. 接口隔离原则
- 6. 迪米特法则
- 7. 合成复用原则
- 7种设计原则的要点
- 创建型模式
-
- 单例模式
- 原型模式
- 工厂方法模式
- 抽象工厂模式
- 建造者模式
- 结构型模式
-
- 代理模式
- 适配器模式
- 桥接模式
- 装饰模式
- 外观模式
- 享元模式
- 组合模式
- 行为型模式
-
- 模板方法模式
- 策略模式
- 命令模式
- 职责链模式
- 状态模式
- 观察者模式
- 中介者模式
- 迭代器模式
- 访问者模式
- 备忘录模式
- 解释器模式
前提
如果你不太明白抽象和接口,可以先来参考一下:抽象和接口
软件设计模式的简单分类
GoF(Gang of Four)的 23 种设计模式:
-
创建型模式:单原工抽建
-
结构型模式:代适桥装,外享组
-
行为型模式:模策命,取状观,中迭访备解
面向对象的设计的7项原则简介
软件开发中,程序员要尽量根据以下7条原则来开发程序,从而提高软件的开发效率、节约软件开发成本和维护成本。
- 开闭原则
- 里氏替换原则
- 依赖倒置原则
- 单一职责原则
- 接口隔离原则
- 迪米特法则
- 合成复用原则
1. 开闭原则
-
定义:软件实体应当对扩展开放,对修改关闭。
-
作用:
- 在软件测试时,只需要测试扩展的代码
- 提高代码的复用性(粒度越小,被复用的可能性越大)
- 提高软件的可维护性
-
实现:通过接口或抽象类为软件实体定义一个相对稳定的抽象层,将相同的可变因素封装在相同的具体实现类中。
2. 里氏替换原则
- 定义:子类可以扩展父类的功能,但不能改变父类原有的功能。
- 作用:
- 里氏替换原则是实现开闭原则的重要方式之一 (扩展了子类而不能修改父类)
- 克服了继承中重写父类造成的可用性变差的缺点 (不能修改父类)
- 是动作正确性的保证 (扩展的类不会给已有的系统引入错误,如开闭原则中只测试扩展的代码)
- 实现:子类继承父类时,除了添加新方法外,尽量不要重写父类的方法。
3. 依赖倒置原则
-
定义:高层,底层,细节都依赖于抽象。
-
核心思想:要面向接口编程,而不是面向实现编程:

-
作用:
- 降低类间的耦合性
- 提高系统的稳定性
- 减少并行开发引起的风险
- 提高代码的可读性和可维护性
-
实现:
- 每个类尽量提供接口或抽象类,或两者都提供
- 变量的声明类型尽量使接口或者是抽象类
- 任何类都不应该从具体类派生
- 使用继承时尽量遵循里氏替换原则
4. 单一职责原则
- 定义:一个类有且仅有一个引起它变化的原因,否则应该被拆
- 理解:一个人应该只有一个引起他心动的原因,否则应该拆分。
- 作用(优点):
- 降低类的复杂性
- 提高类的可读性
- 提高系统的可维护性
- 变更引起的风险降低
- 实现:设计人员发现类的不同职责并将其分离,再封装到不同的类或模块中。
5. 接口隔离原则
-
定义:将臃肿庞大的接口拆分成更小的和更具体的接口,让接口中只包含客户喜欢的方法。
-
理解:卖车的时候根据客户的经济能力为客户分配合适的车辆销售,而不是客户只有10W的预支却提供5W~200W的销售。
-
优点:
- 提高系统的灵活性和可维护性。
- 提高了系统的内聚性。
- 接口的粒度大小定义合理,保证系统的稳定性。
- 使用多个专门的接口体现对象的层次。
- 减少项目工程中的代码冗余。
-
实现:
- 接口尽量小,但应有限度。(不能细分到轮胎,方向盘,发动机,而应是整车销售)
- 为依赖接口的类定制服务。
- 了解环境,拒绝盲从。
- 提高内聚,减少对外交互。
6. 迪米特法则
- 定义:只与朋友说话,不与陌生人说话。
- 理解:Demeter is a famous star in the world , and you can just get in touch with him by his assistant.
- 优点:
- 降低了类之间的耦合度,提高模块的相对独立性。
- 提高了类的可复用率和系统的扩展性。
- 缺点:增加系统的复杂性,减低通讯效率。
- 强调:
- 从依赖者的角度说,只依赖应该依赖的对象。
- 从被依赖者的角度说,只暴露应该暴露的方法。
- 运用时注意:
- 创建弱耦合的类
- 降低类成员的访问权限
- 优先考虑将一个类设置成不变类
- 将引用其他对象的次数降到最低
- 不暴露类的属性成员,提供set和get方法
- 谨慎使用序列化(Serializable)功能
7. 合成复用原则
- 定义:在软件复用时,要尽量先使用组合或者聚合等关联关系来实现,其次考虑使用继承关系来实现。
- 理解:复用时的优先级定义。
- 实现:将已有的对象纳入新对象中,作为新对象的成员对象来实现的,新对象可以调用已有对象的功能,从而实现复用。
7种设计原则的要点
对修改关闭:开闭原则
不破坏继承:里氏替换原则
面向接口编程:依赖倒置原则
类的职责单一:单一职责原则
设计接口精简:接口隔离原则
降低耦合度:迪米特法则
优先使用组合或聚合关系复用,少用继承关系复用:合成复用原则
总而言之,7种设计原则就是考试范围,如果不用就等于盲目复习,在整个过程中效率低下。
创建型模式
概述:怎样创建模型,将对象的创建和使用分离。
单例模式
-
定义:一个类只有一个实例,且该类能自行创建这个实例。
-
特点:
- 只有一个实例对象
- 该单例对象必须由单例类自行创建
- 对外提供一个访问该单例的全局访问点
-
角色:
- 单例类
- 访问类
-
实现:
- 懒汉式:在类加载时没有生成单例,第一次调用getInstance方法时才创建。
- 饿汉式:类一旦加载就创建一个单例,保证在第一次调用getInstance方法前单例存在。
-
应用:
- 如果一个类频繁实例化,创建的对象又频繁销毁。
- 只生成一个对象
- 对象需要被共享的场合
-
举例:班长,身份证 。
原型模式
-
定义:用一个已经创建的实例作为原型,通过赋值该原型对象来创建一个和原型相同或相似的新对象。
-
角色:
- 抽象原型类
- 具体原型类
- 访问类
-
实现:原型模式的克隆分为浅克隆和深克隆。
Java中的Object类提供了浅克隆的clone()方法。
-
应用场景:
- 对象间相同或相似(个别属性不同)
- 对象创建过程比较麻烦,但是复制方便
工厂方法模式
- 定义:定义一个创建产品对象的工厂接口,将产品对象的实际创建工作推迟到具体子工厂中
- 理解:电视机,电冰箱工厂
- 优点:
- 用户只需要知道工厂的名字就可以得到所要的产品
- 系统新家新的产品只需要添加具体产品类和具体工厂类
- 角色:
- 抽象工厂
- 具体工厂
- 抽象产品
- 具体产品
- 应用场景:
- 客户只知道创建产品的工厂名,而不知道具体的产品名
- 创建对象得到任务有多个具体子工厂中的某一个完成,而抽象工厂只提供创建产品的接口
- 客户不关心创建产品的细节,只关心品牌
抽象工厂模式
- 定义:为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无须指定所要产品的具体类就能得到同族的不同等级的产品模式结构
- 理解:电器工厂
- 优点:
- 在类的内部对产品族中相关联的多等级产品共同管理
- 增加一个新的产品族不需要修改代码
- 角色:同工厂模式一样
- 应用场景:
- 创建的对象是一系列相互关联或相互依赖的产品族(电器工厂中电视机,洗衣机)
- 系统中有多个产品族,但每次只使用某一族产品
- 系统中提供了产品的类库,且所有产品的接口相同,客户端不依赖产品实例的创建细节和内部结构
建造者模式
- 定义:将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示
- 理解:装修客厅时,客户告诉负责人需求,负责人分配工人干活
- 优点:
- 各个具体的建造者相互独立,有利于系统的扩展
- 客户端不必知道产品内部组成的细节,便于控制细节风险
- 缺点:
- 产品的组成部分必须相同,限制了使用范围
- 如果产品的内部变化复杂,会增加很多的建造者类
- 角色:
- 产品角色
- 抽象建造者
- 具体建造者
- 指挥者
- 应用场景:
- 创建的对象较复杂,由多个部件构成,各部件面临复杂的变化,但构件间的建造方式是稳定的
- 产品的构建过程和最终的表示是独立的
结构型模式
概述:描述如何将类或对象按某种布局组成更大的结构。
代理模式
-
定义:由于某些原因需要给某对象提供一个代理以控制对该对象的访问。
-
举例:微信上的某些鞋厂代理商(各种鞋都卖)
-
优点:
- 在客户端和目标对象之间起到一个中介作用和保护目标对象的作用
- 代理对象可以扩展目标对象的功能
- 分离客户端和目标对象,降低系统的耦合度
-
缺点:
- 请求处理速度变慢
- 增加系统的复杂性
-
角色:
-
抽象主题类
-
真实主题类
-
代理类
-
-
En:agency,proxy
适配器模式
-
定义:使原来的接口转换成用户希望的接口,使原来因为接口不兼容而不能工作的类而正常工作
-
举例:Type-c接口耳机转换器,读卡器(年代久远)
-
优点:
- 客户端通过适配器可以透明的调用接口
- 复用现存的类
- 将目标类和适配者类解耦,解决目标类和适配者类接口不一致的问题
-
缺点:更换适配器的过程比较复杂
-
角色:
- 目标接口
- 适配器类
- 适配者类
-
En: Adapter
桥接模式
- 定义:将抽象和实现分离,使它们可以独立变化
- 举例:按分类调用某款背包(颜色,类型,大小)
- 优点:
- 扩展能力强
- 实现细节对客户透明
- 缺点: 聚合关系建立在抽象层,增加了系统的理解和设计难度。
- 角色:
- 抽象化角色
- 扩展化角色
- 实现化角色
- 具体实现化角色
- En:bridge,concrete , refine
装饰模式
- 定义:不改变现有对象结构的情况下,动态地给该对象增加一些职责的模式。
- 举例:巴啦啦小魔仙全身变
- 优点:
- 比采用父类继承更灵活
- 可以设计出多个不同的具体装饰类,创造出多个不同行为的组合
- 缺点:装饰模式增加了许多子类
- 角色:
- 抽象构件
- 具体构件
- 抽象装饰
- 具体装饰
- En:concrete,decorator,component
外观模式
- 定义:通过为多个复杂的子系统提供一个一致的接口,而使这些子系统更加容易被访问的模式。
- 举例:市民之家提供一个统一的接口办理许多业务
- 优点:
- 降低了子系统与客户端之间的耦合度
- 对客户屏蔽了子系统组件
- 降低了大型软件系统中的编译依赖性
- 缺点:
- 不能很好的限制客户使用子系统类
- 增加新的子系统可能会修改客户类或客户端的源代码(
开闭原则)
- 角色:
- 外观角色
- 子系统角色
- 客户角色
- En:facade,sub,client
享元模式
-
定义:运用共享技术有效支持大量细粒度对象的复用。
-
举例:五子棋的棋子生成,从享元工厂中取出。
-
优点:相同对象只保存一份,降低了系统中对象的数量。
-
缺点:
- 一些不能共享的状态外部化,增加程序的复杂性。
- 读取享元模式的外部状态会使得运行时间变长。
-
角色:
- 抽象享元角色
- 具体享元角色
- 非享元角色
- 享元工厂角色
-
En:flyweight,concrete
组合模式
-
定义:将对象组合成树状的层次结构模式。
-
举例: 店购物车的实现。
-
优点:
- 客户端代码可以一致地处理单个对象和组合对象,简化了客户端代码
- 容易在组合体内加入新的对象
-
缺点:
- 设计较为复杂。
- 不容易限制容器中的构件。
- 不容易用继承的方法来增加构件的新功能。
-
角色:
- 抽象构件角色
- 树叶构件角色
- 树枝构件角色
-
分类:透明方式,安全方式。
-
En:component , leaf , composite
行为型模式
概述:用于描述程序在运行时复杂的流程控制,满足合成复用原则。
模板方法模式
-
定义:定义了操作中的算法骨架,将算法的一些步骤(每个子类中都会用到的)延迟到子类中。
-
举例:点单——吃饭(父类的抽象方法)——结账
-
优点:
- 封装了不变部分,扩展可变部分。
- 在父类中提取了公共的部分代码,便于代码的复用。
- 部分方法是由子类实现。
-
缺点:
-
每个不同的实现都需要重新定义一个子类。
-
父类中的抽象方法由子类实现,子类执行的结果会影响父类的结果(代码的阅读难度增加)。
-
-
角色:
-
抽象类:
- 抽象方法:父类声明,子类实现。
- 具体方法:父类已实现,子类继承或重写。
- 钩子方法:父类已实现,用于判断的逻辑方法和需要子类重写的空方法。
-
具体子类:
实现抽象和钩子方法。
-
策略模式
-
定义:将定义的每个算法封装起来,使他们可以相互替换,且算法的变换不影响用户的使用。
-
举例:客户点单大闸蟹,厨房做大闸蟹时两种算法:清蒸 和 红烧
-
优点:
- 可以避免使用多重条件语句。
- 提供可重用的算法族,避免重复的代码。
- 可以提供相同行为的不同实现,客户可以根据不同时间/空间选择不同的策略。
- 提供了对开闭原则的完美支持。
- 把算法的使用放到环境类中,而算法的实现移到具体策略类中,实现分离。
-
缺点:
- 客户端必须理解所有策略算法的区别,适当时选择恰当的算法类。
- 策略模式造成很多的策略类。
-
角色:
-
抽象策略类
-
具体策略类
-
环境类
-
命令模式
-
定义:将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。
-
举例:饭店将点餐信息(命令)告诉服务员(调用者),服务员将命令传递给厨师(执行者)。
-
优点:
- 降低系统的耦合度(厨师与服务员解耦)。
- 增加或删除命令非常方便。
- 可以实现宏命令(多个命令装配成一个组合命令)。
- 方便实现Undo和Redo操作。
-
缺点:可能产生大量具体命令类,增加系统的复杂性。
-
角色:
- 抽象命令类角色
- 具体命令角色
- 实现者/接受者角色
- 调用者/请求者角色
职责链模式
-
定义:避免请求发送者与多个请求处理着耦合在一起,将所有请求的处理者通过前一对象记住其下一对象的引用而形成一条链。
-
举例: 请假的过程 :学生→班主任 → 辅导员 →院主任→ 教务处 有一个对象处理即可,对象无权限处理传递到下一级
-
优点:
- 降低对象间的耦合度
- 增强了系统的扩展性
- 增强了给对象指派职责的灵活性
- 简化了对象间的链接
- 责任分担
-
缺点:
- 不能保证每个请求一定被处理
- 对于较长的链,请求的处理可能涉及多个处理对象,系统性能受一定的影响。
- 职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,导致系统出错,循环调用。
-
角色:
- 抽象处理者
- 具体处理者
- 客户类
状态模式
-
定义:对有状态的对象,把复杂的”判断逻辑“提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。
-
举例:由风扇的转速把风扇的档位进行划分。
-
优点:
- 将与特定状态相关的行为局部化到一个状态,并且将不同状态的行为分割开。
- 减少对象间的相互依赖。
- 有利于程序的扩展。
-
缺点:
- 会增加系统的类和对象的个数。
- 结构和实现比较复杂,使用不当会造成程序结构和代码的混乱。
-
角色:
- 环境角色(维持当前状态)
- 抽象状态角色
- 具体状态角色
-
En: Context,state,concrete,enviroment
观察者模式
-
定义:多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
-
举例:上课程序检测到上课时间到,打铃通知上课。
-
优点:
- 降低了目标与观察者之间的耦合关系。
- 目标与观察者之间建立了一套触发机制。
-
缺点:
- 目标与观察者之间的依赖问题并没有完全解除,可能出现循环引用。
- 观察者对象很多时,通知的发布会花费很多时间,降低效率。
-
角色:
- 抽象主题角色
- 具体主题角色
- 抽象观察者角色
- 具体观察者角色
-
En:subject,concrete,observer
中介者模式
- 定义:用一个中介对象来封装一系列对象之间的交互,使原有对象之间的耦合松散,并可以独立的改变他们之间的交互。
- 举例:Like Demeter
- 优点:
- 降低耦合度
- 将对象间一对多关联变成一对一
- 缺点:同事类太多时,难以维护
- 角色:
- 抽象中介者角色
- 具体中介者角色
- 抽象同事类角色
- 具体同事类角色
- En:concrete ,mediator ,colluage
迭代器模式
-
定义:提供一种方法来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。
-
举例:旅行软件展示一个地区的所有旅游景点(java中提供的iterator)。
-
优点:
- 访问一个聚合对象的内部无需暴露它的内部表示。
- 遍历任务交由迭代器完成。
- 支持以不同的方式遍历一个集合,可以自定义。
- 增加新的聚合类无须修改原有的代码。
- 封装性良好,为聚合结构提供了一个统一的接口。
-
缺点:增加了类的个数,增加了系统的复杂性。
-
角色:
- 抽象聚合角色
- 具体聚合角色
- 抽象迭代器角色
- 具体迭代器角色
-
En:aggregate (聚合),iterator
访问者模式
- 定义:将数据结构中的各元素的操作分离出来封装成独立的类,在不改变集合元素的前提下,为数据结构中每个元素提供多种访问方式。
- 举例:不同的访问着对类的处理不同,生成的产品也不同(比如自己和厨师同样拿一条鱼做饭)
- 优点:
- 扩展性好。
- 复用性好。
- 灵活性好。
- 符合单一职责原则。
- 缺点:
- 增加新的元素类很困难。
- 破坏了封装。对访问者公布了具体元素细节。
- 违反了依赖倒置原则,依赖了具体类而没有依赖抽象类。
- 角色:
- 抽象访问者角色
- 具体访问者角色
- 抽象元素角色
- 具体元素角色
- 对象结构
- En:Visitor,element
备忘录模式
- 定义:不破坏封装性的前提下,获取保存一个对象的内部状态,以便以后需要的时候恢复。
- 举例:书中提供了一个相亲游戏,从四大美女中选择来做自己的爱人(
我觉得行,你觉得呢) - 优点:
- 提供了一种可以恢复状态的机制。
- 实现了内部状态的封装。
- 简化了发起人类。
- 缺点:资源消耗大。
- 角色:
- 发起人角色。
- 备忘录角色。
- 管理者角色。
- En:Originator,memento,caretaker
解释器模式
-
定义:给分析对象定义一个语言,并定义该语言的文法表示,设计一个解析器来解释语言中的句子。
-
举例:我觉得这个可以用 java—语法—API来表示,或者 English—Grammar—dictionary
-
优点:
- 扩展性好
- 容易实现
-
缺点:
- 执行效率较低(通过例子就很容易理解了)
- 引起类膨胀,每条规则都应至少定义一个类,导致系统难以管理和维护
-
角色:
- 抽象表达式角色
- 终结符表达式
- 非终结符表达式
- 环境角色
- 客户端
-
En:Expression, terminal, noterminal , context , client
文章知识点与官方知识档案匹配,可进一步学习相关知识Java技能树首页概览91956 人正在系统学习中
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!