一文总结设计模式
前言
- 看了很多寓教于学写设计模式的,看的有点头疼,注意力全都在故事上了,满脑子都是鸭子,餐厅之类,还有一堆和设计模式不相关的话,翻书都翻的挺累的。
- 这里我整理了下23种设计模式,
没什么多余的话,代码演示,简单粗暴
,借鉴的地方都附上了参考链接(做个优秀的搬运工),没附上的是自己总结的。 - 借鉴的例子代码,基本都做了一些精简,
如果相关例子写的有什么不准确,麻烦在评论里面指出来,最好附上代码,我会尽快修改文章中的相关实例
。 - 23种设计模式,一文呈现,方便大家和自己查询,也方便自己随时修改,请配合文章旁边的大纲食用。
总述
7种面向对象设计原则
设计原则名称 | 定 义 |
---|---|
单一职责原则(Single Responsibility Principle, SRP) | 一个类只负责一个功能领域中的相应职责 |
开闭原则(Open-Closed Principle, OCP) | 软件实体应对扩展开放,而对修改关闭 |
里氏代换原则(Liskov Substitution Principle, LSP) | 所有引用基类对象的地方能够透明地使用其子类的对象 |
迪米特法则(Law of Demeter, LoD) | 一个软件实体应当尽可能少地与其他实体发生相互作用 |
接口隔离原则(Interface Segregation Principle, ISP) | 使用多个专门的接口,而不使用单一的总接口 |
依赖倒转原则(Dependence Inversion Principle, DIP) | 抽象不应该依赖于细节,细节应该依赖于抽象 |
合成复用原则(Composite Reuse Principle, CRP) | 尽量使用对象组合,而不是继承来达到复用的目的 |
-
设计模式可分为创建型(Creational),结构型(Structural)和行为型(Behavioral)三种
- 创建型模式主要用于描述如何创建对象(5种)
- 结构型模式主要用于描述如何实现类或对象的组合(7种)
- 行为型模式主要用于描述类或对象怎样交互以及怎样分配职责(11种)
-
图解:花了我蛮多精力画的,有需要到的,可以参照参照
- 图片是高清图,下载后查看,体验更好
原则简述
1. 单一职责原则
定义:一个类只有一个引起它变化的原因。
理解:对功能进行分类,代码进行解耦,一个类只管一件事
举例:就比如一个网络请求框架大体分为:请求类,缓存类,配置类,不能把这3个混在一起,必须分为3个类去实现不同的功能。
2.开闭原则
定义:一个实体(类、函数、模块等)应该对外扩展开放,对内修改关闭
理解:每次发生变化时,要通过新增代码来增强现有类型的行为,而不是修改原有代码。
举例:就比如在软件的生命周期内,因为产品迭代,软件升级维护等原因,需要对原有代码进行修改时,可能会给原有代码引入错误,也可能使得我们对整个功能不得不进行重构,并且需要对原有代码进行重新测试,这样的话,对开发周期影响很大,所以开闭原则刚好解决这个问题。
3. 里氏替换原则
定义:继承必须确保父类所拥有的性质在子类中仍然成立。
理解:在继承类时,除了扩展一些新的功能之外,尽量不要删除或者修改对父类方法的引用,也尽量不要重写父类的方法。
举例:你看啊,就比如Object有个方法,叫equals,如果不遵守里氏代替原则,它的子类重写了equals这个方法,并且返回了个null,这个子类的下一个继承者也会返回null,那么,在不同开发人员开发时,可能考虑不到这个问题,那么就可能导致程序崩溃。
4.迪米特法则
定义:一个模块或对象应尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立,这样当一个模块修改时,影响的模块就会越少,扩展起来更加容易。
理解:一个对象应该对其他对象有最少的了解;一个类应该对自己需要耦合或调用的类知道得最少,类的内部如何实现、如何复杂都与调用者或者依赖者没关系,调用者或者依赖者只需要知道他需要的方法即可,其他的一概不关心。类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另一个类的影响也越大。
举例:一般在使用框架的时候,框架的开发者会抽出一个类供外部调用,而这个主要的类像是一个中介一样去调用框架里面的其他类,恰恰框架里面其他类一般都是不可访问(调用)的,这个框架就遵守了迪米特原则,其他开发人员只关心调用的方法,并不需要关心功能具体如何实现。
5.接口隔离原则
定义:使用多个专门功能的接口,而不是使用单一的总接口
理解:在定义接口方法时应该合理化,尽量追求简单最小,避免接口臃肿
举例:在实际开发中,往往为了节省时间,可能会将多个功能的方法抽成一个接口,其实这设计理念不正确的,这样会使接口处于臃肿的状态,这时就需要合理的拆分接口中的方法,另外抽取成一个独立的接口,避免原有的接口臃肿导致代码理解困难。
6.依赖倒置原则
定义:细节应该依赖于抽象,而抽象不应该依赖于细节
理解:高层模块不依赖低层次模块的细节,不依赖具体的类,而是依赖于接口
举例:比如说我们写一个网络框架,为了满足不同开发者的需求,即能使用高效的OkHttp框架,也可以使用原生的API。那么是如何进行切换的呢,这个时候需要面向接口编程思想了,把一些网络请求的方法封装成一个接口,然后分别创建OkHttp和原生API的接口实现类,当然也可以扩展其他网络框架的应用。
7.合成复用原则
定义:尽量使用对象组合,而不是继承来达到复用的目的。
理解:它要求在软件复用时,要尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。
举例:相当于我们开发软件,一个模块的构造,就像搭积木一样,通过组合的形式,完成整体的构建;现在声明式UI框架,对于这种思想比较贯彻。
参考
- https://blog.csdn.net/lovelion/article/details/7536532
- https://juejin.im/post/5d8861f45188250970131f41
创建型模式
单例模式
饿汉模式
- 在未使用该类的时候,也会实例化对象,会造成资源浪费,除非在一定会用到该种类的场景,否则不建议使用。
懒汉模式
- 使用volatile修饰,可禁止重排,在多线程场景也能保证正常运行;只会在使用的时候,才会去实例化对象,一般场景都可以使用。
静态内部类模式
- 饿汉模式升级版,解决了资源浪费问题,同时也能保证线程安全;只有在内部类被加载的时候,才会去实例化对象;相对于懒汉模式,静态内部类的方式避免了排队进同步代码块做null的判断,性能优于懒汉模式,推荐使用。
枚举模式
- 推荐:effective java中最佳的单例实现模式就是枚举模式,JVM来帮我们保证线程安全和单一实例,在反射和序列化的场景中,仍能保证单一实例。
参考
- https://blog.csdn.net/zhangerqing/article/details/8194653
- https://www.cnblogs.com/happy4java/p/11206105.html
工厂模式
简单工厂模式
- 以手机为例来说明
- 定义一个接口,开机的时候,手机界面上,会显示该手机系统
- 实现一个Android手机类,实现Phone接口,返回“Android”手机系统
- 实现一个IOS手机类
- 创建手机工厂管理类
- 使用
- 结果显示:Android IOS
工厂模式
- 工厂模式区别与简单工厂模式的是,工厂模式不是用一个统一的工厂类来管理所有对象,而是每一个对象都有不同的工厂相对应。
- 继续以手机为例说明,需要针对Android和IOS手机这俩个实现类,去构建不同的工厂,这边用接口去规范不同的工厂类
- 手机工厂接口
- 创建Android手机工厂
- 创建IOS手机工厂
- 使用
- 结果显示:Android IOS
抽象工厂模式
- 抽象模式是对工厂模式的进一步优化,工厂类不单单只能创建一个对象,而是能创建一组对象。
- 在这里,大家可能存在一个疑惑,简单工厂不也是创建一组对象吗?是的,在这点上,俩者是非常非常相似的,区别在于:简单工厂模式是外部传值进去,以获取不同对象;抽象工厂模式直接通过方法获取对象,不需要传值。
- 继续以手机为例说明,咱们用俩种方式来看看,中间工厂类来创建一组对象
1、第一种方式
- 定义一个接口,这个接口里面是获取一系列的手机系统的实例
- 实现抽象工厂的这个接口
- 使用
- 结果显示:Android IOS
2、第二种方式
- 在这里思考下,抽象工厂模式,是在工厂类里面创建一组对象,与外层交互,不是通过关键字去返回相应对象,而是通过某个共性方法去返回“符合条件的实例”。
- 这里假设一个场景:我们定义的手机对象,其中的开机功能,只在对应的手机上才会起作用(Android手机想开机,只能使用Android手机类中的开机方法,IOS也是如此),在这里,假设此款手机是Android手机。
- 此处,我们不定义接口,直接创建抽象工厂类。
- 使用
- 结果显示:IOS
建造者模式
说明
- 在Computer 中创建一个静态内部类 Builder,然后将Computer 中的参数都复制到Builder类中。
- 在Computer中创建一个private的构造函数,参数为Builder类型
- 在Builder中创建一个public的构造函数,参数为Computer中必填的那些参数,cpu 和ram。
- 在Builder中创建设置函数,对Computer中那些可选参数进行赋值,返回值为Builder类型的实例
- 在Builder中创建一个build()方法,在其中构建Computer的实例并返回
实现
使用
参考
原型模式
定义
- 原型模式(Prototype Pattern):使用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。原型模式是一种对象创建型模式。
实现
参考
结构型模式
适配器模式
类适配
- 有一个已存在的将被适配的类
- 定义一个目标接口
- 怎么才可以在目标接口中的 request() 调用 Adaptee 的 adapteeRequest() 方法呢?
- 通过一个适配器类,实现 Target 接口,同时继承了 Adaptee 类,然后在实现的 request() 方法中调用父类的 adapteeRequest() 即可实现
- 测试一下
- 输出
对象适配
- 电源适配器:定义输出交流电接口,输出220V交流电类
- 适配器接口,outputDC5V() 方法则用于将输入的电压变换为 5V 后输出
- 实现电源适配器
- 使用
- 输出
接口适配
- 在实际开发中,经常会遇到接口中定义了太多的方法,以致于有时我们在一些实现类中并不是都需要
- 抽象类Wrapper:
- 可选择的去实现,我们想要的方法
参考
- https://juejin.im/post/5ba28986f265da0abc2b6084#heading-7
- https://blog.csdn.net/zhangerqing/article/details/8239539
装饰模式
定义
- 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。
实现
- 创建一个接口
- 创建俩个实现类
- 创建实现了 Shape 接口的抽象装饰类
- 测试
- 结果
参考
代理模式
定义
- 在代理模式(Proxy Pattern)中,一个类代表另一个类的功能。这种类型的设计模式属于结构型模式;在代理模式中,我们创建具有现有对象的对象,以便向外界提供功能接口。
- 其实每个模式名称就表明了该模式的作用,代理模式就是多一个代理类出来,替原对象进行一些操作,比如我们在租房子的时候回去找中介,为什么呢?因为你对该地区房屋的信息掌握的不够全面,希望找一个更熟悉的人去帮你做,此处的代理就是这个意思。再如我们有的时候打官司,我们需要请律师,因为律师在法律方面有专长,可以替我们进行操作,表达我们的想法。
实现
- 创建一个接口
- 实现
- 创建一个代理类
- 测试
- 结果
参考
外观模式
定义
- 外观模式(Facade Pattern)隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口。这种类型的设计模式属于结构型模式,它向现有的系统添加一个接口,来隐藏系统的复杂性
- 这种模式涉及到一个单一的类,该类提供了客户端请求的简化方法和对现有系统类方法的委托调用
- 外观模式就是将他们的关系放在一个Facade类中,降低了类类之间的耦合度
实现
- 实现类
- Facade类
- 测试
- 结果
参考
桥接模式
定义
- 桥接(Bridge)是用于把抽象化与实现化解耦,使得二者可以独立变化。这种类型的设计模式属于结构型模式,它通过提供抽象化和实现化之间的桥接结构,来实现二者的解耦。
- 这种模式涉及到一个作为桥接的接口,使得实体类的功能独立于接口实现类。这两种类型的类可被结构化改变而互不影响。
- 桥接模式就是把事物和其具体实现分开,使他们可以各自独立的变化。桥接的用意是:将抽象化与实现化解耦,使得二者可以独立变化,像我们常用的JDBC桥DriverManager一样,JDBC进行连接数据库的时候,在各个数据库之间进行切换,基本不需要动太多的代码,甚至丝毫不用动,原因就是JDBC提供统一接口,每个数据库提供各自的实现,用一个叫做数据库驱动的程序来桥接就行了。
- 总的来说:实现接口作为一个维度,继承抽象类作为一个维度,俩者可以随意组合
实现
第一个维度
- 创建桥接实现接口
- 创建实现了 DrawAPI 接口的实体桥接实现类
第二个维度
- 使用 DrawAPI 接口创建抽象类 Shape。
- 实现了 Shape 接口的实体类
- 测试
- 结果
组合模式
定义
- 组合模式(Composite Pattern),又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。这种类型的设计模式属于结构型模式,它创建了对象组的树形结构。
- 这种模式创建了一个包含自己对象组的类。该类提供了修改相同对象组的方式。
实现
- 抽象构件
- UnsupportedOperationException 是为了 Leaf 在继承之后不用重写该方法,因为这些方法是 Composite 对象需要重写的,而单个对象不需要。
- Leaf 类:
- 组合类:
- 调用:
- 运行结果:
参考
享元模式
定义
- 享元模式(Flyweight Pattern)主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结构的方式。
- 享元模式尝试重用现有的同类对象,如果未找到匹配的对象,则创建新对象。
典型享元模式
- 典型享元类(享元:Flyweight,w不是大写)
- 典型享元工厂类
通用写法
- 解决某些场景频繁生成实例问题;使用泛型,节省写判断逻辑
连接池的实现
- 数据库连接池
- 通过连接池的管理,实现了数据库连接的共享,不需要每一次都重新创建连接,节省了数据库重新创建的开销,提升了系统的性能!
参考
- https://juejin.im/post/5ba9ff4b6fb9a05d0f16df6a
- https://blog.csdn.net/zhangerqing/article/details/8239539
行为型模式
思考
策略模式和状态模式
相同点
- 两者通过将行为和状态拆分成一系列小的组件,由条件和状态进行功能更替,这样符合开闭原则,便于扩展。此外均可作为if else或者分支的替换方案;支持的最大行为和状态均有限;
不同点
- 策略模式中,类的功能是根据当前条件主动更改;
- 状态模式中,类的功能是被动由当前状态更改;
- 策略模式中每个行为或算法之间没有关联;
- 状态模式中的状态之间有关联,并且状态本身控制着状态转移;
解释器模式
定义
- 解释器模式(Interpreter Pattern)提供了评估语言的语法或表达式的方式,它属于行为型模式。这种模式实现了一个表达式接口,该接口解释一个特定的上下文。这种模式被用在 SQL 解析、符号处理引擎等。
实现
- 创建一个表达式接口。
- 创建实现了上述接口的实体类。
- Test 使用 Expression 类来创建规则,并解析它们。
- 结果
参考
状态模式
定义
- 在状态模式(State Pattern)中,类的行为是基于它的状态改变的;我们创建表示各种状态的对象和一个行为随着状态对象改变而改变的 context 对象。
实现
- 创建一个接口。
- 俩个状态实现类.
- 创建 Context 类。
- 测试
- 结果
参考
策略模式
定义
- 在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。
- 在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。
- 策略模式定义了一系列算法,并将每个算法封装起来,使他们可以相互替换,且算法的变化不会影响到使用算法的客户.
实现
- 策略接口,定义策略执行接口
- 具体策略类,实现策略接口,提供具体算法
- 测试
- 测试main方法中,我们先使用"加法策略(算法)",然后调用calculate(1,2)方法得到结果3。然后动态替换策略为"减法策略(算法)",再次调用calculate(1,2)得到结果-1。
参考
观察者模式
定义
- 当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知依赖它的对象。
实现
-
抽象观察者接口(Observer)
- 为所有的具体观察者定义一个接口,在得到主题通知时更新自己。
-
观察者接口实现类
- 实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题状态保持一致。
-
主题类
- 将有关状态存入具体观察者对象;当具体主题内部状态放生改变时,通知所有注册过的观察者。
- 测试
- 日志打印
中介者模式
定义
- 中介者模式(Mediator Pattern)是用来降低多个对象和类之间的通信复杂性。这种模式提供了一个中介类,该类通常处理不同类之间的通信,并支持松耦合,使代码易于维护。
实现
- 统一实现的行为
- 创建 user 类。
- 创建中介类 - 负责传话
- 测试
- 结果
备忘录模式
定义
- 备忘录模式(Memento Pattern)保存一个对象的某个状态,以便在适当的时候恢复对象。
实现
- Original类是原始类,里面有需要保存的属性value及创建一个备忘录类,用来保存value值。Memento类是备忘录类,Storage类是存储备忘录的类,持有Memento类的实例
- 测试
- 结果
参考
命令模式
定义
- 命令模式(Command Pattern)是一种数据驱动的设计模式。请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。
实现
- 命令接口
- 创建执行请求命令的实体类。
- 创建命令调用类。
- 测试
- 结果
参考
责任链模式
定义
-
责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。
责任链会将特定行为转换为被称作处理者的独立对象。 每个检查步骤都可被抽取为仅有单个方法的类, 并执行检查操作。 请求及其数据则会被作为参数传递给该方法。
该模式建议你将这些处理者连成一条链。 链上的每个处理者都有一个成员变量来保存对于下一处理者的引用。 除了处理请求外, 处理者还负责沿着链传递请求。 请求会在链上移动, 直至所有处理者都有机会对其进行处理。
最重要的是: 处理者可以决定不再沿着链传递请求, 这可高效地取消所有后续处理步骤。
实现
- 创建抽象的记录器类
- 创建扩展了该记录器类的实体类
- 测试
- 结果
参考
访问者模式
定义
- 在访问者模式(Visitor Pattern)中,我们使用了一个访问者类,它改变了元素类的执行算法。通过这种方式,元素的执行算法可以随着访问者改变而改变。这种类型的设计模式属于行为型模式。根据模式,元素对象已接受访问者对象,这样访问者对象就可以处理元素对象上的操作。
实现
- Visitor类,存放要访问的对象
- Subject类,accept方法,接受将要访问它的对象,getSubject()获取将要被访问的属性
- 测试
- 结果:visit the subject:love
参考
迭代器模式
定义
- 迭代器模式(Iterator Pattern)是 Java 和 .Net 编程环境中非常常用的设计模式。这种模式用于顺序访问集合对象的元素,不需要知道集合对象的底层表示。
实现
- 创建接口
- 创建实现了 Container 接口的实体类。该类有实现了 Iterator 接口的内部类 NameIterator。
- 测试
- 结果
参考
模板模式
定义
- 在模板模式(Template Pattern)中,一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。
- 超类中定义了一个算法的框架, 允许子类在不修改结构的情况下重写算法的特定步骤
实现
- 创建一个抽象类
- 实现类
- 测试
- 结果
参考
最后
- 文章地址:study-notes
- 提供良好的搬运格式,搬运使用时,请附上引用出处
__EOF__

本文链接:https://www.cnblogs.com/xdd666/p/14025132.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
· PowerShell开发游戏 · 打蜜蜂
· 凌晨三点救火实录:Java内存泄漏的七个神坑,你至少踩过三个!