网站到期请续费网站怎么做会员系统
2026/1/16 16:47:43 网站建设 项目流程
网站到期请续费,网站怎么做会员系统,网站怎么在百度搜不到,有没有免费开网站的例模式是一种创建型设计模式#xff0c;它确保一个类只有一个实例#xff0c;并提供一个全局访问点来获取这个实例。在 TypeScript 中#xff0c;单例模式特别有用#xff0c;因为它结合了 JavaScript 的灵活性和 TypeScript 的类型安全。为什么需要单例模式#xff1f;想…例模式是一种创建型设计模式它确保一个类只有一个实例并提供一个全局访问点来获取这个实例。在 TypeScript 中单例模式特别有用因为它结合了 JavaScript 的灵活性和 TypeScript 的类型安全。为什么需要单例模式想象一下这些场景数据库连接池管理应用程序配置管理器日志记录器缓存管理器在这些情况下我们需要确保整个应用程序中只有一个实例来处理这些全局资源避免资源浪费和不一致的状态。基础单例实现让我们从最简单的实现开始class Singleton {private static instance: Singleton;private constructor() {// 私有构造函数防止外部实例化}public static getInstance(): Singleton {if (!Singleton.instance) {Singleton.instance new Singleton();}return Singleton.instance;}public someBusinessLogic() {// 业务逻辑}}线程安全的单例实现在 JavaScript/TypeScript 中由于是单线程环境我们不需要担心传统的线程安全问题。但考虑到异步操作我们可以使用更安全的实现class ThreadSafeSingleton {private static instance: ThreadSafeSingleton;private constructor() {// 初始化代码}public static getInstance(): ThreadSafeSingleton {if (!ThreadSafeSingleton.instance) {ThreadSafeSingleton.instance new ThreadSafeSingleton();}return ThreadSafeSingleton.instance;}// 使用 Promise 确保异步安全public static async getInstanceAsync(): PromiseThreadSafeSingleton {if (!ThreadSafeSingleton.instance) {ThreadSafeSingleton.instance new ThreadSafeSingleton();// 模拟异步初始化await new Promise(resolve setTimeout(resolve, 0));}return ThreadSafeSingleton.instance;}}使用模块模式的单例实现TypeScript 的模块系统天然支持单例模式// Logger.tsclass Logger {private logs: string[] [];log(message: string) {this.logs.push(${new Date().toISOString()}: ${message});console.log(message);}getLogs(): string[] {return [...this.logs];}}// 直接导出实例export const logger new Logger();带参数的单例模式有时我们需要在单例初始化时传递参数class ConfigManager {private static instance: ConfigManager;private config: Recordstring, any;private constructor(initialConfig?: Recordstring, any) {this.config initialConfig || {};}public static initialize(initialConfig?: Recordstring, any): ConfigManager {if (!ConfigManager.instance) {ConfigManager.instance new ConfigManager(initialConfig);}return ConfigManager.instance;}public static getInstance(): ConfigManager {if (!ConfigManager.instance) {throw new Error(ConfigManager not initialized. Call initialize() first.);}return ConfigManager.instance;}public set(key: string, value: any): void {this.config[key] value;}public get(key: string): any {return this.config[key];}}单例模式的优缺点优点严格控制实例数量确保全局唯一实例全局访问点方便在任何地方访问延迟初始化只有在需要时才创建实例缺点违反单一职责原则类需要管理自己的生命周期隐藏的依赖关系单例的使用可能不明显测试困难难以模拟和测试全局状态可能导致代码耦合测试单例模式测试单例类时需要特别注意describe(Singleton, () {beforeEach(() {// 重置单例实例用于测试(Singleton as any).instance undefined;});it(should return the same instance, () {const instance1 Singleton.getInstance();const instance2 Singleton.getInstance();expect(instance1).toBe(instance2);});});实际应用示例数据库连接池让我们看一个实际的数据库连接池单例实现interface DatabaseConfig {host: string;port: number;username: string;password: string;database: string;}class DatabaseConnectionPool {private static instance: DatabaseConnectionPool;private connections: any[] [];private config: DatabaseConfig;private constructor(config: DatabaseConfig) {this.config config;this.initializePool();}public static getInstance(config?: DatabaseConfig): DatabaseConnectionPool {if (!DatabaseConnectionPool.instance) {if (!config) {throw new Error(Configuration required for first initialization);}DatabaseConnectionPool.instance new DatabaseConnectionPool(config);}return DatabaseConnectionPool.instance;}private initializePool(): void {// 初始化连接池for (let i 0; i 10; i) {this.connections.push(this.createConnection());}}private createConnection(): any {// 创建数据库连接的逻辑return {query: (sql: string) console.log(Executing: ${sql}),close: () console.log(Connection closed)};}public getConnection(): any {return this.connections.pop() || this.createConnection();}public releaseConnection(connection: any): void {this.connections.push(connection);}}实际应用示例Streams to RiverStreams to River 由字节跳动开源 是一款英语学习应用。该产品的初衷是通过将日常所见的英语单词、句子和相关的上下文进行记录、提取和管理, 结合 艾宾浩斯遗忘曲线进行周期性的学习和记忆。在开发过程中深度采用了 TRAE 进行代码的开发和调试、注释和单测的编写通过 coze workflow 快速集成了图像转文字、实时聊天、语音识别、单词划线等大模型能力。在该项目代码中就存在大量的单例模式代码。1. AuthService 的实现class AuthService {private static instance: AuthService;private serverConfig: ServerConfig;private constructor() {this.serverConfig ServerConfig.getInstance();}public static getInstance(): AuthService {if (!AuthService.instance) {AuthService.instance new AuthService();}return AuthService.instance;}async login(loginData: LoginRequest): PromiseAuthResponse {try {const response await Taro.request({url: this.serverConfig.getFullUrl(/api/login),method: POST,data: loginData,header: {Content-Type: application/json}});if (response.statusCode 200) {const authData response.data as AuthResponse;await this.setToken(authData.token);return authData;} else {throw new Error(response.data || 登录失败);}} catch (error) {console.error(Login error:, error);throw error;}}async register(registerData: RegisterRequest): PromiseAuthResponse {try {const response await Taro.request({url: this.serverConfig.getFullUrl(/api/register),method: POST,data: registerData,header: {Content-Type: application/json}});if (response.statusCode 200) {const authData response.data as AuthResponse;await this.setToken(authData.token);return authData;} else {throw new Error(response.data || 注册失败);}} catch (error) {console.error(Register error:, error);throw error;}}async getUserInfo(): PromiseUser {try {const token await this.getToken();if (!token) {throw new Error(未找到token);}const response await Taro.request({url: this.serverConfig.getFullUrl(/api/user),method: GET,header: {Authorization: Bearer ${token},Content-Type: application/json}});if (response.statusCode 200) {return response.data as User;} else {throw new Error(获取用户信息失败);}} catch (error) {console.error(Get user info error:, error);throw error;}}async setToken(token: string): Promisevoid {try {await Taro.setStorageSync(jwt_token, token);} catch (error) {console.error(Set token error:, error);throw error;}}async getToken(): Promisestring | null {try {return Taro.getStorageSync(jwt_token) || null;} catch (error) {console.error(Get token error:, error);return null;}}async clearToken(): Promisevoid {try {await Taro.removeStorageSync(jwt_token);} catch (error) {console.error(Clear token error:, error);}}async isLoggedIn(): Promiseboolean {const token await this.getToken();return !!token;}async logout(): Promisevoid {await this.clearToken();}}2. AudioManager 的实现class AudioManager {private static instance: AudioManager;private currentAudio: HTMLAudioElement | null null;private currentWordId: number | null null;private playingCallbacks: Mapnumber, (isPlaying: boolean) void new Map();static getInstance(): AudioManager {if (!AudioManager.instance) {AudioManager.instance new AudioManager();}return AudioManager.instance;}// Register playback status callbackregisterCallback(wordId: number, callback: (isPlaying: boolean) void) {this.playingCallbacks.set(wordId, callback);}// Unregister callbackunregisterCallback(wordId: number) {this.playingCallbacks.delete(wordId);}// Play audioasync playAudio(wordId: number, audioUrl: string): Promisevoid {try {// Stop currently playing audiothis.stopCurrentAudio();// Create new audio instanceconst audio new Audio(audioUrl);this.currentAudio audio;this.currentWordId wordId;// Set audio propertiesaudio.preload auto;audio.volume 1.0;// Notify playback startthis.notifyPlayingState(wordId, true);// Listen to audio eventsaudio.addEventListener(ended, () {this.handleAudioEnd();});audio.addEventListener(error, (e) {console.error(Audio playback error:, e);this.handleAudioEnd();});// Play audioawait audio.play();} catch (error) {console.error(Failed to play audio:, error);this.handleAudioEnd();}}// Stop current audioprivate stopCurrentAudio() {if (this.currentAudio) {this.currentAudio.pause();this.currentAudio.currentTime 0;this.currentAudio null;}if (this.currentWordId ! null) {this.notifyPlayingState(this.currentWordId, false);this.currentWordId null;}}// Handle audio endprivate handleAudioEnd() {if (this.currentWordId ! null) {this.notifyPlayingState(this.currentWordId, false);}this.currentAudio null;this.currentWordId null;}// Notify playback state changeprivate notifyPlayingState(wordId: number, isPlaying: boolean) {const callback this.playingCallbacks.get(wordId);if (callback) {callback(isPlaying);}}// Check if currently playingisPlaying(wordId: number): boolean {return this.currentWordId wordId this.currentAudio ! null;}}3. ServerConfig 的实现class ServerConfig {private static instance: ServerConfig;private config: ServerConfigInterface;private constructor() {this.config this.loadConfig();}public static getInstance(): ServerConfig {if (!ServerConfig.instance) {ServerConfig.instance new ServerConfig();}return ServerConfig.instance;}private loadConfig(): ServerConfigInterface {const serverDomain location.origin;const url new URL(serverDomain);return {domain: url.hostname,port: url.port ? parseInt(url.port) : (url.protocol https: ? 443 : 80),protocol: url.protocol.replace(:, ),};}public getDomain(): string {return this.config.domain;}public getPort(): number {return this.config.port || 80;}public getProtocol(): string {return this.config.protocol || http;}public getBaseUrl(): string {const port this.getPort();const protocol this.getProtocol();const domain this.getDomain();if ((protocol http port 80) || (protocol https port 443)) {return ${protocol}://${domain};}return ${protocol}://${domain}:${port};}public getFullUrl(path: string ): string {const baseUrl this.getBaseUrl();const cleanPath path.startsWith(/) ? path : /${path};return ${baseUrl}${cleanPath};}}实际应用示例Cherry Studio Cherry Studio 是一款支持多个大语言模型LLM服务商的桌面客户端兼容 Windows、Mac 和 Linux 系统。。该项目前端是比较复杂的 采用良好的设计十分必要。它的代码中也存在大量的单例模式设计。1. StoreSyncService 的实现import { IpcChannel } from shared/IpcChannelimport type { StoreSyncAction } from typesimport { BrowserWindow, ipcMain } from electron/*** StoreSyncService class manages Redux store synchronization between multiple windows in the main process* It uses singleton pattern to ensure only one sync service instance exists in the application** Main features:* 1. Manages window subscriptions for store sync* 2. Handles IPC communication for store sync between windows* 3. Broadcasts Redux actions from one window to all other windows* 4. Adds metadata to synced actions to prevent infinite sync loops*/export class StoreSyncService {private static instance: StoreSyncServiceprivate windowIds: number[] []private isIpcHandlerRegistered falseprivate constructor() {return}/*** Get the singleton instance of StoreSyncService*/public static getInstance(): StoreSyncService {if (!StoreSyncService.instance) {StoreSyncService.instance new StoreSyncService()}return StoreSyncService.instance}/*** Subscribe a window to store sync* param windowId ID of the window to subscribe*/public subscribe(windowId: number): void {if (!this.windowIds.includes(windowId)) {this.windowIds.push(windowId)}}/*** Unsubscribe a window from store sync* param windowId ID of the window to unsubscribe*/public unsubscribe(windowId: number): void {this.windowIds this.windowIds.filter((id) id ! windowId)}/*** Sync an action to all renderer windows* param type Action type, like settings/setTray* param payload Action payload** NOTICE: DO NOT use directly in ConfigManager, may cause infinite sync loop*/public syncToRenderer(type: string, payload: any): void {const action: StoreSyncAction {type,payload}//-1 means the action is from the main process, will be broadcast to all windowsthis.broadcastToOtherWindows(-1, action)}/*** Register IPC handlers for store sync communication* Handles window subscription, unsubscription and action broadcasting*/public registerIpcHandler(): void {if (this.isIpcHandlerRegistered) returnipcMain.handle(IpcChannel.StoreSync_Subscribe, (event) {const windowId BrowserWindow.fromWebContents(event.sender)?.idif (windowId) {this.subscribe(windowId)}})ipcMain.handle(IpcChannel.StoreSync_Unsubscribe, (event) {const windowId BrowserWindow.fromWebContents(event.sender)?.idif (windowId) {this.unsubscribe(windowId)}})ipcMain.handle(IpcChannel.StoreSync_OnUpdate, (event, action: StoreSyncAction) {const sourceWindowId BrowserWindow.fromWebContents(event.sender)?.idif (!sourceWindowId) return// Broadcast the action to all other windowsthis.broadcastToOtherWindows(sourceWindowId, action)})this.isIpcHandlerRegistered true}/*** Broadcast a Redux action to all other windows except the source* param sourceWindowId ID of the window that originated the action* param action Redux action to broadcast*/private broadcastToOtherWindows(sourceWindowId: number, action: StoreSyncAction): void {// Add metadata to indicate this action came from syncconst syncAction {...action,meta: {...action.meta,fromSync: true,source: windowId:${sourceWindowId}}}// Send to all windows except the sourcethis.windowIds.forEach((windowId) {if (windowId ! sourceWindowId) {const targetWindow BrowserWindow.fromId(windowId)if (targetWindow !targetWindow.isDestroyed()) {targetWindow.webContents.send(IpcChannel.StoreSync_BroadcastSync, syncAction)} else {this.unsubscribe(windowId)}}})}}// Export singleton instanceexport default StoreSyncService.getInstance()2. NotificationQueue 的实现import type { Notification } from renderer/types/notificationimport PQueue from p-queuetype NotificationListener (notification: Notification) Promisevoid | voidexport class NotificationQueue {private static instance: NotificationQueueprivate queue new PQueue({ concurrency: 1 })private listeners: NotificationListener[] []// oxlint-disable-next-line typescript-eslint/no-empty-functionprivate constructor() {}public static getInstance(): NotificationQueue {if (!NotificationQueue.instance) {NotificationQueue.instance new NotificationQueue()}return NotificationQueue.instance}public subscribe(listener: NotificationListener) {this.listeners.push(listener)}public unsubscribe(listener: NotificationListener) {this.listeners this.listeners.filter((l) l ! listener)}public async add(notification: Notification): Promisevoid {await this.queue.add(() Promise.all(this.listeners.map((listener) listener(notification))))}/*** 清空通知队列*/public clear(): void {this.queue.clear()}/*** 获取队列中等待的任务数量*/public get pending(): number {return this.queue.pending}/*** 获取队列的大小包括正在进行和等待的任务*/public get size(): number {return this.queue.size}}3. AgentService 的实现import path from node:pathimport { loggerService } from loggerimport { pluginService } from main/services/agents/plugins/PluginServiceimport { getDataPath } from main/utilsimport type {AgentEntity,CreateAgentRequest,CreateAgentResponse,GetAgentResponse,ListOptions,UpdateAgentRequest,UpdateAgentResponse} from typesimport { AgentBaseSchema } from typesimport { asc, count, desc, eq } from drizzle-ormimport { BaseService } from ../BaseServiceimport { type AgentRow, agentsTable, type InsertAgentRow } from ../database/schemaimport type { AgentModelField } from ../errorsconst logger loggerService.withContext(AgentService)export class AgentService extends BaseService {private static instance: AgentService | null nullprivate readonly modelFields: AgentModelField[] [model, plan_model, small_model]static getInstance(): AgentService {if (!AgentService.instance) {AgentService.instance new AgentService()}return AgentService.instance}async initialize(): Promisevoid {await BaseService.initialize()}// Agent Methodsasync createAgent(req: CreateAgentRequest): PromiseCreateAgentResponse {this.ensureInitialized()const id agent_${Date.now()}_${Math.random().toString(36).substring(2, 11)}const now new Date().toISOString()

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询