这是参加第六届青训营第五次笔记,以下是
课程纲要
以及知识要点
,以及一丢丢自己的理解。简单的记录一下,方便后面回头复盘。复盘再深入更新思考吧。至于我说的东西可能不对,但不喜勿喷。如果,愿意帮忙纠正,还是很欢迎的。 今天的设计模式,先了解一下概念和特点,然后先写了一点demo吧。
一、 什么是设计模式
1. 软件设计中常见问题的解决方案模型
- 历史经验的总结
- 与特定语言无关
二、设计模式分类
1. 23种设计模式
- 创建型 - 如何创建一个对象
- 结构型 - 如何灵活的将对象组装成较大的结构
- 行为型 - 负责对象间的高效铜线和职责划分
罗列一下23种设计模式,带有
*
比较常用。
创建型模式(Creational Patterns):
*
工厂方法模式(Factory Method Pattern)
抽象工厂模式(Abstract Factory Pattern)
*
单例模式(Singleton Pattern)
建造者模式(Builder Pattern)
原型模式(Prototype Pattern)
结构型模式(Structural Patterns):
*
适配器模式(Adapter Pattern)
桥接模式(Bridge Pattern)
组合模式(Composite Pattern)
*
装饰器模式(Decorator Pattern)
外观模式(Facade Pattern)
享元模式(Flyweight Pattern)
*
代理模式(Proxy Pattern)
行为型模式(Behavioral Patterns):
职责链模式(Chain of Responsibility Pattern)
命令模式(Command Pattern)
解释器模式(Interpreter Pattern)
迭代器模式(Iterator Pattern)
中介者模式(Mediator Pattern)
备忘录模式(Memento Pattern)
*
观察者模式(Observer Pattern)
状态模式(State Pattern)
*
策略模式(Strategy Pattern)
*
模板方法模式(Template Method Pattern)
访问者模式(Visitor Pattern)
2. 浏览器中的设计模式
- 单例模式(eg:window)
定义: 全局唯一访问对象
应用场景:缓存,全局状态管理等。
特点:- 单一实例:无论多少次实例化,都只会存在一个实例(存在实例化,就不再重新实例化了)。
- 全局访问:该实例可以在应用程序的任何地方被访问,使其成为一个全局的资源。
- 相等性:无论从哪里访问单例实例,得到的都是同一个对象,所以它们是完全相等的。
- 延迟实例化:单例对象在首次访问时才会被创建,以节省资源。
class AppConfig {
private static instance: AppConfig;
private apiUrl: string;
private constructor() {
this.apiUrl = "https://api.example.com";
}
static getInstance(): AppConfig {
if (!this.instance) {
this.instance = new AppConfig();
** **}
return this.instance;
}
getApiUrl(): string {
return this.apiUrl;
}
}
// 使用单例模式获取配置实例
const appConfig1 = AppConfig.getInstance();
const appConfig2 = AppConfig.getInstance();
console.log(appConfig1 === appConfig2); // 输出 true,两个实例是同一个实例
console.log(appConfig1.getApiUrl()); // 输出 "https://api.example.com"
console.log(appConfig2.getApiUrl()); // 输出 "https://api.example.com"
- 发布订阅模式(eg:subscribe)
定义:一种订阅机制,可在被订阅对象发生变化时通知订阅者。
应用场景:从系统架构之间的解耦,到业务中一些实现模式,像邮件订阅,上线订阅等等,应用广泛。
特点:- 解耦:发布者和订阅者之间互不依赖,通过中介来进行通信,降低了组件之间的耦合度。
- 可扩展性:新的订阅者可以轻松地加入,不会影响到发布者或其他订阅者。
- 多对多关系:一个消息可以有多个订阅者,一个订阅者也可以订阅多个消息。
- 异步通信:订阅者不需要立即处理消息,可以在适当的时机进行处理。
// 发布者(主题)
class NewsPublisher {
constructor() {
this.subscribers = [];
}
subscribe(subscriber) {
this.subscribers.push(subscriber);
}
publish(news) {
this.subscribers.forEach(subscriber => {
subscriber.notify(news);
});
}
}
// 订阅者(观察者)
class Subscriber {
constructor(name) {
this.name = name;
}
notify(news) {
console.log(`${this.name} received news: ${news}`);
}
}
// 创建发布者和订阅者
const publisher = new NewsPublisher();
const subscriber1 = new Subscriber('Subscriber 1');
const subscriber2 = new Subscriber('Subscriber 2');
// 订阅新闻
publisher.subscribe(subscriber1);
publisher.subscribe(subscriber2);
// 发布新闻
publisher.publish('Breaking News: COVID-19 Vaccine Developed');
// 输出:
// Subscriber 1 received news: Breaking News: COVID-19 Vaccine Developed
// Subscriber 2 received news: Breaking News: COVID-19 Vaccine Developed
- 原型模式(原型链)
定义:赋值已有对象来创建新的对象
应用场景: JS中对象创建的基本模式
特点:是通过复制(克隆)已有对象的实例来创建新的对象,从而避免了直接创建对象时的开销和复杂性.
// 原型对象
class Shape {
constructor() {
this.type = '';
}
draw() {
console.log(`Drawing a ${this.type}`);
}
}
// Circle 类继承自原型对象
class Circle extends Shape {
constructor() {
super();
this.type = 'Circle';
}
}
// Rectangle 类继承自原型对象
class Rectangle extends Shape {
constructor() {
super();
this.type = 'Rectangle';
}
}
// 创建原型对象的实例
const circlePrototype = new Circle();
const rectanglePrototype = new Rectangle();
// 使用原型对象的实例创建新对象
const circle1 = Object.create(circlePrototype);
const circle2 = Object.create(circlePrototype);
const rectangle1 = Object.create(rectanglePrototype);
circle1.draw(); // 输出:Drawing a Circle
circle2.draw(); // 输出:Drawing a Circle
rectangle1.draw(); // 输出:Drawing a Rectangle
- 代理模式
定义: 可自定义控制对原对象的访问方式,并且允许在更新前后做一些额外处理
应用场景:监控,代理工具,前端框架实现等等
特点:- 控制访问: 代理对象可以控制客户端对目标对象的访问权限,限制或增加访问的方式。
- 封装: 代理可以隐藏目标对象的实现细节,客户端只需与代理进行交互,无需直接与目标对象打交道。
- 延迟加载: 代理可以在需要的时候才创建或初始化目标对象,实现延迟加载,提升性能。
- 缓存: 代理可以缓存目标对象的结果,以便重复的请求可以直接从缓存中获取,减少重复计算。
- 远程代理: 代理可以在不同的地址空间中代表目标对象,实现远程调用。
- 虚拟代理: 代理可以在创建开销较大的对象时充当占位符,等到实际需要时再创建真正的对象。
// 目标对象接口
class Image {
display() {
console.log('显示图片');
}
}
// 真实图片对象
class RealImage extends Image {
constructor(filename) {
super();
this.filename = filename;
this.loadFromDisk();
}
loadFromDisk() {
console.log('加载图片:', this.filename);
}
display() {
console.log('显示图片:', this.filename);
}
}
// 图片代理对象
class ProxyImage extends Image {
constructor(filename) {
super();
this.filename = filename;
this.realImage = null;
}
display() {
if (!this.realImage) {
this.realImage = new RealImage(this.filename);
}
this.realImage.display();
}
}
// 客户端代码
const image = new ProxyImage('image.jpg');
// 第一次显示图片,加载图片
image.display();
// 第二次显示图片,直接从缓存中显示
image.display();
- 迭代器模式
定义:在不暴露数据类型的情况下访问集合中的数据
应用场景:数据结构中有多种数据类型,列表,树等,提供通用操作接口。
特点:- 分离遍历和容器:迭代器将遍历操作与容器对象解耦,使得容器的内部结构对外部不可见,也不需要了解容器内部的实现细节。
- 统一的遍历接口:迭代器模式定义了统一的遍历接口,使得客户端代码可以一致地处理不同类型的容器对象。
- 支持多种遍历方式:不同类型的容器可以提供不同的迭代器,以支持不同的遍历方式,如正向遍历、逆向遍历等。
// 定义迭代器接口
class Iterator {
constructor(data) {
this.index = 0;
this.data = data;
}
hasNext() {}
next() {}
}
// 具体的迭代器实现
class ArrayIterator extends Iterator {
constructor(data) {
super(data);
}
hasNext() {
return this.index < this.data.length;
}
next() {
return this.data[this.index++];
}
}
// 容器对象
class MyArray {
constructor() {
this.items = [];
}
addItem(item) {
this.items.push(item);
}
getIterator() {
return new ArrayIterator(this.items);
}
}
// 使用迭代器遍历容器对象
const myArray = new MyArray();
myArray.addItem('Item 1');
myArray.addItem('Item 2');
myArray.addItem('Item 3');
const iterator = myArray.getIterator();
while (iterator.hasNext()) {
console.log(iterator.next());
}
前端框架中的设计模式(Vue, React)
-
代理模式
-
组合模式
定义:可多个对象组合使用,也可单个对象独立使用
应用场景:DOM、前端组件、文件按目录、部门
4. 总结
设计模式不是银弹
- 总结出抽象的模式相对比较简单,但是想要将抽象的模式套用到场景中却非常困难
- 现代编程语言的多编程范式带来的更多可能性真正优秀的- - 开源项目学习设计模式并不断实践
学习建议以及总结:对于设计模式这一块,可能用过。但是自己对设计模式概念的理解几乎为零。
所以,要好好熟悉设计模式概念,以及具体实现方式。不管怎样,起码先认识吧。再说之后……。
先了解一些平常容易遇见的。
不要急,慢慢来。