当前位置: 首页 > news >正文

关于软件设计模式的理解

系列文章
关于时间复杂度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. 可以提高程序员的思维能力、编程能力和设计能力。
  2. 可以使程序设计更加标准化、代码编制更加工程化,使软件开发效率大大提高,从而缩短软件的开发周期。
  3. 可以使设计的代码可复用性高、可读性强、可靠性高、灵活性好、可维护性强。

三、使用率最高的设计模式有哪几个?具体使用场景举例

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);}
}

解释
支付策略接口: 定义了支付的方法。
具体的支付策略类: 实现了支付策略接口,每个类代表一种支付方式,实现了具体的支付逻辑。
支付上下文类: 持有具体的支付策略对象,并提供支付方法,客户端通过上下文类进行支付。
使用策略模式: 创建支付上下文对象,根据需要设置不同的支付策略,然后进行支付操作。
策略模式能够有效地解耦客户端和具体的支付策略,使得支付系统更加灵活,易于扩展和维护。

相关文章:

  • HQL面试题练习 —— 合并数据
  • [Python]pyenv 环境配置
  • Selenium 库的爬虫实现
  • Host头攻击-使用加密和身份验证机制
  • git分支常用命令
  • Scrum 的速度如何衡量和提高
  • 单细胞 10X 和seurat对象学习
  • 视频推拉流EasyDSS系统如何在清理缓存文件的同时不影响缓存读写?
  • C++ 程序的基本要素
  • 通过JavaScript本地存储数据
  • HG/T 6088-2022 透水道路用涂料检测
  • 有限元法之有限元空间的构造
  • LeetCode2.两数相加
  • nodejs安装配置
  • K-means聚类算法详细介绍
  • SegmentFault for Android 3.0 发布
  • iOS小技巧之UIImagePickerController实现头像选择
  • Java,console输出实时的转向GUI textbox
  • JavaScript 奇技淫巧
  • k8s 面向应用开发者的基础命令
  • log4j2输出到kafka
  • mysql 5.6 原生Online DDL解析
  • NSTimer学习笔记
  • Vue 动态创建 component
  • vue-loader 源码解析系列之 selector
  • Vue全家桶实现一个Web App
  • 浅谈Kotlin实战篇之自定义View图片圆角简单应用(一)
  • 如何优雅的使用vue+Dcloud(Hbuild)开发混合app
  • 问:在指定的JSON数据中(最外层是数组)根据指定条件拿到匹配到的结果
  • 一个完整Java Web项目背后的密码
  • 回归生活:清理微信公众号
  • 我们雇佣了一只大猴子...
  • 支付宝花15年解决的这个问题,顶得上做出十个支付宝 ...
  • ​埃文科技受邀出席2024 “数据要素×”生态大会​
  • ‌移动管家手机智能控制汽车系统
  • #QT项目实战(天气预报)
  • #每日一题合集#牛客JZ23-JZ33
  • (2024)docker-compose实战 (9)部署多项目环境(LAMP+react+vue+redis+mysql+nginx)
  • (9)YOLO-Pose:使用对象关键点相似性损失增强多人姿态估计的增强版YOLO
  • (delphi11最新学习资料) Object Pascal 学习笔记---第13章第1节 (全局数据、栈和堆)
  • (done) 两个矩阵 “相似” 是什么意思?
  • (Redis使用系列) Springboot 使用Redis+Session实现Session共享 ,简单的单点登录 五
  • (官网安装) 基于CentOS 7安装MangoDB和MangoDB Shell
  • (免费分享)基于springboot,vue疗养中心管理系统
  • (一)spring cloud微服务分布式云架构 - Spring Cloud简介
  • (转) Android中ViewStub组件使用
  • (转)EXC_BREAKPOINT僵尸错误
  • (转)大型网站的系统架构
  • .MyFile@waifu.club.wis.mkp勒索病毒数据怎么处理|数据解密恢复
  • .net websocket 获取http登录的用户_如何解密浏览器的登录密码?获取浏览器内用户信息?...
  • .Net6使用WebSocket与前端进行通信
  • .net和jar包windows服务部署
  • .NET开发者必备的11款免费工具
  • .NET平台开源项目速览(15)文档数据库RavenDB-介绍与初体验
  • :“Failed to access IIS metabase”解决方法