前端框架中,设计模式是一种解决常见问题的经验总结和行之有效的方法。设计模式帮助开发者构建可维护、可扩展、可复用的代码,并提高开发效率。 由于篇幅所限,本文仅介绍几种常见设计模式(前端框架以vue为例)。
1.单例模式
定义:一个类只有一个实例,且该类能自行创建这个实例的一种模式。
优点:确保一个类只有一个实例,节约了系统资源。
缺点:违背了单一职责原则,代码的可测试性较差。
使用案例:在前端框架中,可以用单例模式创建一个全局状态管理对象,对应于Vuex或Redux等状态管理库。
<template>
<div>
<p>Count: {{ $store.state.count }}</p>
<button @click="increment">Increment</button>
<button @click="decrement">Decrement</button>
</div>
</template>
<script>
export default {
methods: {
increment() {
this.$store.dispatch('increment');
},
decrement() {
this.$store.dispatch('decrement');
},
},
};
</script>
这里vue组件中通过this.$store来访问和修改全局的状态。
2.观察者模式
定义:指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。这种模式有时又称作发布订阅模式。
优点:可实现对象之间的松耦合,当一个对象状态发生变化时,其他对象能够自动更新。
缺点:如果观察者过多或者耗时操作过多,会导致性能问题。
使用案例:在前端框架中,可以用观察者模式实现事件机制,例如Vue中的响应式原理就是基于观察者模式。
<template>
<div>
<p>Counter: {{ counter }}</p>
<button @click="increment">Increment</button>
</div>
</template>
<script>
import { reactive, watch } from 'vue';
export default {
setup() {
const data = reactive({
counter: 0,
});
watch(() => data.counter, (newValue) => {
console.log('Counter changed:', newValue);
});
function increment() {
data.counter++;
}
return {
counter: data.counter,
increment,
};
},
};
</script>
以上代码中,使用Vue3提供的reactive和watch函数来实现观察者模式。通过reactive将data对象变成响应式的,然后使用watch监听data.counter的变化,并在变化时输出日志。
在模板中,可以直接引用counter变量和increment方法,当点击按钮时,increment方法会更新data.counter的值,触发watch中的回调函数。
通过观察者模式,Vue能够实时检测到数据的变化,并自动更新相关视图,实现了响应式原理。这使得开发者可以方便地编写数据驱动的代码,无需手动去更新视图。
3.工厂模式
定义:工厂模式定义创建对象的接口,但是让子类决定实例化哪个类。工厂方法将类的实例化延迟到子类。
优点:封装了对象的创建过程,提供了统一的接口来创建对象,易于扩展和维护。
缺点:增加了系统的复杂性,需要对对象的具体类型有一定的了解。
使用案例:在前端框架中,可以用工厂模式创建不同类型的组件,例如Vue中的组件工厂函数。
<template>
<div>
<button @click="createComponent">Create Component</button>
<div ref="componentContainer"></div>
</div>
</template>
<script>
import { createApp, h } from 'vue';
export default {
methods: {
createComponent() {
const app = createApp({
template: '<div>Dynamic Component</div>',
});
const componentInstance = app.mount(this.$refs.componentContainer);
},
},
};
</script>
在上述代码中,我们定义了一个按钮,当点击按钮时,调用createComponent方法。该方法创建了一个app实例,其中包含了一个简单的组件配置,该组件在template中定义为一个div,显示 "Dynamic Component" 字符串。
接下来,通过调用app.mount方法,将组件实例挂载到$refs.componentContainer元素中,使其显示在页面上。
通过组件工厂函数,我们可以动态地创建组件实例,这在需要根据不同情况生成具有特定配置的组件时非常有用。例如,可以根据用户的权限动态生成不同的菜单组件,或者在特定条件下动态生成不同样式的弹窗组件等。
4.代理模式
定义:它提供了一个代理对象来控制对另一个对象的访问。代理对象充当了被代理对象的接口,并且可以在不改变原有逻辑的情况下,加入额外的功能或控制对被代理对象的访问。
优点:
- 职责分离:代理对象负责控制和管理对目标对象的访问,实现了被代理对象和代理对象的分离。
- 增强原有功能:代理对象可以在原有逻辑的基础上添加额外的功能,比如性能优化、安全验证、缓存等。
- 控制对目标对象的访问:代理对象可以控制对目标对象的访问权限,只允许符合条件的访问请求。
- 提高性能:通过代理对象预处理耗时操作或延迟加载,可以提高系统性能。
缺点:
- 增加了系统的复杂性:引入代理对象会增加额外的类和逻辑,对于简单的场景可能会显得过于繁琐。
- 增加了系统的开销:代理模式需要维护额外的对象和方法调用,可能会引入一定的性能开销。
使用案例: 在前端框架中,可以使用代理模式创建一个全局状态管理对象,对应于Vuex或Redux等状态管理库。通过代理对象,可以控制对全局状态的访问,实现状态的集中管理和跨组件共享。代理对象可以添加额外的功能,比如在状态改变时进行通知或记录日志,从而增强全局状态的管理能力。
// 创建全局状态管理对象的代理
const stateProxy = {
state: {
count: 0,
},
// 读取全局状态的方法
getState() {
return this.state;
},
// 更新全局状态的方法
updateState(newState) {
this.state = { ...this.state, ...newState };
this.notifyObservers();
},
// 观察者模式,用于跨组件通信
observers: [],
addObserver(observer) {
this.observers.push(observer);
},
removeObserver(observer) {
this.observers = this.observers.filter((obs) => obs !== observer);
},
notifyObservers() {
this.observers.forEach((observer) => observer.update());
},
};
// 组件A
const componentA = {
update() {
const state = stateProxy.getState();
console.log('Component A received state update:', state);
}
};
// 组件B
const componentB = {
update() {
const state = stateProxy.getState();
console.log('Component B received state update:', state);
}
};
// 通过代理对象访问全局状态
stateProxy.addObserver(componentA);
stateProxy.addObserver(componentB);
// 更新全局状态
stateProxy.updateState({ count: 1 });
在上述案例中,首先创建了一个名为stateProxy的代理对象,它包含了全局状态的属性state、读取全局状态的方法getState()、更新全局状态的方法updateState(),以及用于跨组件通信的观察者模式相关方法。
然后,定义了两个组件componentA和componentB,它们都实现了update()方法,用于接收和处理全局状态的更新通知。
最后,通过代理对象stateProxy的addObserver()方法,将组件componentA和componentB添加为观察者。当调用stateProxy的updateState()方法更新全局状态时,观察者会收到相应的更新通知,并执行对应的update()方法来处理更新后的全局状态。
这样,通过代理对象stateProxy,可以实现全局状态的集中管理和跨组件共享,同时利用观察者模式来实现组件间的通信。这种代理模式的应用,类似于Vuex状态管理库在前端框架中的作用。
最后
引用一下课程ppt中的忠告:
设计模式不是"银弹" 总结出抽象的模式相对比较简单,但是想要将抽象的模式套 用到场景中却非常困难 现代编程语言的多编程范式带来的更多可能性 真正优秀的开源项目学习设计模式并不断实践