设计模式的原则有哪些?

作者:你的人类朋友日期:2025/10/31

前言

温馨提示

对于原本不太熟悉设计模式的人来说(比如在下),这些内容是需要一定的时间消化的!慢慢来 😆

👋 你好啊,我是你的人类朋友!

今天说说设计模式的原则有哪些!

在开发用户权限系统时,你是否遇到过这样的问题:

当创建新的管理员用户类型时,发现它无法兼容普通用户的所有方法,导致系统中到处需要判断用户类型?

让我们了解设计模式的基本原则,构建更健壮的软件架构~

健壮是啥意思? 健壮是指软件系统在面对变化和复杂性时,能够保持稳定运行的能力。也就是耐造的能力。

正文

SOLID 原则

SOLID 是面向对象设计的五个基本原则。

1. 单一职责原则 (SRP - Single Responsibility Principle)

含义:一个类应该只有一个引起变化的原因。

1/* 违反SRP:订单处理类承担了验证、计算、存储、通知多个职责 */
2class OrderProcessor {
3    validateOrder(order) {
4        return order.items.length > 0
5    }
6
7    calculateTotal(order) {
8        return order.items.reduce((sum, item) => sum + item.price, 0)
9    }
10
11    saveToDatabase(order) {
12        console.log('保存订单到数据库')
13    }
14
15    sendEmailConfirmation(order) {
16        console.log('发送订单确认邮件')
17    }
18}
19
20// 使用
21const processor = new OrderProcessor()
22processor.validateOrder({items: [{price: 100}]})
23/* 违反SRP结束 */
24
25/* 遵循SRP:每个类只负责一个明确职责 */
26class OrderValidator {
27    validate(order) {
28        return order.items.length > 0
29    }
30}
31
32class OrderCalculator {
33    calculateTotal(order) {
34        return order.items.reduce((sum, item) => sum + item.price, 0)
35    }
36}
37
38class OrderRepository {
39    save(order) {
40        console.log('保存订单到数据库')
41    }
42}
43
44class OrderProcessor {
45    constructor() {
46        this.validator = new OrderValidator()
47        this.calculator = new OrderCalculator()
48        this.repository = new OrderRepository()
49    }
50
51    process(order) {
52        if (!this.validator.validate(order)) return
53        order.total = this.calculator.calculateTotal(order)
54        this.repository.save(order)
55    }
56}
57
58// 使用
59const order = {items: [{price: 100}, {price: 200}]}
60const processor = new OrderProcessor()
61processor.process(order)
62console.log(order.total) // 输出: 300
63/* 遵循SRP结束 */
64

2. 开闭原则 (OCP - Open-Closed Principle)

含义:软件实体应该对扩展开放,对修改关闭。

1/* 违反OCP:添加新通知类型需要修改原有类 */
2class NotificationService {
3    send(message, type) {
4        if (type === 'email') {
5            console.log(`发送邮件: ${message}`)
6        } else if (type === 'sms') {
7            console.log(`发送短信: ${message}`)
8        }
9        // 添加新类型需要修改这里
10    }
11}
12
13// 使用
14const notifier = new NotificationService()
15notifier.send('订单创建成功', 'email')
16/* 违反OCP结束 */
17
18/* 遵循OCP:通过扩展而非修改来支持新类型 */
19class Notification {
20    send(message) {
21        throw new Error('必须实现send方法')
22    }
23}
24
25class EmailNotification extends Notification {
26    send(message) {
27        console.log(`发送邮件: ${message}`)
28    }
29}
30
31class SMSNotification extends Notification {
32    send(message) {
33        console.log(`发送短信: ${message}`)
34    }
35}
36
37class PushNotification extends Notification {
38    send(message) {
39        console.log(`发送推送: ${message}`)
40    }
41}
42
43class NotificationService {
44    constructor() {
45        this.notifications = []
46    }
47
48    addNotification(notification) {
49        this.notifications.push(notification)
50    }
51
52    broadcast(message) {
53        this.notifications.forEach(notification => {
54            notification.send(message)
55        })
56    }
57}
58
59// 使用
60const service = new NotificationService()
61service.addNotification(new EmailNotification())
62service.addNotification(new SMSNotification())
63service.broadcast('系统维护通知')
64// 输出: 发送邮件: 系统维护通知
65// 输出: 发送短信: 系统维护通知
66/* 遵循OCP结束 */
67

3. 里氏替换原则 (LSP - Liskov Substitution Principle)

含义:子类应该能够替换它们的父类,而不影响程序的正确性。

1/* 违反LSP:子类改变了父类的权限检查逻辑 */
2class User {
3    constructor(permissions) {
4        this.permissions = permissions || []
5    }
6
7    hasPermission(permission) {
8        return this.permissions.includes(permission)
9    }
10
11    canDeleteContent() {
12        return this.hasPermission('delete')
13    }
14}
15
16class AdminUser extends User {
17    canDeleteContent() {
18        // 违反:管理员绕过权限检查,破坏了父类契约
19        return true
20    }
21}
22
23// 使用 - 会出现不一致行为
24function deleteContent(user, content) {
25    if (user.canDeleteContent()) {
26        console.log(`删除内容: ${content}`)
27    }
28}
29
30const regularUser = new User(['read'])
31const adminUser = new AdminUser([]) // 没有delete权限但可以删除
32deleteContent(regularUser, '文章') // 无输出
33deleteContent(adminUser, '文章') // 输出: 删除内容: 文章
34/* 违反LSP结束 */
35
36/* 遵循LSP:子类遵守父类的行为契约 */
37class User {
38    constructor(permissions) {
39        this.permissions = permissions || []
40    }
41
42    hasPermission(permission) {
43        return this.permissions.includes(permission)
44    }
45
46    canDeleteContent() {
47        return this.hasPermission('delete')
48    }
49}
50
51class AdminUser extends User {
52    canDeleteContent() {
53        // 遵循:仍然进行权限检查,只是权限范围更大
54        return this.hasPermission('delete') || this.hasPermission('admin_delete')
55    }
56}
57
58// 使用 - 行为一致
59const regularUser = new User(['delete'])
60const adminUser = new AdminUser(['admin_delete'])
61deleteContent(regularUser, '文章') // 输出: 删除内容: 文章
62deleteContent(adminUser, '文章') // 输出: 删除内容: 文章
63/* 遵循LSP结束 */
64

4. 接口隔离原则 (ISP - Interface Segregation Principle)

含义:客户端不应该被迫依赖它们不使用的接口。

1/* 违反ISP:简单商店被迫依赖所有支付方法 */
2class PaymentProcessor {
3    processCreditCard(amount) {
4        console.log(`信用卡支付: $${amount}`)
5    }
6    processPayPal(amount) {
7        console.log(`PayPal支付: $${amount}`)
8    }
9    processBankTransfer(amount) {
10        console.log(`银行转账: $${amount}`)
11    }
12}
13
14class SimpleStore {
15    constructor() {
16        this.paymentProcessor = new PaymentProcessor()
17    }
18
19    processPayment(amount) {
20        // 只使用信用卡,但依赖了所有支付方法
21        this.paymentProcessor.processCreditCard(amount)
22    }
23}
24
25// 使用
26const store = new SimpleStore()
27store.processPayment(100) // 输出: 信用卡支付: $100
28/* 违反ISP结束 */
29
30/* 遵循ISP:客户端只依赖需要的接口 */
31class PaymentProcessor {
32    process(amount) {
33        throw new Error('必须实现process方法')
34    }
35}
36
37class CreditCardProcessor extends PaymentProcessor {
38    process(amount) {
39        console.log(`信用卡支付: $${amount}`)
40    }
41}
42
43class PayPalProcessor extends PaymentProcessor {
44    process(amount) {
45        console.log(`PayPal支付: $${amount}`)
46    }
47}
48
49class SimpleStore {
50    constructor(paymentProcessor) {
51        this.paymentProcessor = paymentProcessor
52    }
53
54    processPayment(amount) {
55        this.paymentProcessor.process(amount)
56    }
57}
58
59// 使用
60const creditStore = new SimpleStore(new CreditCardProcessor())
61const paypalStore = new SimpleStore(new PayPalProcessor())
62creditStore.processPayment(100) // 输出: 信用卡支付: $100
63paypalStore.processPayment(200) // 输出: PayPal支付: $200
64/* 遵循ISP结束 */
65

5. 依赖倒置原则 (DIP - Dependency Inversion Principle)

含义:高层模块不应该依赖低层模块,两者都应该依赖抽象。

1/* 违反DIP:服务层直接依赖具体的数据访问实现 */
2class MySQLUserRepository {
3    findUserById(id) {
4        console.log([`从MySQL查询用户: ${id}`](https://xplanc.org/primers/document/zh/10.Bash/90.%E5%B8%AE%E5%8A%A9%E6%89%8B%E5%86%8C/EX.id.md))
5        return {id, name: 'MySQL用户'}
6    }
7}
8
9class UserService {
10    constructor() {
11        // 违反:直接依赖具体实现
12        this.userRepository = new MySQLUserRepository()
13    }
14
15    getUserProfile(id) {
16        return this.userRepository.findUserById(id)
17    }
18}
19
20// 使用
21const service = new UserService()
22const user = service.getUserProfile(1)
23console.log(user.name) // 输出: MySQL用户
24/* 违反DIP结束 */
25
26/* 遵循DIP:通过抽象解耦依赖关系 */
27class UserRepository {
28    findUserById(id) {
29        throw new Error('必须实现findUserById方法')
30    }
31}
32
33class MySQLUserRepository extends UserRepository {
34    findUserById(id) {
35        console.log([`从MySQL查询用户: ${id}`](https://xplanc.org/primers/document/zh/10.Bash/90.%E5%B8%AE%E5%8A%A9%E6%89%8B%E5%86%8C/EX.id.md))
36        return {id, name: 'MySQL用户'}
37    }
38}
39
40class MongoDBUserRepository extends UserRepository {
41    findUserById(id) {
42        console.log([`从MongoDB查询用户: ${id}`](https://xplanc.org/primers/document/zh/10.Bash/90.%E5%B8%AE%E5%8A%A9%E6%89%8B%E5%86%8C/EX.id.md))
43        return {id, name: 'MongoDB用户'}
44    }
45}
46
47class UserService {
48    constructor(userRepository) {
49        // 遵循:依赖抽象接口
50        this.userRepository = userRepository
51    }
52
53    getUserProfile(id) {
54        return this.userRepository.findUserById(id)
55    }
56}
57
58// 使用 - 可以轻松切换不同的数据源
59const mysqlService = new UserService(new MySQLUserRepository())
60const mongoService = new UserService(new MongoDBUserRepository())
61mysqlService.getUserProfile(1) // 输出: 从MySQL查询用户: 1
62mongoService.getUserProfile(1) // 输出: 从MongoDB查询用户: 1
63/* 遵循DIP结束 */
64

其他重要原则

6. 合成复用原则 (CRP - Composite Reuse Principle)

含义:优先使用对象组合,而不是继承来达到复用的目的。

1/* 违反CRP:使用继承导致类层次复杂 */
2class Logger {
3    log(message) {
4        console.log(`基础日志: ${message}`)
5    }
6}
7
8class FileLogger extends Logger {
9    log(message) {
10        super.log(message)
11        console.log(`文件日志: ${message}`)
12    }
13}
14
15// 使用
16const fileLogger = new FileLogger()
17fileLogger.log('测试消息')
18/* 违反CRP结束 */
19
20/* 遵循CRP:使用组合提高灵活性 */
21class ConsoleWriter {
22    write(message) {
23        console.log(`控制台: ${message}`)
24    }
25}
26
27class FileWriter {
28    write(message) {
29        console.log(`写入文件: ${message}`)
30    }
31}
32
33class DatabaseWriter {
34    write(message) {
35        console.log(`保存到数据库: ${message}`)
36    }
37}
38
39class Logger {
40    constructor(writer) {
41        this.writer = writer
42    }
43
44    log(message) {
45        this.writer.write(message)
46    }
47}
48
49// 使用 - 可以灵活组合不同的写入器
50const consoleLogger = new Logger(new ConsoleWriter())
51const fileLogger = new Logger(new FileWriter())
52const dbLogger = new Logger(new DatabaseWriter())
53
54consoleLogger.log('控制台消息') // 输出: 控制台: 控制台消息
55fileLogger.log('文件消息') // 输出: 写入文件: 文件消息
56dbLogger.log('数据库消息') // 输出: 保存到数据库: 数据库消息
57/* 遵循CRP结束 */
58

7. 迪米特法则 (LoD - Law of Demeter)

含义:一个对象应该对其他对象有最少的了解,只与直接的朋友通信。

1/* 违反LoD:订单服务深入访问多层对象 */
2class OrderService {
3    constructor(userService) {
4        this.userService = userService
5    }
6
7    getOrderSummary(orderId) {
8        const order = this.userService.orderRepository.findOrder(orderId)
9        const user = order.getUser()
10        const address = user.getAddress() // 违反:访问间接对象
11        const city = address.getCity() // 违反:访问更深层对象
12        return {city}
13    }
14}
15
16// 使用 - 耦合度过高
17const service = new OrderService(userService)
18const summary = service.getOrderSummary(123)
19/* 违反LoD结束 */
20
21/* 遵循LoD:只与直接朋友通信,减少耦合 */
22class OrderRepository {
23    getOrderSummary(orderId) {
24        const order = this.findOrder(orderId)
25        return order.getSummary() // 委托给订单对象处理内部细节
26    }
27}
28
29class OrderService {
30    constructor(orderRepository) {
31        this.orderRepository = orderRepository
32    }
33
34    getOrderSummary(orderId) {
35        const orderSummary = this.orderRepository.getOrderSummary(orderId)
36        return {
37            orderId: orderSummary.id,
38            userName: orderSummary.userName,
39            city: orderSummary.city
40        } // 遵循:只访问直接提供的数据
41    }
42}
43
44// 使用 - 耦合度低,易于测试和维护
45const orderRepo = new OrderRepository()
46const service = new OrderService(orderRepo)
47const summary = service.getOrderSummary(123)
48console.log(summary.city)
49/* 遵循LoD结束 */
50

最后

回到前言中的问题:

如何确保新的管理员用户类型能够兼容普通用户?

答案就是应用里氏替换原则

确保子类完全遵守父类的行为契约,任何使用父类的地方都可以安全地替换为子类,不会破坏系统功能!


设计模式的原则有哪些?》 是转载文章,点击查看原文


相关推荐


Java的包装类
麦麦鸡腿堡2025/10/29

包装类(Wrapper)的分类: 1.针对八种基本数据类型相应的引用类型--包装类 2.有了类的特点,就可以调用类中的方法 *黄色框内都是number的子类,number是Ojbect子类,黑色框中的包装类是独立的,Ojbect子类 //boolean-Boolean-父类Object //char-Character-父类Object //byte-Byte-父类number-父类Object //int-Integer-父类number-父类Object //long-Long


17_AI智能体开发架构搭建之Flask集成swagger在线文档实践
腾飞开源2025/10/26

一、为什么需要Swagger集成? 在微服务架构和前后端分离的现代开发模式中,API文档承担着关键角色: 开发效率:前后端并行开发,减少沟通成本 接口契约:明确的请求/响应规范,避免歧义 测试便利:直接在文档界面测试API 团队协作:新成员快速理解接口设计 客户端生成:自动生成多种语言客户端代码 AI智能体系统设计相关文章: 👉《01_AI智能体系统设计之系统架构设计》 👉《02_AI智能体系统设计之钉钉消息处理流程设计》 👉《03_AI智能体系统设计之Ag


C#.NET SqlKata 使用详解:优雅构建动态 SQL 查询
唐青枫2025/10/23

简介 在复杂项目中,为了保持 SQL 灵活性与可读性,开发者往往需要手写大量拼接字符串或使用 ORMs 附带的 LINQ,但两者各有局限:手写拼接易出错、难以维护;LINQ 在某些场景下生成的 SQL 不够直观或性能不佳。 SqlKata 是一款轻量级、数据库无关的查询构建器(Query Builder),提供—— 流式 API,链式调用拼装 SQL 可切换编译器,支持多种数据库方言(SQL Server、PostgreSQL、MySQL、SQLite、Oracle 等) 语


深度解析 DNS服务:从基础原理到自建 DNS 服务器完整指南
coder4_2025/10/22

目录 一、DNS服务基础知识 1、DNS 2、域名 ①域名组成 ②域名管理与结构 3、DNS服务工作原理 ①主机中DNS客户端配置 ②DNS服务解析域名流程 4、DNS查询类型 ①递归查询 ②迭代查询 5、DNS解析类型 6、hosts文件 7、完整的域名查询请求流程 二、DNS检测工具 1、dig命令 2、host命令 3、nslookup命令 4、rndc命令 5、whois命令 三、自建DNS服务 1、ubuntu中的systemd-reso


Python爬虫入门:从零到数据采集
Moniane2025/10/21

Python爬虫基础指南 Python爬虫是自动化获取网络数据的技术,广泛应用于数据采集、市场分析等领域。以下是核心实现步骤: 1. 核心库选择 import requests # 发送HTTP请求 from bs4 import BeautifulSoup # HTML解析 import pandas as pd # 数据存储 2. 基础爬取流程 # 发送请求 response = requests.get("https://example.com/books") res


GPT-2 中的残差权重初始化
mwq301232025/10/19

GPT-2 中的残差权重初始化 1. 概述 在深度残差网络中,每一层的输出都会被加到其输入上。如果不对这些层的权重进行特殊处理,随着网络深度的增加,残差路径上累加的信号方差可能会不断增大,导致数值不稳定和训练发散。 为了解决这个问题,GPT-2 的作者在论文中提出了一种针对残差连接路径上的层进行特殊缩放的初始化方法。 2. 哪些是“残差权重”? 在 GPT-2 的一个 Decoder Block 中,有两个子层的输出会直接被加到残差流(Residual Stream)上。因此,这两层的权重就是我


SpringBoot启动优化7板斧:砍掉70%启动时间的魔鬼实践
皮皮林5512025/10/18

1.延迟初始化:按需加载的智慧 2.组件扫描精准打击:告别无差别扫描 3.JVM参数调优:启动加速的隐藏开关 4.自动配置瘦身:砍掉Spring Boot的"赘肉" 5.类加载优化:让JVM轻装上阵 6.数据库连接优化:断开启动时的枷锁 7.编译优化:AOT与分层编译的威力 综合优化案例:电商平台实战 启动优化检查清单 各优化手段效果对比图 1. 延迟初始化:按需加载的智慧 实践方案: # application.properties spring.main.lazy-initializat


除了JSON/XML,你还应该了解的数据描述语言ASN.1 —— 附《SpringBoot实现ASN.1在线解析工具》
风象南2025/10/17

前言 在日常开发中,我们经常接触JSON、XML等数据格式,但你是否听说过ASN.1?这种在通信、安全、物联网领域广泛使用的数据描述语言。 可能对一部分开发者来说有些陌生,但在特定场景下却有着不可替代的作用。今天,我们就来深入了解一下ASN.1,并用SpringBoot实现一个在线解析工具。 什么是ASN.1? 基本概念 ASN.1(Abstract Syntax Notation One)是一种标准化的数据描述语言,由ITU-T(国际电信联盟)和ISO(国际标准化组织)共同制定。它提供了一种平


Redis(62)如何优化Redis的连接数?
Victor3562025/10/15

优化 Redis 的连接数可以通过以下策略实现: 使用连接池。 合理配置 Redis。 调整操作系统限制。 使用客户端连接复用。 实现批量操作。 分布式架构及读写分离。 以下将详细探讨这些策略,并提供相应代码示例。 1. 使用连接池 连接池可以重用现有连接,减少连接创建和释放的开销,从而提升性能。 Java 示例(Jedis 连接池) 添加 Jedis 依赖: 确保在 pom.xml 文件中添加 Jedis 依赖: <dependency> <groupId>redis.clie


nginx反向代理与缓存功能
Justin_192025/10/14

目录 一,正向代理和反向代理 1,正向代理概述 2,反向代理概述 二,配置实战 1,实现反向代理负载均衡 三,nginx配置跨域cors 1,跨域的定义 2,同源的定义 3,不同源的限制 4,nginx解决跨域的原理 5,案例 一,正向代理和反向代理 1,正向代理概述 正向代理是一个位于客户端和目标服务器之间的代理服务器。为了从目标服务器得到目标,客户端向代理服务器发送一个请求,并且指定目标服务器,再代理向目标服务器转发请求,将得到的目标返回给客户端。 正向

首页编辑器站点地图

Copyright © 2025 聚合阅读

License: CC BY-SA 4.0