概述
设计模式作为软件工程中解决特定问题的经典方案,虽然设计模式很多,对于前端开发来说,实际业务工程项目中常用的就那么几个,见下。
1. 单例模式 (Singleton Pattern)
确保一个类只有一个实例,并提供全局访问点。
应用场景:全局状态管理、模态框、提示组件等
javascript
class AuthService {
constructor() {
if (AuthService.instance) {
return AuthService.instance;
}
this.token = null;
this.user = null;
AuthService.instance = this;
}
login(token, user) {
this.token = token;
this.user = user;
}
logout() {
this.token = null;
this.user = null;
}
isAuthenticated() {
return !!this.token;
}
}
// 使用示例
const auth1 = new AuthService();
const auth2 = new AuthService();
console.log(auth1 === auth2); // true
auth1.login('token123', { name: 'John' });
console.log(auth2.isAuthenticated()); // true
2. 发布-订阅模式
观察者模式的变体,通过消息通道进行通信,发布者和订阅者不需要知道彼此的存在。
javascript
class PubSub {
constructor() {
this.topics = {};
this.subId = 0;
}
subscribe(topic, callback) {
if (!this.topics[topic]) {
this.topics[topic] = {};
}
const id = ++this.subId;
this.topics[topic][id] = callback;
return {
unsubscribe: () => {
delete this.topics[topic][id];
if (Object.keys(this.topics[topic]).length === 0) {
delete this.topics[topic];
}
}
};
}
publish(topic, data) {
if (!this.topics[topic]) return;
Object.values(this.topics[topic]).forEach(callback => {
callback(data);
});
}
}
// 使用示例
const pubsub = new PubSub();
// 订阅
const subscription = pubsub.subscribe('user.login', (user) => {
console.log('用户登录:', user);
});
// 发布
pubsub.publish('user.login', { name: 'Alice', id: 1 });
// 取消订阅
subscription.unsubscribe();
3. 工厂模式
创建对象而不暴露创建逻辑,通过工厂方法来创建对象。
应用场景:复杂对象创建、根据不同条件创建不同实例
javascript
class Dialog {
constructor(type, content) {
this.type = type;
this.content = content;
}
show() {
console.log(`显示${this.type}对话框: ${this.content}`);
}
}
class DialogFactory {
static createDialog(type, content) {
switch (type) {
case 'success':
return new Dialog('成功', content);
case 'error':
return new Dialog('错误', content);
case 'warning':
return new Dialog('警告', content);
default:
throw new Error('未知的对话框类型');
}
}
}
// 使用示例
const successDialog = DialogFactory.createDialog('success', '操作成功!');
const errorDialog = DialogFactory.createDialog('error', '操作失败!');
successDialog.show(); // 显示成功对话框: 操作成功!
errorDialog.show(); // 显示错误对话框: 操作失败!
4. 策略模式
定义一系列算法,将它们封装起来,并且使它们可以相互替换。
应用场景:表单验证、支付方式等
javascript
// 策略类
const validationStrategies = {
isRequired: (value) => ({
isValid: value !== undefined && value !== null && value !== '',
message: '该字段为必填项'
}),
isEmail: (value) => ({
isValid: /^[^\s@]+@[^\s@]+.[^\s@]+$/.test(value),
message: '请输入有效的邮箱地址'
}),
minLength: (value, length) => ({
isValid: value.length >= length,
message: `长度不能少于${length}个字符`
}),
maxLength: (value, length) => ({
isValid: value.length <= length,
message: `长度不能超过${length}个字符`
})
};
// 验证器类
class Validator {
constructor() {
this.rules = [];
}
addRule(field, strategy, ...params) {
this.rules.push({ field, strategy, params });
return this;
}
validate(data) {
const errors = {};
this.rules.forEach(rule => {
const value = data[rule.field];
const result = validationStrategies[rule.strategy](value, ...rule.params);
if (!result.isValid) {
if (!errors[rule.field]) {
errors[rule.field] = [];
}
errors[rule.field].push(result.message);
}
});
return {
isValid: Object.keys(errors).length === 0,
errors
};
}
}
// 使用示例
const validator = new Validator()
.addRule('username', 'isRequired')
.addRule('username', 'minLength', 3)
.addRule('email', 'isRequired')
.addRule('email', 'isEmail');
const data = {
username: 'ab',
email: 'invalid-email'
};
const result = validator.validate(data);
console.log(result);
5. 装饰器模式
动态地给对象添加额外的职责,而不改变对象本身。
应用场景:功能扩展、高阶组件、vue2类组件。
javascript
// 基础组件
class Component {
render() {
return '基础组件';
}
}
// 装饰器基类
class Decorator {
constructor(component) {
this.component = component;
}
render() {
return this.component.render();
}
}
// 具体装饰器
class BorderDecorator extends Decorator {
render() {
return `带有边框的(${super.render()})`;
}
}
class ColorDecorator extends Decorator {
constructor(component, color) {
super(component);
this.color = color;
}
render() {
return `<span style="color: ${this.color}">${super.render()}</span>`;
}
}
// 使用示例
let component = new Component();
component = new BorderDecorator(component);
component = new ColorDecorator(component, 'red');
console.log(component.render());
// <span style="color: red">带有边框的(基础组件)</span>
6. 适配器模式
将一个类的接口转换成客户希望的另一个接口。
应用场景:API兼容、第三方库适配、数据格式转换
javascript
// 老版本API
class OldAPI {
request(data) {
return `老API响应: ${JSON.stringify(data)}`;
}
}
// 新版本API(期望的接口)
class NewAPI {
fetch(options) {
return `新API响应: ${options.url} - ${JSON.stringify(options.body)}`;
}
}
// 适配器
class APIAdapter {
constructor(oldAPI) {
this.oldAPI = oldAPI;
}
fetch(options) {
// 将新API的调用方式适配到老API
const oldStyleData = {
method: options.method || 'GET',
path: options.url,
body: options.body
};
return this.oldAPI.request(oldStyleData);
}
}
// 使用示例
const oldAPI = new OldAPI();
const adapter = new APIAdapter(oldAPI);
// 使用新API的接口调用老API的功能
const result = adapter.fetch({
url: '/api/users',
method: 'POST',
body: { name: 'John' }
});
console.log(result);
7. 代理模式
为其他对象提供一种代理以控制对这个对象的访问。
应用场景:缓存代理、权限控制、虚拟代理、数据验证
javascript
// 真实服务
class ImageService {
constructor(filename) {
this.filename = filename;
this.loadImage(); // 模拟耗时的图片加载
}
loadImage() {
console.log(`加载图片: ${this.filename}`);
}
display() {
console.log(`显示图片: ${this.filename}`);
}
}
// 代理
class ImageProxy {
constructor(filename) {
this.filename = filename;
this.realService = null;
}
display() {
if (!this.realService) {
this.realService = new ImageService(this.filename);
}
this.realService.display();
}
}
// 使用示例
const image1 = new ImageProxy('photo1.jpg');
const image2 = new ImageProxy('photo2.jpg');
// 只有在真正需要显示时才加载图片
image1.display(); // 第一次调用会加载图片
image1.display(); // 第二次直接使用已加载的图片