7种设计模式

1. 工厂模式

优点:封装了对象的创建过程,降低了耦合性,提供了灵活性和可扩展性。
缺点:增加了代码的复杂性,需要创建工厂类。
适用场景:当需要根据不同条件创建不同对象时,或者需要隐藏对象创建的细节时,可以使用工厂模式。

class Button {constructor(text) {this.text = text;}render() {console.log(`Rendering button with text: ${this.text}`);}
}class ButtonFactory {createButton(text) {return new Button(text);}
}const factory = new ButtonFactory();
const button = factory.createButton('Submit');
button.render(); // Output: Rendering button with text: Submit

2.单例模式

优点:确保一个类只有一个实例,节省系统资源,提供全局访问点。
缺点:可能引入全局状态,不利于扩展和测试。
适用场景:当需要全局唯一的对象实例时,例如日志记录器、全局配置对象等,可以使用单例模式。

class Logger {constructor() {if (Logger.instance) {return Logger.instance;}Logger.instance = this;}log(message) {console.log(`Logging: ${message}`);}
}const logger1 = new Logger();
const logger2 = new Logger();console.log(logger1 === logger2); // Output: true

3. 观察者模式

优点:实现了对象之间的松耦合,支持广播通信,当一个对象状态改变时,可以通知依赖它的其他对象进行更新。
缺点:可能导致性能问题和内存泄漏,需要合理管理观察者列表。
适用场景:当需要实现对象之间的一对多关系,一个对象的改变需要通知其他多个对象时,可以使用观察者模式。

class Subject {constructor() {this.observers = [];}addObserver(observer) {this.observers.push(observer);}removeObserver(observer) {const index = this.observers.indexOf(observer);if (index !== -1) {this.observers.splice(index, 1);}}notify(message) {this.observers.forEach((observer) => observer.update(message));}
}class Observer {update(message) {console.log(`Received message: ${message}`);}
}const subject = new Subject();
const observer1 = new Observer();
const observer2 = new Observer();subject.addObserver(observer1);
subject.addObserver(observer2);
subject.notify('Hello, observers!'); // Output

4.发布订阅模式

优点:解耦了发布者和订阅者,使它们可以独立变化。增加了代码的灵活性和可维护性。
缺点:可能会导致发布者过度发布消息,造成性能问题。订阅者需要订阅和取消订阅相关的逻辑。
适用场景:当存在一对多的关系,一个对象的状态变化需要通知多个其他对象时,可以使用发布订阅模式。

class PubSub {constructor() {this.subscribers = {};}subscribe(event, callback) {if (!this.subscribers[event]) {this.subscribers[event] = [];}this.subscribers[event].push(callback);}unsubscribe(event, callback) {const subscribers = this.subscribers[event];if (subscribers) {this.subscribers[event] = subscribers.filter(cb => cb !== callback);}}publish(event, data) {const subscribers = this.subscribers[event];if (subscribers) {subscribers.forEach(callback => callback(data));}}
}
const pubsub = new PubSub(); // 创建发布订阅对象
const callback1 = data => console.log('Subscriber 1:', data);
const callback2 = data => console.log('Subscriber 2:', data);
pubsub.subscribe('event1', callback1); // 订阅事件
pubsub.subscribe('event1', callback2); // 订阅事件
pubsub.publish('event1', 'Hello, world!'); // 发布事件
pubsub.unsubscribe('event1', callback2); // 取消订阅事件
pubsub.publish('event1', 'Hello again!'); // 再次发布事件
/*** Subscriber 1: Hello, world!Subscriber 2: Hello, world!Subscriber 1: Hello again!*/
  • 在上述示例中,PubSub 是发布订阅的实现类,它维护一个订阅者列表 subscribers,用于存储不同事件的订阅者列表。通过 subscribe 方法订阅事件,将回调函数添加到对应事件的订阅者列表中;通过 unsubscribe 方法取消订阅事件,从对应事件的订阅者列表中移除回调函数;通过 publish 方法发布事件,遍历对应事件的订阅者列表,依次执行回调函数。通过发布订阅模式,发布者和订阅者之间解耦,可以实现松散耦合的组件间通信。

  • 发布订阅模式适用于许多场景,如事件驱动的系统、消息队列、UI组件间的通信等,可以实现组件之间的解耦和灵活性。

  • 发布订阅模式(Publish-Subscribe Pattern)和观察者模式(Observer Pattern)是两种常见的设计模式,它们有一些相似之处,但也存在一些区别。

相似之处:

  • 都用于实现对象之间的消息通信和事件处理。
  • 都支持解耦,让发布者和订阅者(观察者)之间相互独立。

区别:

  • 关注点不同:观察者模式关注的是一个主题对象(被观察者)和多个观察者对象之间的关系。当主题对象的状态发生变化时,它会通知所有观察者对象进行更新。而发布订阅模式关注的是发布者和订阅者之间的关系,发布者将消息发送到一个中心调度器(或者称为事件总线),然后由调度器将消息分发给所有订阅者。
  • 中间件存在与否:发布订阅模式通常需要一个中间件(调度器或事件总线)来管理消息的发布和订阅,这样发布者和订阅者之间的通信通过中间件进行。而观察者模式则直接在主题对象和观察者对象之间进行通信,没有中间件的参与。
  • 松散耦合程度不同:观察者模式中,主题对象和观察者对象之间是直接关联的,主题对象需要知道每个观察者对象的存在。而在发布订阅模式中,发布者和订阅者之间并不直接关联,它们只与中间件进行通信,发布者和订阅者之间的耦合更加松散。
    观察者模式示例:
class Subject {constructor() {this.observers = [];}addObserver(observer) {this.observers.push(observer);}removeObserver(observer) {this.observers = this.observers.filter(obs => obs !== observer);}notify(data) {this.observers.forEach(observer => observer.update(data));}
}class Observer {update(data) {console.log('Received data:', data);}
}// 创建主题对象
const subject = new Subject();// 创建观察者对象
const observer1 = new Observer();
const observer2 = new Observer();// 添加观察者
subject.addObserver(observer1);
subject.addObserver(observer2);// 发送通知
subject.notify('Hello, observers!');

发布订阅模式示例:

class EventBus {constructor() {this.subscribers = {};}subscribe(event, callback) {if (!this.subscribers[event]) {this.subscribers[event] = [];}this.subscribers[event].push(callback);}unsubscribe(event, callback) {const subscribers = this.subscribers[event];if (subscribers) {this.subscribers[event] = subscribers.filter(cb => cb !== callback);}}publish(event, data) {const subscribers = this.subscribers[event];if (subscribers) {subscribers.forEach(callback => callback(data));}}}// 创建事件总线对象const eventBus = new EventBus();// 订阅事件eventBus.subscribe('message', data => {console.log('Received message:', data);});// 发布事件eventBus.publish('message', 'Hello, subscribers!');

在上述示例中,观察者模式中的Subject类相当于发布订阅模式中的EventBus类,Observer类相当于订阅者(观察者),notify方法相当于publish方法,update方法相当于订阅者接收到事件后的回调函数。

观察者模式和发布订阅模式都是常见的用于实现事件处理和消息通信的设计模式,根据实际场景和需求选择合适的模式进行使用。观察者模式更加简单直接,适用于一对多的关系,而发布订阅模式更加灵活,可以支持多对多的关系,并且通过中间件来解耦发布者和订阅者。

5. 原型模式:

  • 优点:通过克隆现有对象来创建新对象,避免了频繁的对象创建过程,提高了性能。
  • 缺点:需要正确设置原型对象和克隆方法,可能引入深拷贝或浅拷贝的问题。
    适用场景:当创建对象的成本较大且对象之间相似度较高时,可以使用原型模式来复用已有对象。
class Shape {constructor() {this.type = '';}clone() {return Object.create(this);}draw() {console.log(`Drawing a ${this.type}`);}
}const circlePrototype = new Shape();
circlePrototype.type = 'Circle';const squarePrototype = new Shape();
squarePrototype.type = 'Square';const circle = circlePrototype.clone();
circle.draw(); // Output: Drawing a Circleconst square = squarePrototype.clone();
square.draw(); // Output: Drawing a Square

6. 装饰者模式(Decorator Pattern)

  • 优点:动态地给对象添加新的功能,避免了使用子类继承的方式导致类爆炸的问题。
  • 缺点:增加了代码的复杂性,需要理解和管理装饰器的层次结构。
    适用场景:当需要在不修改现有对象结构的情况下,动态地添加功能或修改行为时,可以使用装饰者模式。
class Component {operation() {console.log('Component operation');}
}
class Decorator {constructor(component) {this.component = component;}operation() {this.component.operation();}
}class ConcreteComponent extends Component {operation() {console.log('ConcreteComponent operation');}
}class ConcreteDecoratorA extends Decorator {operation() {super.operation();console.log('ConcreteDecoratorA operation');}
}class ConcreteDecoratorB extends Decorator {operation() {super.operation();console.log('ConcreteDecoratorB operation');}
}const component = new ConcreteComponent();
const decoratorA = new ConcreteDecoratorA(component);
const decoratorB = new ConcreteDecoratorB(decoratorA);decoratorB.operation();
// Output:
// Component operation
// ConcreteComponent operation
// ConcreteDecoratorA operation
// ConcreteDecoratorB operation

7. 适配器模式

  • 优点:允许不兼容接口的对象协同工作,提高代码的复用性和灵活性。
  • 缺点:增加了代码的复杂性,需要理解和管理适配器的转换过程。
    适用场景:当需要将一个类的接口转换成客户端所期望的另一个接口时,可以使用适配器模式。
    示例代码:
class Target {request() {console.log('Target request');}
}class Adaptee {specificRequest() {console.log('Adaptee specificRequest');}
}class Adapter extends Target {constructor(adaptee) {super();this.adaptee = adaptee;}request() {this.adaptee.specificRequest();}
}const target = new Target();
target.request();
// Output: Target requestconst adaptee = new Adaptee();
const adapter = new Adapter(adaptee);
adapter.request();
// Output: Adaptee specificRequest

在上述示例中,Target 定义了客户端所期望的接口,Adaptee 是一个已有的类,它的接口与 Target 不兼容。适配器 Adapter 继承自 Target,并在其内部持有一个 Adaptee 的引用,通过适配器的 request 方法调用 Adaptee 的 specificRequest 方法,从而实现了对不兼容接口的适配。客户端可以通过调用适配器的 request 方法来使用 Adaptee 的功能。

适配器模式可以用于许多场景,例如在使用第三方库时需要将其接口转换成符合自己代码规范的接口,或者在对旧系统进行重构时需要兼容旧代码和新代码之间的差异。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/105982.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Go语言入门心法(五): 函数

一: go语言函数认知 Go语言入门心法(一): 基础语法 Go语言入门心法(二): 结构体 Go语言入门心法(三): 接口 忙着去耍帅,后期补上..........

Epoch和episodes的区别

“Epoch” 和 “episode” 是两个不同的概念,通常在不同领域中使用。 Epoch(周期): Epoch 是一个在机器学习和深度学习中常用的术语,通常用于表示训练数据集中的一个完整遍历。在每个 epoch 中,整个训练数据…

[题] 差分矩阵 #差分

题目 差分矩阵 题解 只有一个操作: void insert(int x1, int y1, int x2, int y2, int c){b[x1][y1] c;b[x2 1][y1] - c;b[x1][y2 1] - c;b[x2 1][y2 1] c; }利用差分的思想,扩展到二维上。 insert函数作用是将矩阵之内的数全部加上c,…

集合的进阶

不可变集合 创建不可变的集合 在创建了之后集合的长度内容都不可以变化 静态集合的创建在list ,set ,map接口当中都可以获取不可变集合 方法名称说明static list of(E …elements)创建一个具有指定元素集合list集合对象staticlist of(E…elements)创…

Java8新特性实战

Java 8 作为一个里程碑式的版本,其中所做出的改变,在许多方面比Java历史上任何一次改变都深远。Java为什么会一直在改变,因为编程语言就像生态系统一样,更优秀语言的出现,落后的语言就会被取代,除非它们不断…

Ionic4 生命周期钩子函数和angular生命周期钩子函数介绍

1、Ionic4 生命周期钩子函数 Ionic 4(以及之后的 Ionic 版本)使用了 Angular 生命周期钩子,因为 Ionic 是基于 Angular 构建的。因此,Ionic 4 中的生命周期与 Angular 组件生命周期非常相似。以下是一些常见的 Ionic 4 生命周期钩…

掌握Python爬虫实现网站关键词扩展提升曝光率

目录 一、关键词优化的重要性 二、关键词优化的基本方法 1、选择与网站内容相关的关键词 2、控制关键词的密度和分布 3、关键词的层次布局 三、Python爬虫实现网站关键词扩展 1、确定目标网站 2、分析目标网站的HTML结构 3、编写Python爬虫代码 4、分析爬取到的关键词…

BSPHP 未授权访问 信息泄露

漏洞描述 BSPHP 存在未授权访问 泄露用户 IP 和 账户名信息 漏洞复现 访问url: 构造payload访问: /admin/index.php?madmin&clog&atable_json&jsonget&soso_ok1&tuser_login_log&page1&limit10&bsphptime16004073…

25栈和队列-理解栈和队列

目录 LeetCode之路——232. 用栈实现队列 分析: LeetCode之路——225. 用队列实现栈 分析: 栈(Stack)和队列(Queue)是两种基本的数据结构,它们在计算机科学中用于不同的目的。以下是它们的定…

P4491 [HAOI2018] 染色

传送门:洛谷 解题思路: 写本题需要知道一个前置知识: 假设恰好选 k k k个条件的方案数为 f ( k ) f(k) f(k);先钦定选 k k k个条件,其他条件无所谓的方案数为 g ( k ) g(k) g(k) 那么存在这样的一个关系: g ( k ) ∑ i k n C i k f ( i ) g(k)\sum_{ik}^nC_{i}^kf(i) g(k)…

【windows下docker安装rocketMQ】

namesrv和broker安装就不说了,见如下博客 https://blog.csdn.net/Wonderful1025/article/details/107244434/ 安装rocketMQ-console docker run -d -e "JAVA_OPTS-Drocketmq.config.namesrvAddr192.168.65.2:9876 -Drocketmq.config.isVIPChannelfalse"…

12.SpringBoot之RestTemplate的使用

SpringBoot之RestTemplate的使用 初识RestTemplate RestTemplate是Spring框架提供用于调用Rest接口的一个应用,它简化了与http服务通信方式。RestTemplate统一Restfull调用的标准,封装HTTP链接,只要需提供URL及返回值类型即可完成调用。相比…

Spark中的Driver、Executor、Stage、TaskSet、DAGScheduler等介绍

工作流程: Driver 创建 SparkSession 并将应用程序转化为执行计划,将作业划分为多个 Stage,并创建相应的 TaskSet。Driver 将 TaskSet 发送给 TaskScheduler 进行调度和执行。TaskScheduler 根据资源情况将任务分发给可用的 Executor 进程执…

DAE转换GLB格式

1、DAE模型介绍 DAEA(Deep Attentive and Ensemble Autoencoder)模型是一种用于无监督学习的深度学习模型,由华为公司提出。DAEA模型结合了自编码器和深度注意力机制,能够对高维数据进行降维和特征提取,并且在处理大规…

博图数值按照特定格式(“T000000”)转换成字符串

一、前言 1.string to dint物流输送线往往需要通过扫码器读取托盘条码,一维码或者二维码​。 读取的数据需要解析才能正常使用。两种方式读取的数据直接是字符串,但当设备与上位机通信时, 字符串数据量太大,故可以通过算法转换成…

Ceph分布式存储的简单介绍与Ceph集群的部署搭建

文章目录 1. 存储的概述1.1 单机存储设备1.1.1 DAS(直接附加存储)1.1.2 NAS(网络附加存储)1.1.3 SAN(存储区域网络) 1.2 单机存储的缺陷1.3 分布式存储(软件定义的存储 SDS)1.4 分布…

unity ugui text 超链接和下划线,支持部分富文本格式

unity版本:2021.3.6f1 局限性: 1.测试发现不能使用 size 富文本标签, 2.同一文本不能设置不同颜色的超链接文本 其它:代码中注释掉使用innerTextColor的地方,可以使用富文本设置超链接颜色, 但是下划线是文本本身颜色 …

springboot中如何进行业务层测试事务回滚

业务层测试事务回滚 为测试用例添加事务,SpringBoot会对测试用例对应的事务提交操作进行回滚 SpringBootTest Transactional public class DaoTest { Autowired private BookService bookService;} 如果想在测试用例中提交事务,可以通过Rollback注…

windows部署django服务器

windows部署django服务器 1、安装IIS1.1 控制面板-----程序----程序和功能----启用或关闭windows功能1.2安装IIS服务器,完成后,重新进入,把CGI安装进系统 2、安装python与虚拟环境2.1 安装python2.2 安装virtualenv虚拟环境2.3 创建一个虚拟环…

求二叉树的高度——函数递归的思想

二叉树的高度:左右两个数最高的那个的1 int TreeHight(BTNode* root) {if (root NULL){return 0;}int lefhightTreeHight(root->left);int righthight TreeHight(root->right);return lefhight > righthight ? TreeHight(root->left) 1 : TreeHight…