[设计模式] 常见的设计模式
文章目录
- 设计模式的 6 大设计原则
- 设计模式的三大分类
- 常见的设计模式有哪几种
- 1. 单例模式:保证一个类仅有一个实例,并提供一个访问它的全局访问点。(连接池)
- 1. 饿汉式
- 2. 懒汉式
- 3. 双重检测
- 2. 工厂模式
- 3. 观察者模式
- ● 推模型
- ● 拉模型
- 4. 装饰模式
- 5. 建造者模式
- 6. 代理模式
- 7. 策略模式
设计模式的 6 大设计原则
- 单一职责原则:就一个类而言,应该仅有一个引起它变化的原因。
- 开放封闭原则:软件实体可以扩展,但是不可修改。即面对需求,对程序的改动可以通过增加代码来完成,但是不能改动现有的代码。
- 里氏代换原则:一个软件实体如果使用的是一个基类,那么一定适用于其派生类。即在软件中,把基类替换成派生类,程序的行为没有变化。
- 依赖倒转原则:抽象不应该依赖细节,细节应该依赖抽象。即针对接口编程,不要针对实现编程。
- 迪米特原则:如果两个类不直接通信,那么这两个类就不应当发生直接的相互作用。如果一个类需要调用另一个类的某个方法的话,可以通过第三个类转发这个调用。
- 接口隔离原则:每个接口中不存在派生类用不到却必须实现的方法,如果不然,就要将接口拆分,使用多个隔离的接口。
设计模式的三大分类
- 创造型模式:单例模式、工厂模式、建造者模式、原型模式 (4)
- 结构型模式:适配器模式、桥接模式、外观模式、组合模式、装饰模式、享元模式、代理模式 (7)
- 行为型模式:责任链模式、命令模式、解释器模式、迭代器模式、中介者模式、备忘录模式、观察者模式、状态模式、策略模式、模板方法模式、访问者模式 (12)
注意:简单工厂模式 违背了六大原则中的开发-封闭原则,故而不属于23种GOF设计模式之一 也叫静态工厂方法模式
常见的设计模式有哪几种
1. 单例模式:保证一个类仅有一个实例,并提供一个访问它的全局访问点。(连接池)
单例模式的实现需要三个必要的条件:
- 单例类的构造函数必须是私有的,这样才能将类的创建权控制在类的内部,从而使得类的外部不能创建类的实例。
- 单例类通过一个私有的静态变量来存储其唯一实例。
- 单例类通过提供一个公开的静态方法,使得外部使用者可以访问类的唯一实例。
另外,实现单例类时,还需要考虑三个问题:
- 创建单例对象时,是否线程安全。
- 单例对象的创建,是否延时加载。
- 获取单例对象时,是否需要加锁(锁会导致低性能)。
下面介绍五种实现单例模式的方式。
1. 饿汉式
饿汉式的单例实现比较简单,其在类加载的时候,静态实例 instance 就已创建并初始化好了。
public class Singleton { private static final Singleton instance = new Singleton();private Singleton () {}public static Singleton getInstance() {return instance;}
}
饿汉式单例优缺点:
- 优点:
- 单例对象的创建是线程安全的;
- 获取单例对象时不需要加锁。
- 缺点:单例对象的创建,不是延时加载。
一般认为延时加载可以节省内存资源。但是延时加载是不是真正的好,要看实际的应用场景,而不一定所有的应用场景都需要延时加载。
2. 懒汉式
与饿汉式对应的是懒汉式,懒汉式为了支持延时加载,将对象的创建延迟到了获取对象的时候,但为了线程安全,不得不为获取对象的操作加锁,这就导致了低性能。
public class Singleton { private static final Singleton instance;private Singleton () {}public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; }
}
懒汉式单例优缺点:
- 优点:
- 对象的创建是线程安全的。
- 支持延时加载。
- 缺点:获取对象的操作被加上了锁,影响了并发度。
- 如果单例对象需要频繁使用,那这个缺点就是无法接受的。
- 如果单例对象不需要频繁使用,那这个缺点也无伤大雅。
3. 双重检测
饿汉式和懒汉式的单例都有缺点,双重检测的实现方式解决了这两者的缺点。
双重检测将懒汉式中的 synchronized 方法改成了 synchronized 代码块。
public class Singleton { private static Singleton instance;private Singleton () {}public static Singleton getInstance() {if (instance == null) {synchronized(Singleton.class) { // 注意这里是类级别的锁if (instance == null) { // 这里的检测避免多线程并发时多次创建对象instance = new Singleton();}}}return instance;}
}
双重检测单例优点:
- 对象的创建是线程安全的。
- 支持延时加载。
- 获取对象时不需要加锁。
2. 工厂模式
包括简单工厂模式、抽象工厂模式、工厂方法模式
- 简单工厂模式:主要用于创建对象。用一个工厂来根据输入的条件产生不同的类,然后根据不同类的虚函数得到不同的结果。
public class SimpleFactory {public static Product createProduct(String type) {if (type.equals("A")) {return new ProductA();} else if (type.equals("B")) {return new ProductB();} else {return null;}}
}public interface Product {void use();
}public class ProductA implements Product {@Overridepublic void use() {System.out.println("Using Product A");}
}public class ProductB implements Product {@Overridepublic void use() {System.out.println("Using Product B");}
}
b. 工厂方法模式:修正了简单工厂模式中不遵守开放封闭原则。把选择判断移到了客户端去实现,如果想添加新功能就不用修改原来的类,直接修改客户端即可。
public interface Factory {Product createProduct();
}public class ProductAFactory implements Factory {@Overridepublic Product createProduct() {return new ProductA();}
}public class ProductBFactory implements Factory {@Overridepublic Product createProduct() {return new ProductB();}
}
c. 抽象工厂模式:定义了一个创建一系列相关或相互依赖的接口,而无需指定他们的具体类。
public interface AbstractFactory {Product createProductA();Product createProductB();
}---public class AbstractProductFactory implements AbstractFactory {@Overridepublic Product createProductA() {return new ProductAFactory().createProduct();}@Overridepublic Product createProductB() {return new ProductBFactory().createProduct();}
}
public class Client {public static void main(String[] args) {AbstractFactory factory = new AbstractProductFactory();Product productA = factory.createProductA();Product productB = factory.createProductB();productA.use();productB.use();}
}
3. 观察者模式
定义了一种一对多的关系,让多个观察对象同时监听一个主题对象,主题对象发生变化时,会通知所有的观察者,使他们能够更新自己。(微信朋友圈动态通知、消息通知、邮件通知、广播通知、桌面程序的事件响应)
在观察者模式中,又分为推模型和拉模型两种方式。
● 推模型
主题对象向观察者推送主题的详细信息,不管观察者是否需要,推送的信息通常是主题对象的全部或部分数据。
● 拉模型
主题对象在通知观察者的时候,只传递少量信息。如果观察者需要更具体的信息,由观察者主动到主题对象中获取,相当于是观察者从主题对象中拉数据。一般这种模型的实现中,会把主题对象自身通过update()方法传递给观察者,这样在观察者需要获取数据的时候,就可以通过这个引用来获取了。
4. 装饰模式
动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成派生类更为灵活。(输入输出流)
文件流 -> 输入输出流 -> 缓冲池流 (层层包装,扩展功能)
BufferedReader in1 = new BufferedReader(new InputStreamReader(new FileInputStream(file)));//字符流DataInputStream in2 = new DataInputStream(new BufferedInputStream(new FileInputStream(file)));//字节流// DataInputStream-从数据流读取字节,并将它们装换为正确的基本类型值或字符串// BufferedInputStream-可以通过减少读写次数来提高输入和输出的速度
5. 建造者模式
建造者模式的目的是为了分离对象的属性与创建过程。
建造者模式是构造方法的一种替代方案,为什么需要建造者模式,我们可以想,假设有一个对象里面有20个属性:
● 属性1
● 属性2
● ...
● 属性20
对开发者来说这不是疯了,也就是说我要去使用这个对象,我得去了解每个属性的含义,然后在构造函数或者Setter中一个一个去指定。更加复杂的场景是,这些属性之间是有关联的,比如属性1=A,那么属性2只能等于B/C/D,这样对于开发者来说更是增加了学习成本,开源产品这样的一个对象相信不会有太多开发者去使用。
为了解决以上的痛点,建造者模式应运而生,对象中属性多,但是通常重要的只有几个,因此建造者模式会让开发者指定一些比较重要的属性或者让开发者指定某几个对象类型,然后让建造者去实现复杂的构建对象的过程,这就是对象的属性与创建分离。这样对于开发者而言隐藏了复杂的对象构建细节,降低了学习成本,同时提升了代码的可复用性。
@Data
public class CarBuilder {// 车型private String type;// 动力private String power;public Car build() {Assert.assertNotNull(type);Assert.assertNotNull(power);return new Car(this);}public CarBuilder type(String type) {this.type = type;return this;}public CarBuilder power(String power) {this.power = power;return this;}}
@Test
public void test() {Car car = new CarBuilder().power("动力一般").type("紧凑型车").build();System.out.println(JSON.toJSONString(car));
}
6. 代理模式
- 优点:代理可以协调调用方与被调用方,降低了系统的耦合度。根据代理类型和场景的不同,可以起到控制安全性、减小系统开销等作用。
- 缺点:增加了一层代理处理,增加了系统的复杂度,同时可能会降低系统的相应速度。
Aop 就是使用代理模式来实现的。
public interface Subject {void request();
}public class RealSubject implements Subject {@Overridepublic void request() {System.out.println("RealSubject handles the request");}
}---public class Proxy implements Subject {private RealSubject realSubject;@Overridepublic void request() {if (realSubject == null) {realSubject = new RealSubject();}System.out.println("Proxy handles the request");// before aoprealSubject.request();// post aop}
}public class Client {public static void main(String[] args) {Proxy proxy = new Proxy();proxy.request();}
}
7. 策略模式
优缺点
- 优点:策略模式提供了对“开闭原则”的完美支持,用户可以在不修改原有系统的基础上选择算法或行为。干掉复杂难看的if-else。
- 缺点:调用时,必须提前知道都有哪些策略模式类,才能自行决定当前场景该使用何种策略。
public interface Strategy {void execute();
}public class ConcreteStrategyA implements Strategy {@Overridepublic void execute() {System.out.println("Executing strategy A");}
}public class ConcreteStrategyB implements Strategy {@Overridepublic void execute() {System.out.println("Executing strategy B");}
}public class Context {private Strategy strategy;public Context(Strategy strategy) {this.strategy = strategy;}public void executeStrategy() {strategy.execute();}
}public class Client {public static void main(String[] args) {Strategy strategyA = new ConcreteStrategyA();Strategy strategyB = new ConcreteStrategyB();Context context = new Context(strategyA);context.executeStrategy();context = new Context(strategyB);context.executeStrategy();}
}
相关文章:

[设计模式] 常见的设计模式
文章目录 设计模式的 6 大设计原则设计模式的三大分类常见的设计模式有哪几种1. 单例模式:保证一个类仅有一个实例,并提供一个访问它的全局访问点。(连接池)1. 饿汉式2. 懒汉式3. 双重检测 2. 工厂模式3. 观察者模式● 推模型● 拉…...

报错解决:You may need an additional loader to handle the result of these loaders.
报错信息如下 vue 项目 Module parse failed: Unexpected token (1:9) File was processed with these loaders:* ./node_modules/vue/cli-plugin-babel/node_modules/cache-loader/dist/cjs.js* ./node_modules/babel-loader/lib/index.js* ./node_modules/eslint-loader/in…...

配置自动化部署Jenkins和Gitea
配置自动化部署 这里使用的是JenkinsGitea 如果不知道怎么安装Jenkins和Gitea可以参考下面文章 https://blog.csdn.net/weixin_46533577/article/details/134644144 我的另一篇文章 介绍 前端 先说下自己的情况,因为自己服务器原因,使用的服务器内…...

VSCODE+QEMU+WSL调试RISCV代码(SBI、kernel)
前言 最近在对RISC-V架构比较感兴趣,正好手头有《RISC-V体系结构编程与实践》的书籍,就打算跟随笨叔将这块的知识学习起来,最开始当然是需要搭建一个基础的实验平台,本来笨叔是贴心的提供了VMare的环境,奈何天生叛逆的…...

二叉树(判断是否为对称二叉树)
题目(力扣): 观察题目,只需判断该二叉树是否对称。 判断二叉树是否对称,就可以换位去判断该二叉树的左子树和右子树是否对称。 这时就可以写一个辅助函数来方便判断。 该函数是判断两颗树是否镜像对称,这…...

STM32开发学习(地址映射)
LED灯代码: #define PERIPH_BASE ((unsigned int)0x40000000)#define AHB1PERIPH_BASE (PERIPH_BASE 0x00020000)#define GPIOF_BASE (AHB1PERIPH_BASE 0x1400)#define GPIOF_MODER *(unsigned int*)(GPIOF_BASE0x00) #define GPIOF_BSRR *(uns…...
证明E(X+Y) =E(X) + E(Y)
E(XY) E(X) E(Y)的成立是不需要X和Y相互独立的!!! 离散型随机变量 E ( X Y ) ∑ i 1 n ∑ j 1 m ( x i y j ) P { X x i , Y y j } ∑ i 1 n ∑ j 1 m x i P { X x i , Y y j } ∑ i 1 n ∑ j 1 m y j P { X x i , Y y j …...
ClickHouse入门手册1.0
1、数据类型 1.1 整数类型: ClickHouse中整型数据均为固定长度(可以设置长度参数,但是会被忽略),整型包括有符号整型和无符号整型。 有符号整型:Int8,Int16,Int32,Int64,Int128,Int256 无符号整型:UInt8,UInt16,UI…...

10个火爆的设计素材网站推荐
所谓聪明的女人没有米饭很难做饭,设计师也是如此。如何找到优秀的设计材料是每个设计师的痛点,国内材料网站收费,但也限制使用范围和期限,大多数外国设计网站不能打开或需要特殊互联网使用,有一定的安全风险。 作为一…...

SQL注入 - CTF常见题型
文章目录 题型一 ( 字符型注入 )题型二 ( 整数型注入 )题型三 ( 信息收集SQL注入)题型四 ( 万能密码登录 )题型五 ( 搜索型注入文件读写 )题型六 (…...

android keylayout键值适配
1、通过getevent打印查看当前keyevent数字对应事件和物理码 2、dumpsys input 查看输入事件对应的 KeyLayoutFile: /system/usr/keylayout/Vendor_6080_Product_8060.kl 3、通过物理码修改键值映射,修改/system/usr/keylayout/目录下的文件...

python读取excel自动化生成sql建表语句和java实体类字段
1、首先准备一个excel文件: idtypenameidint学号namestring姓名ageint年龄sexstring性别weightdecimal(20,4)体重scoredecimal(20,4)分数 2、直接生成java字段和注释: import pandas as pddf pd.read_excel(test.xlsx, sheet_nameSheet1)for i in ran…...
Unity求向量A在平面L上的投影向量
如题:求向量A在平面L上的投影向量(图左) 即求 其实等价于求向量,那在中,,所以只需要求即可 而就是在平面L的法向量的投影坐标,所以代码就是 /// <summary>/// 求向量A在平面B上的投影向量/// </summary>/// <para…...

人机交互2——任务型多轮对话的控制和生成
1.自然语言理解模块 2.对话管理模块 3.自然语言生成模块...

【数据结构】八大排序 (三)
目录 前言: 快速排序 快速排序非递归实现 快速排序特性总结 归并排序 归并排序的代码实现 归并排序的特性总结 计数排序 计数排序的代码实现 计数排序的特性总结 前言: 前文快速排序采用了递归实现,而递归会开辟函数栈帧࿰…...

Redis 命令处理过程
我们知道 Redis 是一个基于内存的高性能键值数据库, 它支持多种数据结构, 提供了丰富的命令, 可以用来实现缓存、消息队列、分布式锁等功能。 而在享受 Redis 带来的种种好处时, 是否曾好奇过 Redis 是如何处理我们发往它的命令的呢? 本文将以伪代码的形式简单分析…...

python爬虫进阶教程之如何正确的使用cookie
文章目录 前言一、获取cookie二、程序实现三、动态获取cookie四、其他关于Python爬虫技术储备一、Python所有方向的学习路线二、Python基础学习视频三、精品Python学习书籍四、Python工具包项目源码合集①Python工具包②Python实战案例③Python小游戏源码五、面试资料六、Pytho…...

【hacker送书第4期】推荐4本Java必读书籍(各送一本)
第4期图书推荐 Java从入门到精通(第7版)内容简介参与方式 项目驱动零基础学Java内容简介参与方式 深入理解Java高并发编程内容简介参与方式 Java编程讲义内容简介参与方式 Java从入门到精通(第7版) 内容简介 《Java从入门到精通&…...

[密码学]DES
先声明两个基本概念 代换(substitution),用别的元素代替当前元素。des的s-box遵循这一设计。 abc-->def 置换(permutation),只改变元素的排列顺序。des的p-box遵循这一设计。 abc-->bac DES最核心的算法就是…...

15个超级实用的Python操作,肯定有你意想不到的!
文章目录 1)映射代理(不可变字典)2)dict 对于类和对象是不同的3) any() 和 all()4) divmod()5) 使用格式化字符串轻松检查变量6) 我们可以将浮点数转换为比率7) 用globals()和locals()显示现有的全局/本地变量8) import() 函数9) …...
浏览器访问 AWS ECS 上部署的 Docker 容器(监听 80 端口)
✅ 一、ECS 服务配置 Dockerfile 确保监听 80 端口 EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]或 EXPOSE 80 CMD ["python3", "-m", "http.server", "80"]任务定义(Task Definition&…...

2025年能源电力系统与流体力学国际会议 (EPSFD 2025)
2025年能源电力系统与流体力学国际会议(EPSFD 2025)将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会,EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...

MongoDB学习和应用(高效的非关系型数据库)
一丶 MongoDB简介 对于社交类软件的功能,我们需要对它的功能特点进行分析: 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具: mysql:关系型数据库&am…...

CentOS下的分布式内存计算Spark环境部署
一、Spark 核心架构与应用场景 1.1 分布式计算引擎的核心优势 Spark 是基于内存的分布式计算框架,相比 MapReduce 具有以下核心优势: 内存计算:数据可常驻内存,迭代计算性能提升 10-100 倍(文档段落:3-79…...

NLP学习路线图(二十三):长短期记忆网络(LSTM)
在自然语言处理(NLP)领域,我们时刻面临着处理序列数据的核心挑战。无论是理解句子的结构、分析文本的情感,还是实现语言的翻译,都需要模型能够捕捉词语之间依时序产生的复杂依赖关系。传统的神经网络结构在处理这种序列依赖时显得力不从心,而循环神经网络(RNN) 曾被视为…...

智能仓储的未来:自动化、AI与数据分析如何重塑物流中心
当仓库学会“思考”,物流的终极形态正在诞生 想象这样的场景: 凌晨3点,某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径;AI视觉系统在0.1秒内扫描包裹信息;数字孪生平台正模拟次日峰值流量压力…...
Element Plus 表单(el-form)中关于正整数输入的校验规则
目录 1 单个正整数输入1.1 模板1.2 校验规则 2 两个正整数输入(联动)2.1 模板2.2 校验规则2.3 CSS 1 单个正整数输入 1.1 模板 <el-formref"formRef":model"formData":rules"formRules"label-width"150px"…...

HashMap中的put方法执行流程(流程图)
1 put操作整体流程 HashMap 的 put 操作是其最核心的功能之一。在 JDK 1.8 及以后版本中,其主要逻辑封装在 putVal 这个内部方法中。整个过程大致如下: 初始判断与哈希计算: 首先,putVal 方法会检查当前的 table(也就…...

JVM 内存结构 详解
内存结构 运行时数据区: Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器: 线程私有,程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 每个线程都有一个程序计数…...
现有的 Redis 分布式锁库(如 Redisson)提供了哪些便利?
现有的 Redis 分布式锁库(如 Redisson)相比于开发者自己基于 Redis 命令(如 SETNX, EXPIRE, DEL)手动实现分布式锁,提供了巨大的便利性和健壮性。主要体现在以下几个方面: 原子性保证 (Atomicity)ÿ…...