关于软件设计模式的理解
系列文章
关于时间复杂度o(1), o(n), o(logn), o(nlogn)的理解
关于HashMap的哈希碰撞、拉链法和key的哈希函数设计
关于JVM内存模型和堆内存模型的理解
关于代理模式的理解
关于Mysql基本概念的理解
关于软件设计模式的理解
文章目录
- 前言
- 一、软件设计模式遵循的六大原则
- 二、学习软件设计模式的意义
- 三、使用率最高的设计模式有哪几个?具体使用场景举例
- 1.单例模式(Singleton)
- 2.工厂模式(Factory)
- 3.观察者模式(Observer)
- 4.策略模式(Strategy)
前言
软件设计模式(Software Design Pattern),是一套被反复使用的、关于代码设计经验的总结。被用来解决一些不断重复发生的问题,是前辈们的代码设计经验的总结,具有一定的普遍性,可以反复使用。其目的是为了提高代码的可重用性、代码的可读性和代码的可靠性
一、软件设计模式遵循的六大原则
设计模式通常遵循的六大原则是:
开放封闭原则(Open/Closed Principle,OCP): 软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。这意味着在不修改现有代码逻辑的情况下,能够通过扩展来增加新的功能。
单一职责原则(Single Responsibility Principle,SRP): 一个类应该只负责一种类型的任务或职责。
里氏替换原则(Liskov Substitution Principle,LSP): 子类型必须能够替换掉它们的父类型,而不影响程序的正确性。
依赖倒置原则(Dependency Inversion Principle,DIP): 应该依赖于接口或抽象类,而不是具体的实现。
接口隔离原则(Interface Segregation Principle,ISP): 一个类不应该依赖它不需要的接口。
合成/聚合复用原则(Composition/Aggregation Reuse Principle,CARP): 应该优先使用对象组合或聚合,而不是继承来实现代码复用。
这些原则提供了指导,帮助开发人员设计出灵活、可维护、可扩展和易于理解的软件系统。虽然并不是每种设计模式都严格遵循这些原则,但设计模式通常是以这些原则为基础来提供解决特定问题的通用方案
二、学习软件设计模式的意义
设计模式的本质是面向对象设计原则的实际运用,是对类的封装性、继承性和多态性以及类的关联关系和组合关系的充分理解。正确使用设计模式具有以下优势。:
- 可以提高程序员的思维能力、编程能力和设计能力。
- 可以使程序设计更加标准化、代码编制更加工程化,使软件开发效率大大提高,从而缩短软件的开发周期。
- 可以使设计的代码可复用性高、可读性强、可靠性高、灵活性好、可维护性强。
三、使用率最高的设计模式有哪几个?具体使用场景举例
1.单例模式(Singleton)
场景举例: 当系统中需要确保一个类只有一个实例,并提供全局访问点时,通常使用单例模式。如数据库连接池、日志管理器等
代码案例:
步骤 1: 创建单例类
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;public class DatabaseConnection {// 数据库连接相关配置private static final String URL = "jdbc:mysql://localhost:3306/mydatabase";private static final String USERNAME = "username";private static final String PASSWORD = "password";// 私有静态变量,保存类的唯一实例private static DatabaseConnection instance;// 数据库连接对象private Connection connection;// 私有构造函数,防止外部直接创建对象private DatabaseConnection() {try {// 创建数据库连接connection = DriverManager.getConnection(URL, USERNAME, PASSWORD);} catch (SQLException e) {e.printStackTrace();}}// 公有静态方法,返回唯一实例public static DatabaseConnection getInstance() {if (instance == null) {// 确保线程安全synchronized (DatabaseConnection.class) {if (instance == null) {instance = new DatabaseConnection();}}}return instance;}// 获取数据库连接对象public Connection getConnection() {return connection;}
}
步骤 2: 使用单例类
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;public class Main {public static void main(String[] args) {// 获取数据库连接Connection connection = DatabaseConnection.getInstance().getConnection();// 执行数据库操作try {PreparedStatement statement = connection.prepareStatement("SELECT * FROM users");ResultSet resultSet = statement.executeQuery();while (resultSet.next()) {System.out.println("User ID: " + resultSet.getInt("id") + ", Name: " + resultSet.getString("name"));}resultSet.close();statement.close();} catch (SQLException e) {e.printStackTrace();}}
}
解释
单例模式的核心: 私有构造函数和一个返回实例的公有静态方法。
线程安全: 使用双重检查锁定确保在多线程环境下安全创建实例。
延迟初始化: 实例在类被加载时不会创建,在首次需要时才创建,节省资源。
数据库连接池: 这种方式也可以用于实现简单的数据库连接池,通过控制并发访问来管理连接数,提高数据库连接的效率和资源利用率。
这种实现方式在实际应用中非常有用,特别是在需要频繁访问数据库的情况下,可以减少连接的开销和管理成本。
2.工厂模式(Factory)
场景举例: 当需要创建多个具有相似功能的对象时,使用工厂模式可以将对象的创建逻辑封装在一个工厂类中,提高代码的灵活性和可维护性。如数据库驱动管理等
代码案例:
步骤 1: 创建接口
import java.sql.Connection;public interface DatabaseConnection {Connection getConnection();
}
步骤 2: 创建具体实现类
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;public class MySQLConnection implements DatabaseConnection {// MySQL数据库连接相关配置private static final String URL = "jdbc:mysql://localhost:3306/mydatabase";private static final String USERNAME = "username";private static final String PASSWORD = "password";@Overridepublic Connection getConnection() {Connection connection = null;try {// 创建MySQL数据库连接connection = DriverManager.getConnection(URL, USERNAME, PASSWORD);} catch (SQLException e) {e.printStackTrace();}return connection;}
}
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;public class PostgreSQLConnection implements DatabaseConnection {// PostgreSQL数据库连接相关配置private static final String URL = "jdbc:postgresql://localhost:5432/mydatabase";private static final String USERNAME = "username";private static final String PASSWORD = "password";@Overridepublic Connection getConnection() {Connection connection = null;try {// 创建PostgreSQL数据库连接connection = DriverManager.getConnection(URL, USERNAME, PASSWORD);} catch (SQLException e) {e.printStackTrace();}return connection;}
}
步骤 3: 创建工厂类
public class ConnectionFactory {public static DatabaseConnection getDatabaseConnection(String dbType) {if (dbType.equalsIgnoreCase("MySQL")) {return new MySQLConnection();} else if (dbType.equalsIgnoreCase("PostgreSQL")) {return new PostgreSQLConnection();}return null;}
}
步骤 4: 使用工厂模式获取数据库连接
import java.sql.Connection;public class Main {public static void main(String[] args) {// 获取MySQL数据库连接DatabaseConnection mysqlConnection = ConnectionFactory.getDatabaseConnection("MySQL");Connection mysqlConn = mysqlConnection.getConnection();// 获取PostgreSQL数据库连接DatabaseConnection postgresConnection = ConnectionFactory.getDatabaseConnection("PostgreSQL");Connection postgresConn = postgresConnection.getConnection();// 使用数据库连接执行操作...}
}
解释
工厂模式的核心: 根据条件动态创建对象,将创建逻辑封装在工厂类中,客户端无需关心具体的创建细节。
可扩展性: 当需要新增其他类型的数据库连接时,只需在工厂类中添加相应的创建逻辑即可,不需要修改客户端代码。
解耦: 客户端与具体数据库连接类之间解耦,通过工厂类进行统一管理,降低了代码的耦合度。
工厂模式能够很好地应对数据库驱动管理场景中的需求变化,使得代码更加灵活和可维护。
3.观察者模式(Observer)
场景举例: 当一个对象的状态发生改变时,需要通知其他相关对象,并自动更新它们的状态时,通常使用观察者模式。如消息订阅与发布系统等
代码案例:
步骤 1: 创建主题接口
首先,定义一个主题接口,用于注册、删除和通知观察者。
public interface Subject {void registerObserver(Observer observer);void removeObserver(Observer observer);void notifyObservers();
}
步骤 2: 创建观察者接口
然后,定义一个观察者接口,用于接收主题的通知。
public interface Observer {void update(String message);
}
步骤 3: 创建具体主题类
接下来,创建具体的主题类,实现主题接口,并维护观察者列表,以及在状态变化时通知观察者。
import java.util.ArrayList;
import java.util.List;public class MessageTopic implements Subject {private List<Observer> observers;private String message;public MessageTopic() {this.observers = new ArrayList<>();}@Overridepublic void registerObserver(Observer observer) {observers.add(observer);}@Overridepublic void removeObserver(Observer observer) {observers.remove(observer);}@Overridepublic void notifyObservers() {for (Observer observer : observers) {observer.update(message);}}public void setMessage(String message) {this.message = message;notifyObservers();}
}
步骤 4: 创建具体观察者类
然后,创建具体的观察者类,实现观察者接口,并在接收到通知时执行相应的操作。
public class MessageSubscriber implements Observer {private String name;public MessageSubscriber(String name) {this.name = name;}@Overridepublic void update(String message) {System.out.println(name + " received message: " + message);}
}
步骤 5: 使用观察者模式
最后,在应用程序中使用观察者模式。
public class Main {public static void main(String[] args) {// 创建主题MessageTopic topic = new MessageTopic();// 创建观察者Observer subscriber1 = new MessageSubscriber("Subscriber 1");Observer subscriber2 = new MessageSubscriber("Subscriber 2");// 注册观察者topic.registerObserver(subscriber1);topic.registerObserver(subscriber2);// 发布消息topic.setMessage("Hello, world!");}
}
解释
主题接口: 定义了注册、删除和通知观察者的方法。
观察者接口: 定义了观察者需要实现的更新方法。
具体主题类: 实现了主题接口,维护了观察者列表,并在状态变化时通知所有观察者。
具体观察者类: 实现了观察者接口,定义了接收通知时的具体行为。
使用观察者模式: 创建主题对象,创建观察者对象并注册到主题中,然后发布消息。所有注册的观察者都会接收到消息通知并执行相应的操作。
观察者模式非常适用于消息订阅与发布场景,可以实现松耦合的通信机制,让发布者和订阅者之间的关系更加灵活。
4.策略模式(Strategy)
场景举例: 当需要在运行时根据不同的情况选择算法或行为时,使用策略模式可以将不同的算法封装成不同的策略类,使得算法的变化独立于使用算法的客户。如支付系统中的支付策略等
代码案例:
步骤 1: 创建支付策略接口
首先,定义一个支付策略接口,用于定义支付的方法。
public interface PaymentStrategy {void pay(double amount);
}
步骤 2: 创建具体的支付策略类
然后,创建具体的支付策略类,实现支付策略接口,每个具体的支付策略类代表一种支付方式,例如信用卡支付、支付宝支付、微信支付等
public class CreditCardPayment implements PaymentStrategy {private String cardNumber;private String expiryDate;private String cvv;public CreditCardPayment(String cardNumber, String expiryDate, String cvv) {this.cardNumber = cardNumber;this.expiryDate = expiryDate;this.cvv = cvv;}@Overridepublic void pay(double amount) {System.out.println("Paid " + amount + " via credit card.");}
}public class AlipayPayment implements PaymentStrategy {private String account;public AlipayPayment(String account) {this.account = account;}@Overridepublic void pay(double amount) {System.out.println("Paid " + amount + " via Alipay.");}
}public class WechatPayment implements PaymentStrategy {private String account;public WechatPayment(String account) {this.account = account;}@Overridepublic void pay(double amount) {System.out.println("Paid " + amount + " via WeChat.");}
}
步骤 3: 创建上下文类
接下来,创建上下文类,用于持有具体的支付策略对象,并提供支付方法。
public class PaymentContext {private PaymentStrategy paymentStrategy;public PaymentContext(PaymentStrategy paymentStrategy) {this.paymentStrategy = paymentStrategy;}public void setPaymentStrategy(PaymentStrategy paymentStrategy) {this.paymentStrategy = paymentStrategy;}public void pay(double amount) {paymentStrategy.pay(amount);}
}
步骤 4: 使用策略模式
最后,在应用程序中使用策略模式进行支付。
public class Main {public static void main(String[] args) {// 创建支付上下文PaymentContext paymentContext = new PaymentContext(new CreditCardPayment("1234 5678 9012 3456", "12/24", "123"));// 进行支付paymentContext.pay(100.0);// 切换支付方式paymentContext.setPaymentStrategy(new AlipayPayment("example@example.com"));// 进行支付paymentContext.pay(200.0);}
}
解释
支付策略接口: 定义了支付的方法。
具体的支付策略类: 实现了支付策略接口,每个类代表一种支付方式,实现了具体的支付逻辑。
支付上下文类: 持有具体的支付策略对象,并提供支付方法,客户端通过上下文类进行支付。
使用策略模式: 创建支付上下文对象,根据需要设置不同的支付策略,然后进行支付操作。
策略模式能够有效地解耦客户端和具体的支付策略,使得支付系统更加灵活,易于扩展和维护。
相关文章:
关于软件设计模式的理解
系列文章 关于时间复杂度o(1), o(n), o(logn), o(nlogn)的理解 关于HashMap的哈希碰撞、拉链法和key的哈希函数设计 关于JVM内存模型和堆内存模型的理解 关于代理模式的理解 关于Mysql基本概念的理解 关于软件设计模式的理解 文章目录 前言一、软件设计模式遵循的六大原则…...
Java开发官方文档
Spring中文网 Spring Cloud中文网 Hutool工具类 Ant Design官方文档 遇见狂神说学习文档 若依后台管理系统测试环境 FineBI官方文档 vscode教程 新一代微服务全家桶AlibabaCloudSpringCloud实战 分布式任务调度平台XXL-JOB...

AI大模型探索之路-实战篇9:探究Agent智能数据分析平台的架构与功能
系列篇章💥 AI大模型探索之路-实战篇4:深入DB-GPT数据应用开发框架调研 AI大模型探索之路-实战篇5:探索Open Interpreter开放代码解释器调研 AI大模型探索之路-实战篇6:掌握Function Calling的详细流程 AI大模型探索之路-实战篇7…...
本地spark3.5(不整合hive) 集成paimon0.9
spark官网下载集成hadoop的spark包: spark-3.5.1-bin-hadoop3.... 解压后 环境变量配置 SPARK_HOME spark-defaults.conf 中增加一行配置(避免启动spark-sql报错hive元数据连不上): spark.sql.catalogImplementationhive 打开paimon官网: https://paimon.apache.org/docs/mas…...

Linux IO模型深度解析与实战应用
linux的5种IO模型 一、这里IO是什么 操作系统设有用户态与内核态,确保系统安全。应用程序默认在用户态运行,而执行如IO操作等底层任务时,需切换至内核态以高效执行。 服务器从网络接收的大致流程如下: 1、数据通过计算机网络来到了网卡 2、把网卡的数据读取到 socket 缓…...

软件系统开发标准流程文档(Word原件)
目的:规范系统开发流程,提高系统开发效率。 立项申请需求分析方案设计方案评审开发调整测试阶段系统培训试运行测试验收投入使用 所有文档过去进主页获取。 软件项目相关全套精华资料包获取方式①:点我获取 获取方式②:本文末个人…...

嵌入式进阶——外部中断(EXTI)
🎬 秋野酱:《个人主页》 🔥 个人专栏:《Java专栏》《Python专栏》 ⛺️心若有所向往,何惧道阻且长 文章目录 STC8H中断外部中断外部中断编写配置外部中断调用中断触发函数 外部中断测试测试外部中断0测试外部中断2、3或者4 PCB中断设计 STC8…...

flinkcdc 3.0 源码学习之客户端flink-cdc-cli模块
注意 : 本文章是基于flinkcdc 3.0 版本写的 我们在前面的文章已经提到过,flinkcdc3.0版本分为4层,API接口层,Connect链接层,Composer同步任务构建层,Runtime运行时层,这篇文章会对API接口层进行一个探索.探索一下flink-cdc-cli模块,看看是如何将一个yaml配置文件转换成一个任务…...

香橙派 AIpro开发体验:使用YOLOV8对USB摄像头画面进行目标检测
香橙派 AIpro开发体验:使用YOLOV8对USB摄像头画面进行目标检测 前言一、香橙派AIpro硬件准备二、连接香橙派AIpro1. 通过网线连接路由器和香橙派AIpro2. 通过wifi连接香橙派AIpro3. 使用vscode 通过ssh连接香橙派AIpro 三、USB摄像头测试1. 配置ipynb远程开发环境1.…...
Python中正则表达式详解
Python中正则表达式详解 引言 正则表达式是一种用于字符串搜索和操作的强大工具。它使用单个字符串来描述、匹配一系列符合某个句法规则的字符串。在Python中,正则表达式通过内置的re模块来实现,使得文本处理变得简洁而高效。 正则表达式基础 在深入…...
vue使用EventBus进行跨组件通信
Vue中的EventBus,又称为事件总线,是一种常用的通信模式,它允许在Vue应用程序的不同组件之间进行松耦合的通信,尤其是对于那些没有直接父子关系的组件间的通信非常有用。EventBus基于Vue的自定义事件系统实现,工作原理遵…...

boot项目中定时任务quartz
最近换项目组,发现项目中定时任务使用的是quartz框架,上一篇文章[springboot定时任务]也是使用的quartz,只不过实现方式不同,于是整理下 定时任务常用方法有Quartz,Spring自带的Schedule框架 Quartz基础知识 quartz…...

使用阿里云OSS实现视频上传功能
目录 前言 视频上传 前言 阿里云对象存储服务(OSS)作为一种高可用、高扩展性的云端存储服务,为开发者提供了便捷、安全的对象存储解决方案。本文将介绍如何利用阿里云OSS实现视频上传功能。 视频上传 前期准备请看阿里云OSS文件上传和下载…...

LOTO示波器软件新增导览功能
新版本的大部分型号LOTO示波器的上位机软件我们改成了导航工具条方式。原来的方式是把所有功能都显示在不同的标签页中,这样的优点是非常快捷方便,基本上用鼠标一两次点击就能直达想要的功能设置。但是缺点是不熟练的客户可能记不住各种功能的标签位置在…...

【StructueEngineering】SYMBOL SCHEDULE
文章目录 标记表列SYMBOL SCHEDULELINES线条COLUMN REFERENCE SYMBOL柱参考标记SECTION REFERENCE SYMBOLS剖面参考标记DETAILREFERENCE SYMBOLS详图参考标记GENERALELEVATIONSYMBOLS一般立面图标记MISCELLANEOUS SYMBOLS杂项标记 STEEL FRAMING SYMBOLS钢结构平面图标记COLUMN…...

简化跨网文件传输摆渡过程,降低IT人员工作量
在当今数字化时代,IT企业面临着日益增长的数据交换需求。随着网络安全威胁的不断演变,网关隔离成为了保护企业内部网络不受外部威胁的重要手段。然而,隔离的同时,企业也需要在不同网络间安全、高效地传输文件,这就催生…...

关于python中屏蔽输出
python中屏蔽输出包含屏蔽标准输出(比如打印出来的内容)、屏蔽标准错误(错误信息)还有屏蔽logging信息等。 屏蔽标准输出 import contextlib import oswith open(os.devnull, "w") as devnull:with contextlib.redire…...

螺旋矩阵(算法题)
文章目录 螺旋矩阵解题思路 螺旋矩阵 给你一个正整数 n ,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。 示例 1: 输入:n 3 输出:[[1,2,3],[8,9,4],[7,6,5]]解题思路 模…...

ffmpeg-webrtc(metartc)给ffmpeg添加webrtc协议
这个是使用metrtc的库为ffmpeg添加webrtc传输协议,目前国内还有一个这样的开源项目,是杨成立大佬,大师兄他们在做,不过wili页面维护的不好,新手不知道如何使用,我专门对它做过介绍,另一篇博文&a…...
C语言知识大纲
一、基础 (一)变量定义和使用 (二)数据类型的字节数 (三)变量转换 (四)程序主要结构 (五)if和else判断 (六)switch判断 (七)while循环 (八)do while循环 (九)for循环 (十)基本输入输出 (十一)数组定义和使用 (十二)函数定义和使用 (十三)指针 (十四)多级指针 (十…...
云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?
大家好,欢迎来到《云原生核心技术》系列的第七篇! 在上一篇,我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在,我们就像一个拥有了一块崭新数字土地的农场主,是时…...

【JVM】- 内存结构
引言 JVM:Java Virtual Machine 定义:Java虚拟机,Java二进制字节码的运行环境好处: 一次编写,到处运行自动内存管理,垃圾回收的功能数组下标越界检查(会抛异常,不会覆盖到其他代码…...

Opencv中的addweighted函数
一.addweighted函数作用 addweighted()是OpenCV库中用于图像处理的函数,主要功能是将两个输入图像(尺寸和类型相同)按照指定的权重进行加权叠加(图像融合),并添加一个标量值&#x…...

srs linux
下载编译运行 git clone https:///ossrs/srs.git ./configure --h265on make 编译完成后即可启动SRS # 启动 ./objs/srs -c conf/srs.conf # 查看日志 tail -n 30 -f ./objs/srs.log 开放端口 默认RTMP接收推流端口是1935,SRS管理页面端口是8080,可…...

P3 QT项目----记事本(3.8)
3.8 记事本项目总结 项目源码 1.main.cpp #include "widget.h" #include <QApplication> int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec(); } 2.widget.cpp #include "widget.h" #include &q…...
【JavaSE】绘图与事件入门学习笔记
-Java绘图坐标体系 坐标体系-介绍 坐标原点位于左上角,以像素为单位。 在Java坐标系中,第一个是x坐标,表示当前位置为水平方向,距离坐标原点x个像素;第二个是y坐标,表示当前位置为垂直方向,距离坐标原点y个像素。 坐标体系-像素 …...
LeetCode - 199. 二叉树的右视图
题目 199. 二叉树的右视图 - 力扣(LeetCode) 思路 右视图是指从树的右侧看,对于每一层,只能看到该层最右边的节点。实现思路是: 使用深度优先搜索(DFS)按照"根-右-左"的顺序遍历树记录每个节点的深度对于…...

算法:模拟
1.替换所有的问号 1576. 替换所有的问号 - 力扣(LeetCode) 遍历字符串:通过外层循环逐一检查每个字符。遇到 ? 时处理: 内层循环遍历小写字母(a 到 z)。对每个字母检查是否满足: 与…...

Git 3天2K星标:Datawhale 的 Happy-LLM 项目介绍(附教程)
引言 在人工智能飞速发展的今天,大语言模型(Large Language Models, LLMs)已成为技术领域的焦点。从智能写作到代码生成,LLM 的应用场景不断扩展,深刻改变了我们的工作和生活方式。然而,理解这些模型的内部…...

pikachu靶场通关笔记19 SQL注入02-字符型注入(GET)
目录 一、SQL注入 二、字符型SQL注入 三、字符型注入与数字型注入 四、源码分析 五、渗透实战 1、渗透准备 2、SQL注入探测 (1)输入单引号 (2)万能注入语句 3、获取回显列orderby 4、获取数据库名database 5、获取表名…...