UNPKG

bowling-analysis-system

Version:

A comprehensive system for analyzing bowling techniques using video processing and metrics calculation

290 lines (249 loc) 8.8 kB
/** * Factory for creating processor instances * @class ProcessorFactory */ class ProcessorFactory { /** * Creates an instance of ProcessorFactory * @param {Object} options - Factory options * @param {Object} [options.logger] - Logger instance * @param {Object} [options.config] - Configuration object * @param {Object} [options.serviceRegistry] - Service registry */ constructor(options = {}) { if (!options.logger) { throw new Error('Logger is required for ProcessorFactory'); } this.logger = options.logger; this.config = options.config || {}; this.serviceRegistry = options.serviceRegistry; this.processorClasses = new Map(); this.processorInstances = new Map(); this.defaultOptions = {}; // Register built-in processors this._registerBuiltInProcessors(); } /** * Register built-in processors * @private */ _registerBuiltInProcessors() { try { const ProcessorBase = require('./ProcessorBase'); const GenericProcessor = require('./GenericProcessor'); this.register('base', ProcessorBase); this.register('generic', GenericProcessor); // Register any additional built-in processors here } catch (error) { this.logger.warn('Failed to register built-in processors:', error); } } /** * Register a processor class * @param {String} type - Processor type identifier * @param {Class} processorClass - Processor class * @param {Object} [defaultOptions] - Default options for this processor type * @returns {ProcessorFactory} - For method chaining */ register(type, processorClass, defaultOptions = {}) { if (!type || typeof type !== 'string') { throw new Error('Processor type must be a non-empty string'); } if (!processorClass || typeof processorClass !== 'function') { throw new Error('Processor class must be a constructor function'); } if (!processorClass.prototype.process || typeof processorClass.prototype.process !== 'function') { throw new Error(`Processor class for ${type} must implement a process method`); } this.processorClasses.set(type, { class: processorClass, defaultOptions: { ...defaultOptions } }); this.logger.debug(`Registered processor type: ${type}`); return this; } /** * Create a processor instance * @param {String} type - Processor type * @param {Object} [options] - Processor options * @param {String} [options.name] - Processor name (defaults to type) * @param {Boolean} [options.singleton=false] - Whether to reuse instances * @returns {Object} - Processor instance */ create(type, options = {}) { if (!this.processorClasses.has(type)) { throw new Error(`Unknown processor type: ${type}`); } const { class: ProcessorClass, defaultOptions } = this.processorClasses.get(type); const name = options.name || type; const isSingleton = options.singleton === true; // For singletons, check if we already have an instance const instanceKey = isSingleton ? `${type}:${name}` : null; if (isSingleton && this.processorInstances.has(instanceKey)) { return this.processorInstances.get(instanceKey); } // Merge options const mergedOptions = { ...this.defaultOptions, ...defaultOptions, ...options, name, logger: options.logger || this.logger, config: options.config || this.config, serviceRegistry: options.serviceRegistry || this.serviceRegistry }; // Create instance const processor = new ProcessorClass(mergedOptions); // Store singleton instances if (isSingleton) { this.processorInstances.set(instanceKey, processor); } return processor; } /** * Create a processor instance and initialize it * @param {String} type - Processor type * @param {Object} [options] - Processor options * @param {Object} [initOptions] - Initialization options * @returns {Promise<Object>} - Initialized processor instance */ async createAndInitialize(type, options = {}, initOptions = {}) { const processor = this.create(type, options); await processor.initialize(initOptions); return processor; } /** * Check if a processor type is registered * @param {String} type - Processor type * @returns {Boolean} - Whether the type is registered */ hasType(type) { return this.processorClasses.has(type); } /** * Get all registered processor types * @returns {Array<String>} - Array of processor types */ getTypes() { return Array.from(this.processorClasses.keys()); } /** * Set default options for all processors * @param {Object} options - Default options * @returns {ProcessorFactory} - For method chaining */ setDefaultOptions(options) { this.defaultOptions = { ...options }; return this; } /** * Get a singleton processor instance * @param {String} type - Processor type * @param {String} name - Processor name * @returns {Object|null} - Processor instance or null if not found */ getSingleton(type, name) { const instanceKey = `${type}:${name || type}`; return this.processorInstances.get(instanceKey) || null; } /** * Clear all singleton instances * @returns {ProcessorFactory} - For method chaining */ clearSingletons() { this.processorInstances.clear(); return this; } /** * Create a processor from a configuration object * @param {Object} config - Processor configuration * @param {String} config.type - Processor type * @param {String} [config.name] - Processor name * @param {Object} [config.options] - Processor options * @param {Boolean} [config.singleton] - Whether to use a singleton * @param {Boolean} [config.initialize=true] - Whether to initialize the processor * @returns {Promise<Object>} - Processor instance */ async createFromConfig(config) { if (!config || typeof config !== 'object') { throw new Error('Processor configuration must be an object'); } if (!config.type) { throw new Error('Processor configuration must specify a type'); } if (!this.hasType(config.type)) { throw new Error(`Unknown processor type: ${config.type}`); } const options = { name: config.name || config.type, singleton: config.singleton === true, ...config.options }; const processor = this.create(config.type, options); if (config.initialize !== false) { await processor.initialize(config.initOptions || {}); } return processor; } /** * Create multiple processors from configuration * @param {Array<Object>} configs - Array of processor configurations * @returns {Promise<Array<Object>>} - Array of processor instances */ async createMultiple(configs) { if (!Array.isArray(configs)) { throw new Error('Processor configurations must be an array'); } const processors = []; for (const config of configs) { const processor = await this.createFromConfig(config); processors.push(processor); } return processors; } /** * Load processor configurations from a config object * @param {Object} configObject - Configuration object * @returns {ProcessorFactory} - For method chaining */ loadFromConfig(configObject = {}) { if (!configObject || typeof configObject !== 'object') { throw new Error('Invalid configuration object'); } if (configObject.processors) { for (const [id, config] of Object.entries(configObject.processors)) { try { const processorClass = this._loadProcessorClass(config); if (processorClass) { this.register(id, processorClass, config.defaultOptions || {}); } else { this.logger.error(`Failed to load processor class for ${id}: Class not found`); } } catch (error) { this.logger.error(`Failed to load processor ${id}: ${error.message}`); throw error; } } } return this; } /** * Load a processor class from configuration * @param {Object} config - Processor configuration * @returns {Class} - Processor class * @private */ _loadProcessorClass(config) { if (!config.classPath) { throw new Error('Processor configuration must specify a classPath'); } try { // Resolve the class path return require(config.classPath); } catch (error) { this.logger.error(`Failed to load processor class from ${config.classPath}: ${error.message}`); throw error; } } } module.exports = ProcessorFactory;