设计模式详解
约 2314 字大约 8 分钟
设计模式详解
创建型模式 🟢
1. 单例模式
单例模式确保一个类只有一个实例,并提供一个全局访问点。在前端开发中,常用于全局状态管理、缓存、全局配置等场景。
1.1 基本实现
- ES5实现:
// ES5单例模式
var Singleton = (function() {
var instance;
function createInstance() {
// 私有变量和方法
var privateVariable = 'private';
function privateMethod() {
console.log('private method');
}
return {
// 公共接口
publicMethod: function() {
console.log('public method');
privateMethod();
},
getPrivateVariable: function() {
return privateVariable;
}
};
}
return {
getInstance: function() {
if (!instance) {
instance = createInstance();
}
return instance;
}
};
})();
- ES6实现:
// ES6单例模式
class Singleton {
constructor() {
if (!Singleton.instance) {
// 初始化代码
this.config = {};
this.data = new Map();
Singleton.instance = this;
}
return Singleton.instance;
}
// 实例方法
setConfig(key, value) {
this.config[key] = value;
}
getConfig(key) {
return this.config[key];
}
// 静态方法
static getInstance() {
if (!Singleton.instance) {
Singleton.instance = new Singleton();
}
return Singleton.instance;
}
}
// 防止实例化
Object.freeze(Singleton);
1.2 实际应用场景
- 全局状态管理:
// 全局状态管理器
class Store {
constructor() {
if (Store.instance) {
return Store.instance;
}
this.state = {};
this.subscribers = new Set();
Store.instance = this;
}
setState(newState) {
this.state = { ...this.state, ...newState };
this.notify();
}
getState() {
return { ...this.state };
}
subscribe(callback) {
this.subscribers.add(callback);
return () => this.subscribers.delete(callback);
}
notify() {
this.subscribers.forEach(callback => callback(this.state));
}
}
// 使用示例
const store = new Store();
const unsubscribe = store.subscribe(state => {
console.log('State updated:', state);
});
- 缓存管理:
class CacheManager {
constructor() {
if (CacheManager.instance) {
return CacheManager.instance;
}
this.cache = new Map();
this.maxSize = 100;
CacheManager.instance = this;
}
set(key, value, ttl = 3600) {
if (this.cache.size >= this.maxSize) {
const oldestKey = this.cache.keys().next().value;
this.cache.delete(oldestKey);
}
this.cache.set(key, {
value,
expiry: Date.now() + ttl * 1000
});
}
get(key) {
const item = this.cache.get(key);
if (!item) return null;
if (item.expiry < Date.now()) {
this.cache.delete(key);
return null;
}
return item.value;
}
}
2. 工厂模式
工厂模式用于创建对象的接口,让子类决定实例化哪个类。主要解决对象创建时的耦合问题。
2.1 简单工厂
// 简单工厂模式
class UserFactory {
static createUser(type) {
switch (type) {
case 'admin':
return new AdminUser();
case 'regular':
return new RegularUser();
case 'guest':
return new GuestUser();
default:
throw new Error('Invalid user type');
}
}
}
// 用户类型
class AdminUser {
constructor() {
this.type = 'admin';
this.permissions = ['read', 'write', 'delete'];
}
}
class RegularUser {
constructor() {
this.type = 'regular';
this.permissions = ['read', 'write'];
}
}
class GuestUser {
constructor() {
this.type = 'guest';
this.permissions = ['read'];
}
}
2.2 抽象工厂
// 抽象工厂模式
class UIFactory {
createButton() {
throw new Error('Method not implemented');
}
createInput() {
throw new Error('Method not implemented');
}
createDialog() {
throw new Error('Method not implemented');
}
}
// 具体工厂 - Material Design
class MaterialUIFactory extends UIFactory {
createButton() {
return new MaterialButton();
}
createInput() {
return new MaterialInput();
}
createDialog() {
return new MaterialDialog();
}
}
// 具体工厂 - iOS Style
class IOSUIFactory extends UIFactory {
createButton() {
return new IOSButton();
}
createInput() {
return new IOSInput();
}
createDialog() {
return new IOSDialog();
}
}
// 使用示例
class UIManager {
constructor(factory) {
this.factory = factory;
}
createUI() {
const button = this.factory.createButton();
const input = this.factory.createInput();
const dialog = this.factory.createDialog();
return { button, input, dialog };
}
}
3. 建造者模式
建造者模式用于创建复杂对象,允许按步骤构建最终对象。适用于对象构建过程复杂的场景。
// 表单构建器示例
class FormBuilder {
constructor() {
this.form = {};
}
addInput(name, type = 'text') {
this.form[name] = {
type,
value: '',
validators: []
};
return this;
}
addValidation(name, validator) {
if (this.form[name]) {
this.form[name].validators.push(validator);
}
return this;
}
addSubmitHandler(handler) {
this.submitHandler = handler;
return this;
}
build() {
const form = document.createElement('form');
// 创建表单元素
Object.entries(this.form).forEach(([name, config]) => {
const input = document.createElement('input');
input.type = config.type;
input.name = name;
// 添加验证
input.addEventListener('input', (e) => {
const value = e.target.value;
const errors = config.validators
.map(validator => validator(value))
.filter(Boolean);
if (errors.length > 0) {
input.setCustomValidity(errors[0]);
} else {
input.setCustomValidity('');
}
});
form.appendChild(input);
});
// 添加提交处理
if (this.submitHandler) {
form.addEventListener('submit', (e) => {
e.preventDefault();
const formData = new FormData(form);
this.submitHandler(Object.fromEntries(formData));
});
}
return form;
}
}
// 使用示例
const loginForm = new FormBuilder()
.addInput('username')
.addValidation('username', value => {
if (value.length < 3) return '用户名至少3个字符';
})
.addInput('password', 'password')
.addValidation('password', value => {
if (value.length < 6) return '密码至少6个字符';
})
.addSubmitHandler(data => {
console.log('Form submitted:', data);
})
.build();
结构型模式 🟡
1. 适配器模式
适配器模式用于将一个类的接口转换成客户希望的另外一个接口,使原本由于接口不兼容而不能一起工作的类可以一起工作。
// 旧接口
class OldAPI {
getUsers() {
return {
code: 200,
data: [
{ id: 1, name: 'John' },
{ id: 2, name: 'Jane' }
]
};
}
}
// 新接口期望的格式
class NewAPI {
async fetchUsers() {
try {
const response = await fetch('/api/users');
return await response.json();
} catch (error) {
throw new Error('Failed to fetch users');
}
}
}
// 适配器
class APIAdapter {
constructor(oldAPI) {
this.oldAPI = oldAPI;
}
async fetchUsers() {
const result = this.oldAPI.getUsers();
// 转换为新格式
return {
success: result.code === 200,
users: result.data,
timestamp: Date.now()
};
}
}
// 使用示例
const oldAPI = new OldAPI();
const adapter = new APIAdapter(oldAPI);
// 现在可以像使用新API一样使用旧API
adapter.fetchUsers().then(result => {
console.log(result.users);
});
2. 装饰器模式
装饰器模式动态地给对象添加额外的职责,是继承关系的一个替代方案。
// 基础组件装饰器
class Component {
operation() {
return 'Component';
}
}
// 装饰器基类
class Decorator {
constructor(component) {
this.component = component;
}
operation() {
return this.component.operation();
}
}
// 具体装饰器
class LoggingDecorator extends Decorator {
operation() {
console.log('Before operation');
const result = super.operation();
console.log('After operation');
return result;
}
}
class CachingDecorator extends Decorator {
constructor(component) {
super(component);
this.cache = new Map();
}
operation() {
const key = JSON.stringify(arguments);
if (this.cache.has(key)) {
return this.cache.get(key);
}
const result = super.operation();
this.cache.set(key, result);
return result;
}
}
// ES7装饰器语法
function log(target, name, descriptor) {
const original = descriptor.value;
descriptor.value = function(...args) {
console.log(`Calling ${name} with:`, args);
const result = original.apply(this, args);
console.log(`Result:`, result);
return result;
};
return descriptor;
}
class Example {
@log
multiply(a, b) {
return a * b;
}
}
3. 代理模式
代理模式为其他对象提供一种代理以控制对这个对象的访问。
// 虚拟代理 - 图片懒加载
class ImageProxy {
constructor(targetUrl) {
this.targetUrl = targetUrl;
this.image = null;
}
display(element) {
// 显示占位图
element.src = 'placeholder.jpg';
// 懒加载实际图片
if (!this.image) {
this.image = new Image();
this.image.onload = () => {
element.src = this.targetUrl;
};
this.image.src = this.targetUrl;
}
}
}
// 保护代理 - 访问控制
class APIProxy {
constructor(api) {
this.api = api;
}
async request(method, url, data) {
// 检查权限
if (!this.checkPermission()) {
throw new Error('Unauthorized');
}
// 添加认证信息
const headers = {
'Authorization': `Bearer ${this.getToken()}`
};
// 调用实际API
return this.api.request(method, url, data, headers);
}
checkPermission() {
// 权限检查逻辑
return true;
}
getToken() {
// 获取认证token
return localStorage.getItem('token');
}
}
// 缓存代理 - 结果缓存
class ComputeProxy {
constructor(target) {
this.target = target;
this.cache = new Map();
}
compute(args) {
const key = JSON.stringify(args);
if (this.cache.has(key)) {
console.log('Using cached result');
return this.cache.get(key);
}
const result = this.target.compute(args);
this.cache.set(key, result);
return result;
}
}
行为型模式 🔴
1. 观察者模式
观察者模式定义了对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
// 事件发布订阅系统
class EventEmitter {
constructor() {
this.events = new Map();
}
// 订阅事件
on(event, callback, options = {}) {
if (!this.events.has(event)) {
this.events.set(event, new Set());
}
const listener = {
callback,
options,
once: options.once || false
};
this.events.get(event).add(listener);
// 返回取消订阅函数
return () => {
this.events.get(event).delete(listener);
};
}
// 一次性订阅
once(event, callback) {
return this.on(event, callback, { once: true });
}
// 发布事件
emit(event, ...args) {
if (!this.events.has(event)) return false;
const listeners = this.events.get(event);
const removeList = new Set();
for (const listener of listeners) {
listener.callback.apply(this, args);
if (listener.once) {
removeList.add(listener);
}
}
// 移除一次性监听器
removeList.forEach(listener => {
listeners.delete(listener);
});
return true;
}
// 移除事件监听
off(event, callback) {
if (!this.events.has(event)) return false;
if (!callback) {
// 移除所有监听器
return this.events.delete(event);
}
const listeners = this.events.get(event);
for (const listener of listeners) {
if (listener.callback === callback) {
listeners.delete(listener);
break;
}
}
return true;
}
}
// 使用示例
class UserService extends EventEmitter {
constructor() {
super();
this.users = new Map();
}
addUser(user) {
this.users.set(user.id, user);
this.emit('userAdded', user);
}
updateUser(id, updates) {
const user = this.users.get(id);
if (user) {
Object.assign(user, updates);
this.emit('userUpdated', user);
}
}
deleteUser(id) {
const user = this.users.get(id);
if (user) {
this.users.delete(id);
this.emit('userDeleted', user);
}
}
}
2. 策略模式
策略模式定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
// 表单验证策略
class FormValidator {
constructor() {
this.validationRules = new Map();
}
// 添加验证规则
addRule(field, rule) {
if (!this.validationRules.has(field)) {
this.validationRules.set(field, []);
}
this.validationRules.get(field).push(rule);
}
// 验证表单
validate(formData) {
const errors = new Map();
for (const [field, rules] of this.validationRules) {
const value = formData[field];
const fieldErrors = [];
for (const rule of rules) {
const error = rule.validate(value);
if (error) {
fieldErrors.push(error);
}
}
if (fieldErrors.length > 0) {
errors.set(field, fieldErrors);
}
}
return errors;
}
}
// 验证规则
class RequiredRule {
validate(value) {
if (!value || value.trim() === '') {
return '此字段是必填的';
}
}
}
class MinLengthRule {
constructor(length) {
this.length = length;
}
validate(value) {
if (value && value.length < this.length) {
return `最小长度为${this.length}个字符`;
}
}
}
class EmailRule {
validate(value) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (value && !emailRegex.test(value)) {
return '请输入有效的邮箱地址';
}
}
}
// 使用示例
const validator = new FormValidator();
validator.addRule('username', new RequiredRule());
validator.addRule('username', new MinLengthRule(3));
validator.addRule('email', new RequiredRule());
validator.addRule('email', new EmailRule());
const formData = {
username: 'Jo',
email: 'invalid-email'
};
const errors = validator.validate(formData);
console.log(errors);
3. 命令模式
命令模式将请求封装成对象,从而使你可用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。
// 命令接口
class Command {
execute() {
throw new Error('Method not implemented');
}
undo() {
throw new Error('Method not implemented');
}
}
// 具体命令
class AddTodoCommand extends Command {
constructor(todoList, text) {
super();
this.todoList = todoList;
this.text = text;
this.id = null;
}
execute() {
this.id = this.todoList.add(this.text);
return this.id;
}
undo() {
if (this.id !== null) {
this.todoList.remove(this.id);
this.id = null;
}
}
}
class UpdateTodoCommand extends Command {
constructor(todoList, id, updates) {
super();
this.todoList = todoList;
this.id = id;
this.updates = updates;
this.oldData = null;
}
execute() {
this.oldData = this.todoList.get(this.id);
this.todoList.update(this.id, this.updates);
}
undo() {
if (this.oldData) {
this.todoList.update(this.id, this.oldData);
}
}
}
// 命令管理器
class CommandManager {
constructor() {
this.history = [];
this.current = -1;
}
execute(command) {
// 执行新命令时,清除当前位置之后的历史
this.history = this.history.slice(0, this.current + 1);
// 执行命令
command.execute();
// 添加到历史
this.history.push(command);
this.current++;
}
undo() {
if (this.current >= 0) {
const command = this.history[this.current];
command.undo();
this.current--;
return true;
}
return false;
}
redo() {
if (this.current < this.history.length - 1) {
this.current++;
const command = this.history[this.current];
command.execute();
return true;
}
return false;
}
}
// 使用示例
class TodoList {
constructor() {
this.todos = new Map();
this.nextId = 1;
}
add(text) {
const id = this.nextId++;
this.todos.set(id, { id, text, completed: false });
return id;
}
remove(id) {
return this.todos.delete(id);
}
update(id, updates) {
const todo = this.todos.get(id);
if (todo) {
Object.assign(todo, updates);
return true;
}
return false;
}
get(id) {
return { ...this.todos.get(id) };
}
}
// 客户端代码
const todoList = new TodoList();
const commandManager = new CommandManager();
// 执行命令
const addCommand = new AddTodoCommand(todoList, '学习设计模式');
commandManager.execute(addCommand);
const updateCommand = new UpdateTodoCommand(todoList, 1, { completed: true });
commandManager.execute(updateCommand);
// 撤销和重做
commandManager.undo(); // 撤销更新
commandManager.undo(); // 撤销添加
commandManager.redo(); // 重做添加
通过以上内容,我们详细介绍了前端开发中常用的设计模式,包括:
- 创建型模式:单例模式、工厂模式、建造者模式
- 结构型模式:适配器模式、装饰器模式、代理模式
- 行为型模式:观察者模式、策略模式、命令模式
每个模式都包含了详细的实现代码和使用示例,帮助开发者更好地理解和应用这些设计模式。这些模式可以帮助我们写出更加可维护、可扩展的代码。