常见的六种设计模式

INFO

本文介绍了前端开发中常见的 8 种设计模式: 观察者模式单例模式工厂模式策略模式装饰者模式代理模式。通过代码示例详细解释了每种模式的实现和应用场景,帮助开发者理解如何在实际项目中应用这些设计模式。

一、观察者模式(observe)

INFO

定义:指定义了对象之间一对多的依赖关系。当一个对象的状态发生变化时,所有依赖于它的对象都会收到通知并自动更新。

案例: 实现一个事件总线(Event Bus),用于非父子组件间的消息传递(如用户登录状态更新通知)

// 创建事件总线 EventBus
class EventBus {
	private events = new Map<string, Function[]>();
	// 订阅
	on(eventName: string, callback: Function) {
		if (!this.events.has(eventName)) this.events.set(eventName, []);
		this.events.get(eventName)!.push(callback);
	}
	// 触发
	emit(eventName: string, ...args: any[]) {
		if (this.events.has(eventName)) {
			this.events.get(eventName)?.forEach(cb => cb(...args));
		} else {
			message.error(`事件${eventName}不存在`);
		}
	}
	// 销毁
	off(eventName: string, callback?: Function) {
		if (!callback) {
			this.events.delete(eventName);
		} else {
			const result = this.events.get(eventName)!.filter(cb => cb !== callback);
			this.events.set(eventName, result);
		}
	}
}
const bus = new EventBus(); // 使用示例:全局事件总线
// 组件A订阅事件 on Event
bus.on("user-login", user => {
	console.log("用户登录,更新头部信息", user);
});
// 组件B触发事件 emit Event
bus.emit("user-login", { name: "Alice", id: 123 });
import { Button, Flex, message } from "antd";
import { useEffect, useState } from "react";
import EventBus from "./EventBus"; // 上述的ts文件

// 使用示例:全局事件总线
const bus = new EventBus();
// 子组件的类型声明
interface Child1Props {
	acount: number;
	setAcount: React.Dispatch<React.SetStateAction<number>>;
}
interface Child2Props {
	setAcount: React.Dispatch<React.SetStateAction<number>>;
}

// 子组件 Child1
function Child1(props: Child1Props) {
	function addBusEvent(eveName: string, num: number) {
		bus.on(eveName, (data: any) => {
			console.log(`${num} 次添加事件:`, data);
		});
	}
	// 组件A订阅事件 login
	function addEvent() {
		addBusEvent("login", props.acount + 1);
		const result =
			props.acount === 0
				? "添加事件成功"
				: `${props.acount + 1} 次添加事件成功`;
		message.success(result);
		props.setAcount(props.acount + 1);
	}
	useEffect(() => {
		addEvent();
	}, []);
	return (
		<Flex align="center" gap={10}>
			<div>Child1</div>
			<Button onClick={addEvent}>添加事件</Button>
		</Flex>
	);
}

// 子组件 Child2
function Child2(props: Child2Props) {
	const trigger = () => {
		bus.emit("login", { name: "zhangsan", password: "123456" });
	};
	const cancel = () => {
		bus.off("login");
		props.setAcount(0);
	};
	return (
		<Flex align="center" gap={10}>
			<div>Child2</div>
			<Button onClick={trigger}>触发事件</Button>
			<Button onClick={cancel}>注销事件</Button>
		</Flex>
	);
}
export default function App() {
	const [acount, setAcount] = useState(0);
	return (
		<Flex gap={10} vertical>
			<Child1 acount={acount} setAcount={setAcount} />
			<Child2 setAcount={setAcount} />
		</Flex>
	);
}

二、单例模式(Singleton)

INFO

定义:指确保一个类只有一个实例,并提供一个全局访问点。

案例: 前端缓存管理器,确保整个应用共享同一缓存实例,禁止外部实例化

class CacheManager {
	private static instance: CacheManger;
	private cache = new Map<string, any>();
	// 禁止外部实例化 new
	private constructor() {}
	static getInstance(): CacheManager {
		if (!CacheManager.instance) {
			CacheManager.instance = new CacheManager();
		}
		return CacheManager.instance;
	}
	get(key: string): any {
		return this.cache.get(key);
	}
	set(key: string, value: any) {
		this.cache.set(key, value);
	}
}

// 使用示例:全局唯一缓存
const cache1 = CacheManager.getInstance();
const cache2 = CacheManager.getInstance();
console.log(cache1 === cache2); // true

三、工厂模式(Factory)

INFO

定义: 指通过提供一个创建对象的接口来创建对象,而无需指定具体的类。可以根据输入参数决定创建哪种类型的对象。

案例: 根据用户角色渲染不同的 UI 组件(小狗 vs 小猫)

// 定义typeScript接口
interface Animal {
	name: string;
	makeSound(): string;
}
class Dog implements Animal {
	name: string = "小狗🐶";
	makeSound(): string {
		return `我是${this.name}; 我会汪汪汪的叫`;
	}
}
class Cat implements Animal {
	name: string = "小猫🐱";
	makeSound(): string {
		return `我是${this.name}; 我会喵喵喵的叫`;
	}
}
// 抽象工厂模式
class AnimalFactory {
	static createAnimal(type: string): Animal {
		switch (type) {
			case "dog":
				return new Dog();
			case "cat":
				return new Cat();
			default:
				throw new Error("未知动物类型");
		}
	}
}

// test 测试
const dog = AnimalFactory.createAnimal("dog");
console.log(dog.makeSound()); // 我是小狗🐶; 我会汪汪汪的叫
const cat = AnimalFactory.createAnimal("cat");
console.log(cat.makeSound()); // 我是小猫🐱; 我会喵喵喵的叫

四、策略模式(Strategy)

INFO

定义:指定义了一系列算法,并将每一个算法封装起来,让它们可以互换。让算法的变化独立于使用算法的客户。

案例: 适用于不同场景下的不同策略选择,比如支付方式、排序算法等。

/*
const originalData = [5, 2, 8, 3, 9] // 原始数据
const context = new Context(动态策略, 原始数据); // 传入策略 && 原始数据
console.log(context.getResult()) // [2, 3, 5, 8, 9]
*/
const originalData = [5, 2, 8, 3, 9]; // 原始数据
type OriginalData = typeof originalData;
interface Strategy {
	sort(val: OriginalData): OriginalData;
}
// 目标类组件
class Context {
	private strategy: Strategy;
	private data: OriginalData;
	constructor(strategy: Strategy, data: OriginalData) {
		this.strategy = strategy;
		this.data = data;
	}
	getResult() {
		return this.strategy.sort(this.data);
	}
}
// 策略1 => 策略...
class Strategy1 implements Strategy {
	sort(data: OriginalData) {
		let result;
		// ... 策略逻辑
		result = data.sort((a, b) => a - b); // 策略1
		return result;
	}
}
const originalData = [5, 2, 8, 3, 9]; // 原始数据
const context = new Context(new Strategy1(), originalData);
console.log(context.getResult()); // [ 2, 3, 5, 8, 9 ]

五、装饰者模式(Decorator)

INFO

场景:在不修改对象结构的情况下,动态地为对象扩展功能

案例: 为 API 请求添加缓存、重试、日志功能

// 基础请求类
class ApiService {
	async fetchData(url: string) {
		const response = await fetch(url);
		return response.json();
	}
}
// 装饰器:缓存功能
class CachedApiService {
	private cache = new Map<string, any>();
	private service: ApiService;
	constructor(service: ApiService) {
		this.service = service;
	}
	async fetchData(url: string) {
		console.log(this.cache, "data");
		if (this.cache.has(url)) {
			return this.cache.get(url);
		}
		const data = await this.service.fetchData(url);
		this.cache.set(url, data);
		return data;
	}
}

// 测试
const cacheService = new CachedApiService(new ApiService());
cacheService.fetchData("http://jsonplaceholder.typicode.com/posts/3");

六、代理模式(Proxy)

INFO

场景:指为其他对象提供一个代理对象以控制对该对象的访问。它可以控制对真实对象的访问,适用于延迟加载、权限控制、缓存等场景。

案例: 适用于需要对某个对象的操作进行控制、保护或优化的情况,比如延迟加载、访问控制等。

class RealSubject {
	request() {
		console.log("RealSubject: Handling request");
	}
}

class Proxy {
	realSubject: RealSubject;
	constructor(realSubject: RealSubject) {
		this.realSubject = realSubject;
	}
	request() {
		console.log("Proxy: Delegating request to RealSubject");
		this.realSubject.request();
	}
}

// 测试
const realSubject = new RealSubject();
const proxy = new Proxy(realSubject);
proxy.request();

七、核心原则

INFO

  1. 避免过度设计: 优先使用简单代码实现功能,复杂场景再引入设计模式。
  2. 关注代码可读性: 模式是为了提升维护性,而非炫技。
  3. 结合框架特性: 如 React Hooks 可替代部分传统模式(如观察者模式可用 Context + useState 实现)。
上次更新 2025/3/19 16:57:59