前言
在前端开发中,代码谁都会写,有时候你可能知道这么写,但是你不知道为什么这么写及这么写的意义是什么。今天有摸鱼事件,来补补设计模式相关的知识点,让自己更深刻地认知设计模式并记录分享出来,让自己的开发思路更清晰。免得以后面试官问:“你知道设计模式吗?用过哪些设计模式?”,我:“我会写但我说不出来”,面试官:“回去等通知吧”
设计模式的分类
设计模式是一种解决软件开发中常见问题的通用方法,它可以帮助我们提高代码的可读性、可维护性和可扩展性。设计模式有很多种类,根据不同的目的和场景,可以分为创建型、结构型和行为型三大类
创建型
-
单例模式
创建型模式主要关注对象的创建方式,当你需要保证一个类只有一个实例,并且提供一个全局访问点时,可以使用单例模式。例如,如果你需要管理一个全局的状态对象,或者一个全局的配置对象,或者一个全局的事件总线对象,你可以使用单例模式来实现。
// 定义一个单例类 class Singleton { constructor(name) { this.name = name; } // 定义一个静态方法获取实例 static getInstance(name) { // 如果实例不存在,则创建一个新的实例 if (!this.instance) { this.instance = new Singleton(name); } // 返回实例 return this.instance; } } // 测试 const a = Singleton.getInstance('a'); const b = Singleton.getInstance('b'); console.log(a === b); // true console.log(a.name); // a console.log(b.name); // a
-
工厂模式
// 定义一个工厂类
class Factory {
// 定义一个静态方法创建对象
static create(type) {
// 根据不同类型返回不同对象
switch (type) {
case 'A':
return new A();
case 'B':
return new B();
default:
return null;
}
}
}
// 定义两个不同类型的类
class A {
constructor() {
this.name = 'A';
}
}
class B {
constructor() {
this.name = 'B';
}
}
// 测试
const a = Factory.create('A');
const b = Factory.create('B');
console.log(a instanceof A); // true
console.log(b instanceof B); // true
console.log(a.name); // A
console.log(b.name); // B
- 建造者模式
建造者模式是一种将一个复杂对象的构建过程和表示过程分离,使得同样的构建过程可以创建不同的表示的设计模式。在前端开发中,我们经常会遇到一些需要创建复杂对象的场景,比如创建一个复杂的表单组件,或者创建一个复杂的图表组件。使用建造者模式可以将复杂对象的构建过程封装在一个类中,提供一些方法来设置不同的属性和配置,然后返回最终的对象。这样可以避免直接使用构造函数或者对象字面量来创建对象,提高代码的可读性和可维护性。当然了,我强烈建议这种写法用tsx来写!用react也好用vue3+tsx都可以实现。
// 定义一个表单组件类
class Form {
constructor() {
this.fields = []; // 表单字段列表
this.title = ''; // 表单标题
this.submitText = ''; // 提交按钮文本
}
// 定义一个渲染方法
render() {
// 返回一个vue组件对象
return {
template: `
<div>
<h1>{{title}}</h1>
<div v-for="field in fields">
<label>{{field.label}}</label>
<input :type="field.type" :name="field.name" />
</div>
<button>{{submitText}}</button>
</div>
`,
data() {
return {
title: this.title,
fields: this.fields,
submitText: this.submitText,
};
},
};
}
}
// 定义一个表单组件建造者类
class FormBuilder {
constructor() {
this.form = new Form(); // 创建一个新的表单组件对象
}
// 定义一个添加字段的方法
addField(field) {
this.form.fields.push(field); // 将字段添加到表单字段列表中
return this; // 返回当前建造者对象,实现链式调用
}
// 定义一个设置标题的方法
setTitle(title) {
this.form.title = title; // 设置表单标题
return this; // 返回当前建造者对象,实现链式调用
}
// 定义一个设置提交按钮文本的方法
setSubmitText(text) {
this.form.submitText = text; // 设置提交按钮文本
return this; // 返回当前建造者对象,实现链式调用
}
// 定义一个获取最终表单组件对象的方法
build() {
return this.form.render(); // 返回最终的表单组件对象
}
}
// 测试
const formBuilder = new FormBuilder();
const form = formBuilder.setTitle('注册表单')
.addField({ label: '用户名', type: 'text', name: 'username' })
.addField({ label: '密码', type: 'password', name: 'password' })
.addField({ label: '邮箱', type: 'email', name: 'email' })
.setSubmitText('注册')
.build();
new Vue({
el: '#app',
components: {
form,
},
});
结构型
- 适配器模式
适配器模式是一种将一个类或者对象的接口转换成另一个接口,使原本不兼容的类或者对象可以协同工作的设计模式。在前端开发中,我们经常会遇到一些需要对接不同接口或者数据格式的场景,比如调用不同来源的 API ,或者处理不同格式的数据。使用适配器模式可以避免修改原有的类或者对象,而是通过一个中间层来实现接口或者数据的转换,提高代码的可复用性和可扩展性。
// 定义一个适配器类
class Adapter {
constructor(adaptee) {
this.adaptee = adaptee;
}
// 定义一个适配方法
getData() {
// 将适配对象的数据转换成目标格式
const data = this.adaptee.data.map(item => ({
id: item._id,
name: item._name,
value: item._value,
}));
// 返回目标对象
return data
}
}
// 定义一个适配对象
const adaptee = {
data: [
{ _id: '1', _name: 'a', _value: '10' },
{ _id: '2', _name: 'b', _value: '20' },
{ _id: '3', _name: 'c', _value: '30' },
],
};
// 测试
const adapter = new Adapter(adaptee);
console.log(adapter.getData()); // [{ id: '1', name: 'a', value: '10' }, { id: '2', name: 'b', value: '20' }, { id: '3', name: 'c', value: '30' }]
- 装饰器模式
装饰模式是一种动态地给对象增加一些职责,即增加其额外的功能的设计模式。它可以让我们在不修改原有对象的基础上,通过创建一个装饰类来包装原有对象,从而实现对原有对象的功能扩展或者修改。好处就是不需要修改原对象即可实现对原有对象的功能扩展或者修改。
// 定义一个抽象组件类
class Component {
// 定义一个抽象方法
operation() {
throw new Error('Abstract method');
}
}
// 定义一个具体组件类
class ConcreteComponent extends Component {
// 实现抽象方法
operation() {
console.log('具体组件的操作');
}
}
// 定义一个抽象装饰类
class Decorator extends Component {
constructor(component) {
super();
// 持有一个组件对象的引用
this.component = component;
}
// 实现抽象方法
operation() {
// 调用组件对象的方法
this.component.operation();
}
}
// 定义一个具体装饰类A
class ConcreteDecoratorA extends Decorator {
// 实现抽象方法
operation() {
// 调用父类的方法
super.operation();
// 执行装饰器自己的操作
console.log('装饰器A的操作');
}
}
// 定义一个具体装饰类B
class ConcreteDecoratorB extends Decorator {
// 实现抽象方法
operation() {
// 调用父类的方法
super.operation();
// 执行装饰器自己的操作
console.log('装饰器B的操作');
}
}
// 测试代码
const c = new ConcreteComponent();
const d1 = new ConcreteDecoratorA(c);
const d2 = new ConcreteDecoratorB(d1);
d2.operation();
行为型
- 观察者模式
观察者模式是一种定义了一对多的依赖关系,当一个对象(被观察者)的状态发生变化时,所有依赖它的对象(观察者)都会得到通知并自动更新的设计模式。在前端开发中,我们经常会遇到一些需要实现事件驱动或者数据绑定的场景,比如实现一个自定义事件系统,或者实现一个简单的 MVVM 框架。使用观察者模式可以实现对象之间的松耦合,提高代码的可扩展性和可测试性。vue中的双向绑定就是一个观察者模式,先对dom使用vmodel命令,在发生改变的时候,通知父组件修改绑定的值
// 定义一个被观察者类
class Subject {
constructor() {
this.state = null; // 被观察者的状态
this.observers = []; // 观察者列表
}
// 添加观察者方法
attach(observer) {
this.observers.push(observer);
}
// 移除观察者方法
detach(observer) {
const index = this.observers.indexOf(observer);
if (index !== -1) {
this.observers.splice(index, 1);
}
}
// 更新状态方法
setState(state) {
this.state = state;
this.notify();
}
// 通知观察者方法
notify() {
for (let observer of this.observers) {
observer.update(this.state);
}
}
}
// 定义一个观察者类
class Observer {
constructor(name) {
this.name = name;
}
// 更新方法
update(state) {
console.log(`${this.name} received ${state}`);
}
}
// 测试
const subject = new Subject();
const observer1 = new Observer('observer1');
const observer2 = new Observer('observer2');
subject.attach(observer1);
subject.attach(observer2);
subject.setState('hello');
subject.detach(observer1);
subject.setState('world');
总结
合理的利用设计模式能提高我们代码的可读性,也能让我们的开发思路更清晰,所以好好吃透这些设计模式,对我们自身是有极大好处的