前端策略模式
设计模式
单例模式
单例模式确保一个类只有一个实例,并提供一个全局访问点
class Singleton {
constructor(name) {
if (!Singleton.instance) {
this.name = name;
Singleton.instance = this;
}
return Singleton.instance;
}
static getInstance(name) {
if (!Singleton.instance) {
Singleton.instance = new Singleton(name);
}
return Singleton.instance;
}
sayHello() {
console.log(`Hello, I am ${this.name}`);
}
}
const instance1 = Singleton.getInstance("Instance 1");
const instance2 = Singleton.getInstance("Instance 2");
instance1.sayHello(); // Hello, I am Instance 1
instance2.sayHello(); // Hello, I am Instance 1
实际使用案例
假设我们有一个前端项目,需要在多个组件之间共享一些全局配置(如 API 基础 URL、用户信息等)。我们可以使用单例模式来管理这些配置
class ConfigManager {
constructor() {
if (!ConfigManager.instance) {
this.config = {
apiUrl: "https://api.example.com",
userId: null,
// 其他配置项
isLoggedIn: false, // 登录状态
cartItems: [], // 购物车商品
};
ConfigManager.instance = this;
}
return ConfigManager.instance;
}
static getInstance() {
if (!ConfigManager.instance) {
ConfigManager.instance = new ConfigManager();
}
return ConfigManager.instance;
}
setConfig(key, value) {
this.config[key] = value;
}
getConfig(key) {
return this.config[key];
}
}
- 在项目启动时,可以初始化配置
import { ConfigManager } from "./ConfigManager"; const configManager = ConfigManager.getInstance(); configManager.setConfig("userId", "12345"); //在需要访问配置的组件中,可以获取 ConfigManager 的实例并使用配置 import React, { useEffect } from "react"; import { ConfigManager } from "./ConfigManager"; function MyComponent() { const configManager = ConfigManager.getInstance(); useEffect(() => { const apiUrl = configManager.getConfig("apiUrl"); const userId = configManager.getConfig("userId"); console.log("API URL:", apiUrl); console.log("User ID:", userId); // 使用配置进行数据请求等操作 }, []); return ( <div> <p>API URL: {configManager.getConfig("apiUrl")}</p > <p>User ID: {configManager.getConfig("userId")}</p > </div> ); } export default MyComponent;
工厂模式
工厂模式用于创建对象,而无需指定具体的类,它将创建对象的逻辑封装在工厂函数中
class Car {
constructor(model) {
this.model = model;
}
drive() {
console.log(`Driving ${this.model}`);
}
}
class Factory {
static createCar(model) {
return new Car(model);
}
}
const car1 = Factory.createCar("Toyota");
car1.drive(); // Driving Toyota
const car2 = Factory.createCar("Honda");
car2.drive(); // Driving Honda
动态创建表单组件
假设我们有一个前端项目,需要根据不同的表单类型动态创建相应的表单组件。我们可以使用工厂模式来实现这一点
- 定义不同类型的表单组件
class TextInput { constructor(label, name) { this.label = label; this.name = name; } render() { return `<label>${this.label}: <input type="text" name="${this.name}" /></label>`; } } class CheckboxInput { constructor(label, name) { this.label = label; this.name = name; } render() { return `<label><input type="checkbox" name="${this.name}" /> ${this.label}</label>`; } } class SelectInput { constructor(label, name, options) { this.label = label; this.name = name; this.options = options; } render() { const optionsHtml = this.options .map( (option) => `<option value="${option.value}">${option.label}</option>` ) .join(""); return `<label>${this.label}: <select name="${this.name}">${optionsHtml}</select></label>`; } } - 接下来,我们创建一个工厂类 FormFactory,用于根据表单类型创建相应的组件实例
class FormFactory { static createInput(type, label, name, options = []) { switch (type) { case "text": return new TextInput(label, name); case "checkbox": return new CheckboxInput(label, name); case "select": return new SelectInput(label, name, options); default: throw new Error("Unknown input type"); } } } - 在项目中使用 FormFactory 类来动态创建表单组件
import React, { useEffect, useState } from "react"; import { FormFactory } from "./FormFactory"; function DynamicForm() { const [formInputs, setFormInputs] = useState([]); useEffect(() => { // 定义表单配置 const formConfig = [ { type: "text", label: "Name", name: "name" }, { type: "checkbox", label: "Accept Terms", name: "accept_terms" }, { type: "select", label: "Country", name: "country", options: [ { value: "us", label: "United States" }, { value: "uk", label: "United Kingdom" }, { value: "ca", label: "Canada" }, ], }, ]; // 使用工厂类创建表单组件 const inputs = formConfig.map((config) => FormFactory.createInput( config.type, config.label, config.name, config.options ) ); setFormInputs(inputs); }, []); return ( <form> {formInputs.map((input, index) => ( <div key={index} dangerouslySetInnerHTML={{ __html: input.render() }} /> ))} <button type="submit">Submit</button> </form> ); } export default DynamicForm;抽象工厂模式
抽象工厂模式提供一个接口,用于创建一系列相关的对象,而无需指定具体的类
class AbstractFactory {
createCar() {}
createBike() {}
}
class ToyotaFactory extends AbstractFactory {
createCar() {
return new Car("Toyota");
}
createBike() {
return new Bike("Toyota");
}
}
class HondaFactory extends AbstractFactory {
createCar() {
return new Car("Honda");
}
createBike() {
return new Bike("Honda");
}
}
class Car {
constructor(model) {
this.model = model;
}
drive() {
console.log(`Driving ${this.model}`);
}
}
class Bike {
constructor(model) {
this.model = model;
}
ride() {
console.log(`Riding ${this.model}`);
}
}
const toyotaFactory = new ToyotaFactory();
const toyotaCar = toyotaFactory.createCar();
toyotaCar.drive(); // Driving Toyota
const hondaFactory = new HondaFactory();
const hondaBike = hondaFactory.createBike();
hondaBike.ride(); // Riding Honda
创建不同品牌的汽车和自行车
假设我们有一个前端项目,需要根据不同的品牌创建相应的汽车和自行车。我们可以使用抽象工厂模式来实现这一点
- 首先,我们定义汽车和自行车的产品接口
class Car { constructor(model) { this.model = model; } drive() { console.log(`Driving ${this.model}`); } } class Bike { constructor(model) { this.model = model; } ride() { console.log(`Riding ${this.model}`); } } - 接下来,我们定义一个抽象工厂接口 AbstractFactory,用于创建汽车和自行车
class AbstractFactory { createCar() {} createBike() {} } - 然后,我们创建两个具体工厂类 ToyotaFactory 和 HondaFactory,分别用于创建丰田和本田品牌的汽车和自行车
class ToyotaFactory extends AbstractFactory { createCar() { return new Car("Toyota Camry"); } createBike() { return new Bike("Toyota Cruiser"); } } class HondaFactory extends AbstractFactory { createCar() { return new Car("Honda Civic"); } createBike() { return new Bike("Honda Rebel"); } } - 项目中使用具体工厂类来创建不同品牌的汽车和自行车
import React, { useEffect, useState } from "react"; import { ToyotaFactory, HondaFactory } from "./factories"; function VehicleShowcase() { const [cars, setCars] = useState([]); const [bikes, setBikes] = useState([]); useEffect(() => { // 使用 ToyotaFactory 创建丰田品牌的汽车和自行车 const toyotaFactory = new ToyotaFactory(); const toyotaCar = toyotaFactory.createCar(); const toyotaBike = toyotaFactory.createBike(); setCars([...cars, toyotaCar]); setBikes([...bikes, toyotaBike]); // 使用 HondaFactory 创建本田品牌的汽车和自行车 const hondaFactory = new HondaFactory(); const hondaCar = hondaFactory.createCar(); const hondaBike = hondaFactory.createBike(); setCars([...cars, hondaCar]); setBikes([...bikes, hondaBike]); }, []); return ( <div> <h2>Cars</h2> <ul> {cars.map((car, index) => ( <li key={index}> {car.model} <button onClick={() => car.drive()}>Drive</button> </li> ))} </ul> <h2>Bikes</h2> <ul> {bikes.map((bike, index) => ( <li key={index}> {bike.model} <button onClick={() => bike.ride()}>Ride</button> </li> ))} </ul> </div> ); } export default VehicleShowcase;原型模式
原型模式通过复制一个现有对象来创建新对象function Car(model) { this.model = model; } Car.prototype.drive = function () { console.log(`Driving ${this.model}`); }; function createCarFromPrototype(model) { const car = Object.create(Car.prototype); car.model = model; return car; } const car3 = createCarFromPrototype("Toyota"); car3.drive(); // Driving Toyota const car4 = createCarFromPrototype("Honda"); car4.drive(); // Driving Honda
创建和管理任务
假设我们有一个前端项目,需要创建和管理多个任务对象。每个任务对象都有相似的属性和方法,但具体的值可能不同。我们可以使用原型模式来实现这一点
- 首先,我们定义一个任务原型对象 TaskPrototype,包含任务的基本属性和方法
function TaskPrototype(title, description, dueDate) { this.title = title; this.description = description; this.dueDate = dueDate; this.completed = false; } TaskPrototype.prototype.complete = function () { this.completed = true; console.log(`${this.title} completed`); }; TaskPrototype.prototype.display = function () { console.log(`Title: ${this.title}`); console.log(`Description: ${this.description}`); console.log(`Due Date: ${this.dueDate}`); console.log(`Completed: ${this.completed ? "Yes" : "No"}`); }; - 接下来,我们使用 Object.create 方法来创建具体的任务对象,并根据需要设置不同的属性值
const taskPrototype = new TaskPrototype(); function createTask(title, description, dueDate) { const task = Object.create(taskPrototype); task.title = title; task.description = description; task.dueDate = dueDate; return task; } - 在项目中使用创建的任务对象,调用它们的方法
import React, { useEffect, useState } from "react"; import { createTask } from "./tasks"; function TaskManager() { const [tasks, setTasks] = useState([]); useEffect(() => { const task1 = createTask( "Buy groceries", "Go to the supermarket and buy fruits and vegetables", "2023-10-01" ); const task2 = createTask( "Write report", "Complete the project report by the deadline", "2023-10-15" ); setTasks([task1, task2]); }, []); const handleComplete = (task) => { task.complete(); setTasks([...tasks]); }; return ( <div> <h2>Tasks</h2> <ul> {tasks.map((task, index) => ( <li key={index}> {task.title} <button onClick={() => handleComplete(task)}>Complete</button> </li> ))} </ul> </div> ); } export default TaskManager;
观察者模式
观察者模式定义了对象之间的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新
class Subject {
constructor() {
this.observers = [];
}
addObserver(observer) {
this.observers.push(observer);
}
removeObserver(observer) {
this.observers = this.observers.filter((obs) => obs !== observer);
}
notify() {
this.observers.forEach((observer) => observer.update());
}
}
class Observer {
update() {
console.log("Observer updated");
}
}
const subject = new Subject();
const observer1 = new Observer();
const observer2 = new Observer();
subject.addObserver(observer1);
subject.addObserver(observer2);
subject.notify(); // Observer updated (两次)
在多个组件中使用观察者模式
假设我们有多个组件需要接收通知,可以将主题实例作为上下文提供给这些组件
- 首先,我们创建一个 React 上下文来提供主题实例
import React, { createContext, useContext } from "react"; import { Subject } from "./observer"; const NotificationContext = createContext(); function NotificationProvider({ children }) { const subject = new Subject(); return ( <NotificationContext.Provider value={subject}> {children} </NotificationContext.Provider> ); } export { NotificationProvider, NotificationContext }; - 在需要接收通知的组件中使用上下文来获取主题实例,并注册为观察者
import React, { useEffect } from "react"; import { useContext } from "react"; import { NotificationContext } from "./NotificationProvider"; function ObserverComponent({ name }) { const subject = useContext(NotificationContext); useEffect(() => { const observer = new Observer(name); subject.addObserver(observer); return () => { subject.removeObserver(observer); }; }, [subject, name]); return ( <div> <h3>{name}</h3> <p>Waiting for notifications...</p > </div> ); } export default ObserverComponent; - 在应用的入口文件中,使用 NotificationProvider 包裹组件树,以便所有子组件都可以访问主题实例
import React from "react"; import ReactDOM from "react-dom"; import { NotificationProvider } from "./NotificationProvider"; import App from "./App"; ReactDOM.render( <NotificationProvider> <App /> </NotificationProvider>, document.getElementById("root") );
装饰器模式
装饰器模式允许动态地给对象添加职责,而不改变其接口
class Car {
drive() {
console.log("Driving");
}
}
class CarDecorator {
constructor(car) {
this.car = car;
}
drive() {
this.car.drive();
}
startEngine() {
console.log("Starting engine");
}
}
const car = new Car();
const decoratedCar = new CarDecorator(car);
decoratedCar.drive(); // Driving
decoratedCar.startEngine(); // Starting engine
扩展按钮组件
假设我们有一个前端项目,需要创建一个基础的按钮组件,并根据不同的需求扩展其功能。我们可以使用装饰器模式来实现这一点
- 首先,我们定义一个基础的按钮组件
class Button { constructor(text) { this.text = text; } render() { return `<button>${this.text}</button>`; } } - 接下来,我们定义几个装饰器类来扩展按钮组件的功能。例如,添加点击事件和样式
class ClickableButton { constructor(button) { this.button = button; } render() { const buttonHtml = this.button.render(); return `<div onclick="alert('Button clicked!')">${buttonHtml}</div>`; } } class StyledButton { constructor(button) { this.button = button; } render() { const buttonHtml = this.button.render(); return `<div style="background-color: lightblue; padding: 10px;">${buttonHtml}</div>`; } } - 在项目中使用装饰器类来扩展按钮组件的功能
import React, { useEffect, useState } from "react"; import { Button, ClickableButton, StyledButton } from "./components"; function App() { const [button, setButton] = useState(new Button("Click me")); useEffect(() => { // 使用装饰器扩展按钮功能 const clickableButton = new ClickableButton(button); const styledClickableButton = new StyledButton(clickableButton); setButton(styledClickableButton); }, []); return <div dangerouslySetInnerHTML={{ __html: button.render() }} />; } export default App;策略模式
策略模式定义了一系列算法,并将每个算法封装起来,使它们可以互换class Context { constructor(strategy) { this.strategy = strategy; } setStrategy(strategy) { this.strategy = strategy; } execute() { this.strategy.execute(); } } class StrategyA { execute() { console.log("Executing Strategy A"); } } class StrategyB { execute() { console.log("Executing Strategy B"); } } const context = new Context(new StrategyA()); context.execute(); // Executing Strategy A context.setStrategy(new StrategyB()); context.execute(); // Executing Strategy B订单处理系统
假设我们有一个前端项目,需要根据不同的订单类型选择不同的处理策略。我们可以使用策略模式来实现这一点 - 首先,我们定义一个策略接口 OrderStrategy,用于定义订单处理的基本方法
class OrderStrategy { processOrder(order) { throw new Error('Method "processOrder" must be implemented.'); } } - 接下来,我们创建几个具体策略类,分别实现不同的订单处理逻辑
class StandardOrderStrategy extends OrderStrategy { processOrder(order) { console.log(`Processing standard order: ${order.id}`); // 标准订单处理逻辑 } } class ExpressOrderStrategy extends OrderStrategy { processOrder(order) { console.log(`Processing express order: ${order.id}`); // 快速订单处理逻辑 } } class SpecialOrderStrategy extends OrderStrategy { processOrder(order) { console.log(`Processing special order: ${order.id}`); // 特殊订单处理逻辑 } } - 然后,我们创建一个上下文类 OrderContext,用于根据订单类型选择并执行相应的策略
class OrderContext { constructor(strategy) { this.strategy = strategy; } setStrategy(strategy) { this.strategy = strategy; } processOrder(order) { this.strategy.processOrder(order); } } - 在项目中使用策略模式来处理不同类型的订单
import React, { useEffect } from "react"; import { StandardOrderStrategy, ExpressOrderStrategy, SpecialOrderStrategy, OrderContext, } from "./order"; function OrderSystem() { useEffect(() => { const standardStrategy = new StandardOrderStrategy(); const expressStrategy = new ExpressOrderStrategy(); const specialStrategy = new SpecialOrderStrategy(); const orderContext = new OrderContext(standardStrategy); const orders = [ { id: 1, type: "standard" }, { id: 2, type: "express" }, { id: 3, type: "special" }, ]; orders.forEach((order) => { switch (order.type) { case "standard": orderContext.setStrategy(standardStrategy); break; case "express": orderContext.setStrategy(expressStrategy); break; case "special": orderContext.setStrategy(specialStrategy); break; default: throw new Error("Unknown order type"); } orderContext.processOrder(order); }); }, []); return ( <div> <h2>Order System</h2> <p>Orders will be processed using different strategies.</p > </div> ); } export default OrderSystem;命令模式
命令模式将请求封装成对象,从而使你可以用不同的请求对客户进行参数化class Light { turnOn() { console.log("Light turned on"); } turnOff() { console.log("Light turned off"); } } class Command { execute() {} } class TurnOnCommand extends Command { constructor(light) { super(); this.light = light; } execute() { this.light.turnOn(); } } class TurnOffCommand extends Command { constructor(light) { super(); this.light = light; } execute() { this.light.turnOff(); } } class RemoteControl { constructor() { this.commands = []; } setCommand(command) { this.commands.push(command); } executeCommands() { this.commands.forEach((command) => command.execute()); } } const light = new Light(); const remote = new RemoteControl(); remote.setCommand(new TurnOnCommand(light)); remote.setCommand(new TurnOffCommand(light)); remote.executeCommands(); // Light turned on, Light turned off文本编辑器
假设我们有一个前端项目,需要实现一个文本编辑器,支持多种操作(如插入文本、删除文本等)。我们可以使用命令模式来封装这些操作 - 首先,我们定义一个命令接口 Command,用于定义命令的基本方法
class Command { execute() { throw new Error('Method "execute" must be implemented.'); } undo() { throw new Error('Method "undo" must be implemented.'); } } - 接下来,我们创建几个具体命令类,分别实现不同的编辑操作
class InsertTextCommand extends Command { constructor(editor, text, position) { super(); this.editor = editor; this.text = text; this.position = position; } execute() { this.editor.insertText(this.text, this.position); } undo() { this.editor.deleteText(this.position, this.text.length); } } class DeleteTextCommand extends Command { constructor(editor, position, length) { super(); this.editor = editor; this.position = position; this.length = length; } execute() { this.editor.deleteText(this.position, this.length); } undo() { this.editor.insertText(this.text, this.position); } } class UndoCommand extends Command { constructor(editor) { super(); this.editor = editor; } execute() { this.editor.undo(); } undo() { // Undo command cannot be undone } } - 然后,我们创建一个编辑器类 Editor,用于管理文本和执行命令
class Editor { constructor() { this.text = ""; this.history = []; this.currentStep = -1; } insertText(text, position) { this.text = this.text.slice(0, position) + text + this.text.slice(position); this._saveStep(); } deleteText(position, length) { this.text = this.text.slice(0, position) + this.text.slice(position + length); this._saveStep(); } undo() { if (this.currentStep >= 0) { const command = this.history[this.currentStep]; command.undo(); this.currentStep--; } } _saveStep() { if (this.currentStep < this.history.length - 1) { this.history = this.history.slice(0, this.currentStep + 1); } this.history.push(new UndoCommand(this)); this.currentStep++; } getText() { return this.text; } } - 在项目中使用命令模式来执行编辑操作
import React, { useState } from "react"; import { Editor, InsertTextCommand, DeleteTextCommand, UndoCommand, } from "./editor"; function TextEditor() { const [text, setText] = useState(""); const editor = new Editor(); const handleInsert = () => { const command = new InsertTextCommand(editor, "Hello", 0); command.execute(); setText(editor.getText()); }; const handleDelete = () => { const command = new DeleteTextCommand(editor, 0, 5); command.execute(); setText(editor.getText()); }; const handleUndo = () => { const command = new UndoCommand(editor); command.execute(); setText(editor.getText()); }; return ( <div> <h2>Text Editor</h2> <textarea value={text} readOnly /> <div> <button onClick={handleInsert}>Insert Text</button> <button onClick={handleDelete}>Delete Text</button> <button onClick={handleUndo}>Undo</button> </div> </div> ); } export default TextEditor;
发布订阅者模式
定义一个事件中心类 EventCenter
class EventCenter {
constructor() {
this.events = {}; // 保存所有事件和订阅者
}
// 订阅事件
subscribe(eventName, subscriber) {
if (!this.events[eventName]) {
this.events[eventName] = []; // 如果事件不存在,创建一个新的数组
}
this.events[eventName].push(subscriber); // 添加订阅者到事件列表中
}
// 发布事件
publish(eventName, data) {
const subscribers = this.events[eventName] || [];
for (const subscriber of subscribers) {
subscriber(data); // 依次执行所有订阅者的回调函数
}
}
clearSubscribe(eventName) {
let object = {};
for (const key in this.events) {
if (key !== eventName) {
object[key] = this.events[key];
}
}
this.events = object;
}
get() {
console.log(this.events);
}
}
// 创建一个事件中心对象
const eventCenter = new EventCenter();
// 订阅事件
eventCenter.subscribe("event1", function (data) {
console.log(`Event1 received data: ${data}`);
});
eventCenter.subscribe("event2", function (data) {
console.log(`Event2 received data: ${data}`);
});
// 发布事件
eventCenter.publish("event1", "Hello, event1!");
eventCenter.publish("event2", "Hello, event2!");
eventCenter.clearSubscribe("event1");
eventCenter.get();
消息通知系统
假设我们有一个前端项目,需要在用户执行某些操作时向多个组件发送通知。我们可以使用发布订阅者模式来实现这一点
- 首先,我们定义一个发布订阅者类 EventBus,用于管理事件的发布和订阅
class EventBus { constructor() { this.events = {}; } subscribe(eventName, callback) { if (!this.events[eventName]) { this.events[eventName] = []; } this.events[eventName].push(callback); } publish(eventName, data) { const callbacks = this.events[eventName]; if (callbacks) { callbacks.forEach((callback) => callback(data)); } } unsubscribe(eventName, callback) { const callbacks = this.events[eventName]; if (callbacks) { this.events[eventName] = callbacks.filter((cb) => cb !== callback); } } } - 接下来,我们创建一个 EventBus 实例,并在需要的地方使用它
const eventBus = new EventBus(); - 在需要接收通知的组件中订阅事件,并提供回调函数来处理通知
import React, { useEffect } from "react"; // import { eventBus } from "./eventBus"; function NotificationComponent() { useEffect(() => { const handleNotification = (message) => { console.log(`Received notification: ${message}`); }; eventBus.subscribe("notification", handleNotification); // 清理订阅 return () => { eventBus.unsubscribe("notification", handleNotification); }; }, []); return ( <div> <h3>Notification Component</h3> <p>Waiting for notifications...</p > </div> ); } export default NotificationComponent; - 在需要发送通知的地方发布事件
import React, { useState } from "react"; import { eventBus } from "./eventBus"; function NotificationSender() { const [message, setMessage] = useState(""); const handleSendNotification = () => { eventBus.publish("notification", message); }; return ( <div> <h3>Notification Sender</h3> <input type="text" value={message} onChange={(e) => setMessage(e.target.value)} placeholder="Enter notification message" /> <button onClick={handleSendNotification}>Send Notification</button> </div> ); } export default NotificationSender; - 在应用的入口文件中,将 NotificationComponent 和 NotificationSender 组件组合在一起
import React from "react"; import ReactDOM from "react-dom"; import NotificationComponent from "./NotificationComponent"; import NotificationSender from "./NotificationSender"; function App() { return ( <div> <NotificationComponent /> <NotificationSender /> </div> ); } ReactDOM.render(<App />, document.getElementById("root"));
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 从安的博客!
评论



