常见的六种设计模式
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
- 避免过度设计: 优先使用简单代码实现功能,复杂场景再引入设计模式。
- 关注代码可读性: 模式是为了提升维护性,而非炫技。
- 结合框架特性: 如 React Hooks 可替代部分传统模式(如观察者模式可用 Context + useState 实现)。