前端中的核心设计模式详解
1. 单例模式
单例模式确保一个类只有一个实例,并提供一个全局访问点。
基础实现
class Singleton {
private static instance: Singleton;
private constructor() {} // 私有构造函数
public static getInstance(): Singleton {
if (!Singleton.instance) {
Singleton.instance = new Singleton();
}
return Singleton.instance;
}
}
实际应用:全局状态管理
class Store {
private static instance: Store;
private state: Record<string, any> = {};
private constructor() {}
static getInstance(): Store {
if (!Store.instance) {
Store.instance = new Store();
}
return Store.instance;
}
setState(key: string, value: any) {
this.state[key] = value;
}
getState(key: string) {
return this.state[key];
}
}
// 使用示例
const store1 = Store.getInstance();
const store2 = Store.getInstance();
console.log(store1 === store2); // true
2. 发布订阅模式
发布订阅模式允许对象间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知。
基础实现
class EventEmitter {
private events: Map<string, Function[]> = new Map();
// 订阅
subscribe(event: string, callback: Function) {
if (!this.events.has(event)) {
this.events.set(event, []);
}
this.events.get(event)!.push(callback);
}
// 发布
publish(event: string, data?: any) {
if (this.events.has(event)) {
this.events.get(event)!.forEach(callback => callback(data));
}
}
// 取消订阅
unsubscribe(event: string, callback: Function) {
if (this.events.has(event)) {
const callbacks = this.events.get(event)!;
this.events.set(event, callbacks.filter(cb => cb !== callback));
}
}
}
实际应用:组件通信
// 创建事件总线
const eventBus = new EventEmitter();
// 组件A:发布消息
function ComponentA() {
const sendMessage = () => {
eventBus.publish('message', 'Hello from A!');
};
}
// 组件B:订阅消息
function ComponentB() {
useEffect(() => {
const handleMessage = (message: string) => {
console.log(message);
};
eventBus.subscribe('message', handleMessage);
return () => eventBus.unsubscribe('message', handleMessage);
}, []);
}
3. 原型模式
原型模式通过克隆现有对象来创建新对象,而不是通过实例化类。
基础实现
interface Cloneable {
clone(): this;
}
class Component implements Cloneable {
constructor(public name: string, public config: any) {}
clone(): this {
return Object.create(
Object.getPrototypeOf(this),
Object.getOwnPropertyDescriptors(this)
);
}
}
实际应用:组件复用
class UIComponent implements Cloneable {
constructor(
public type: string,
public styles: Record<string, string>,
public children: UIComponent[] = []
) {}
clone(): this {
const clone = Object.create(Object.getPrototypeOf(this));
clone.type = this.type;
clone.styles = { ...this.styles };
clone.children = this.children.map(child => child.clone());
return clone;
}
}
// 使用示例
const button = new UIComponent('button', { color: 'blue' });
const clonedButton = button.clone();
4. 代理模式(Proxy Pattern)
代理模式为其他对象提供一种代理以控制对这个对象的访问。
基础实现
// 使用ES6 Proxy
const handler = {
get(target: any, property: string) {
console.log(`访问属性: ${property}`);
return target[property];
},
set(target: any, property: string, value: any) {
console.log(`设置属性: ${property} = ${value}`);
target[property] = value;
return true;
}
};
const target = { name: 'target' };
const proxy = new Proxy(target, handler);
实际应用:数据响应式
function reactive<T extends object>(obj: T): T {
return new Proxy(obj, {
get(target, key) {
track(target, key); // 追踪依赖
return Reflect.get(target, key);
},
set(target, key, value) {
const result = Reflect.set(target, key, value);
trigger(target, key); // 触发更新
return result;
}
});
}
// 使用示例
const state = reactive({ count: 0 });
5. 迭代器模式
迭代器模式提供一种方法顺序访问一个聚合对象中的各个元素。
基础实现
class Iterator<T> {
private index = 0;
constructor(private items: T[]) {}
hasNext(): boolean {
return this.index < this.items.length;
}
next(): T | null {
return this.hasNext() ? this.items[this.index++] : null;
}
current(): T | null {
return this.items[this.index] || null;
}
reset() {
this.index = 0;
}
}
实际应用:虚拟列表
class VirtualList<T> {
private iterator: Iterator<T>;
constructor(private items: T[], private pageSize: number = 10) {
this.iterator = new Iterator(items);
}
getPage(): T[] {
const result: T[] = [];
let count = 0;
while (this.iterator.hasNext() && count < this.pageSize) {
const item = this.iterator.next();
if (item) {
result.push(item);
count++;
}
}
return result;
}
reset() {
this.iterator.reset();
}
}
// 使用示例
const list = new VirtualList([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 3);
console.log(list.getPage()); // [1, 2, 3]
console.log(list.getPage()); // [4, 5, 6]
6. 组合模式
组合模式允许你将对象组合成树形结构来表现"整体/部分"层次结构,使得客户以一致的方式处理单个对象和对象的组合。
基础实现
// 抽象组件
interface Component {
name: string;
add?(component: Component): void;
remove?(component: Component): void;
getChild?(index: number): Component | null;
operation(): void;
}
// 叶子节点
class Leaf implements Component {
constructor(public name: string) {}
operation(): void {
console.log(`Leaf ${this.name} operation`);
}
}
// 复合节点
class Composite implements Component {
private children: Component[] = [];
constructor(public name: string) {}
add(component: Component): void {
this.children.push(component);
}
remove(component: Component): void {
const index = this.children.indexOf(component);
if (index > -1) {
this.children.splice(index, 1);
}
}
getChild(index: number): Component | null {
return this.children[index] || null;
}
operation(): void {
console.log(`Composite ${this.name} operation`);
this.children.forEach(child => child.operation());
}
}
实际应用:UI组件树
// UI组件基类
interface UIComponent {
render(): void;
add?(child: UIComponent): void;
remove?(child: UIComponent): void;
getChildren?(): UIComponent[];
}
// 基础组件(叶子节点)
class Button implements UIComponent {
constructor(private text: string) {}
render(): void {
console.log(`Rendering Button: ${this.text}`);
}
}
class Input implements UIComponent {
constructor(private placeholder: string) {}
render(): void {
console.log(`Rendering Input: ${this.placeholder}`);
}
}
// 容器组件(复合节点)
class Form implements UIComponent {
private children: UIComponent[] = [];
constructor(private name: string) {}
add(child: UIComponent): void {
this.children.push(child);
}
remove(child: UIComponent): void {
const index = this.children.indexOf(child);
if (index > -1) {
this.children.splice(index, 1);
}
}
getChildren(): UIComponent[] {
return this.children;
}
render(): void {
console.log(`Rendering Form: ${this.name}`);
this.children.forEach(child => child.render());
}
}
// 使用示例
const loginForm = new Form('Login');
loginForm.add(new Input('Username'));
loginForm.add(new Input('Password'));
loginForm.add(new Button('Submit'));
loginForm.render();
// 输出:
// Rendering Form: Login
// Rendering Input: Username
// Rendering Input: Password
// Rendering Button: Submit
实际应用:虚拟DOM实现
interface VNode {
type: string;
props: Record<string, any>;
children: (VNode | string)[];
render(): void;
}
class VElement implements VNode {
constructor(
public type: string,
public props: Record<string, any>,
public children: (VNode | string)[] = []
) {}
render(): void {
console.log(`<${this.type} ${Object.entries(this.props).map(([key, value]) =>
`${key}="${value}"`).join(' ')}>`);
this.children.forEach(child => {
if (typeof child === 'string') {
console.log(child);
} else {
child.render();
}
});
console.log(`</${this.type}>`);
}
}
// 使用示例
const app = new VElement('div', { class: 'container' }, [
new VElement('h1', {}, ['Hello']),
new VElement('p', { class: 'content' }, [
'This is a ',
new VElement('strong', {}, ['composite']),
' pattern example'
])
]);
app.render();
组合模式在React中的应用
// React组件树示例
interface Props {
children?: React.ReactNode;
}
// 容器组件
const Container: React.FC<Props> = ({ children }) => {
return (
<div className="container">
{children}
</div>
);
};
// 复合组件
const Section: React.FC<Props & { title: string }> = ({ title, children }) => {
return (
<section>
<h2>{title}</h2>
{children}
</section>
);
};
// 使用示例
function App() {
return (
<Container>
<Section title="Section 1">
<p>Content 1</p>
</Section>
<Section title="Section 2">
<p>Content 2</p>
<Section title="Nested Section">
<p>Nested Content</p>
</Section>
</Section>
</Container>
);
}
组合模式的优缺点
优点:
- 简化了客户端代码,客户端可以一致地处理单个对象和组合对象
- 使得添加新类型的组件变得容易
- 提供了清晰的层次结构
缺点:
- 可能使设计变得太过一般化
- 在某些情况下很难限制组合中的组件类型
- 可能需要处理组件遍历的性能问题
总结
各模式的适用场景
- 单例模式:全局状态管理、配置管理
- 发布订阅模式:组件通信、事件处理
- 原型模式:对象复制、组件克隆
- 代理模式:数据响应式、权限控制
- 迭代器模式:数据遍历、分页处理
- 组合模式:构建组件树、处理层次结构