Last active
February 21, 2024 16:22
-
-
Save mducharme/223604eb819def3e6337ec4ec05c41a6 to your computer and use it in GitHub Desktop.
Enterprise-Level Typescript Pizza Calculator
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Import the EventEmitter class from Node.js for creating and handling custom events. | |
import { EventEmitter } from 'events'; | |
// Define an interface for the configuration settings needed for pizza calculation. | |
interface IConfiguration { | |
slicesPerPerson: number; // Number of pizza slices each person will eat. | |
slicesPerPizza: number; // Number of slices that each pizza is divided into. | |
} | |
// Define an interface for the calculator, which will compute the number of pizzas needed. | |
interface ICalculator { | |
calculate(numPeople: number): number; // Calculate the number of pizzas based on the number of people. | |
} | |
// Define an interface for the strategy to be used in the calculation. | |
// This allows for different calculation strategies to be implemented and used interchangeably. | |
interface IStrategy { | |
execute(numPeople: number, slicesPerPerson: number, slicesPerPizza: number): number; // The strategy's calculation method. | |
} | |
// Concrete implementation of the IStrategy interface. | |
// This strategy calculates the number of pizzas needed based on people, slices per person, and slices per pizza. | |
class CalculationStrategy implements IStrategy { | |
execute(numPeople: number, slicesPerPerson: number, slicesPerPizza: number): number { | |
return Math.ceil(slicesPerPerson * numPeople / slicesPerPizza); // Perform calculation and round up to the nearest whole number. | |
} | |
} | |
// Configuration class implementing IConfiguration interface to store pizza calculation settings. | |
class Configuration implements IConfiguration { | |
constructor( | |
public slicesPerPerson: number = 2, // Default value of 2 slices per person. | |
public slicesPerPizza: number = 8 // Default value of 8 slices per pizza. | |
) {} | |
} | |
// Service Locator with Singleton pattern to manage and provide access to various services like strategy, configuration, and EventEmitter. | |
class ServiceLocator { | |
private services: Map<string, any> = new Map(); // Stores service instances by name. | |
static instance: ServiceLocator; // Singleton instance of the ServiceLocator. | |
private constructor() { | |
// Register an EventEmitter service for handling events within the application. | |
this.registerService('eventEmitter', new EventEmitter()); | |
} | |
// Ensures a single instance of the ServiceLocator is created and used throughout the application. | |
static getInstance(): ServiceLocator { | |
if (!ServiceLocator.instance) { | |
ServiceLocator.instance = new ServiceLocator(); | |
} | |
return ServiceLocator.instance; | |
} | |
// Register a service with the locator. | |
registerService(serviceName: string, instance: any) { | |
this.services.set(serviceName, instance); | |
} | |
// Retrieve a service by name. | |
getService(serviceName: string) { | |
return this.services.get(serviceName); | |
} | |
} | |
// Basic Calculator implementation that uses a given strategy and configuration to calculate the number of pizzas needed. | |
class PizzaCalculator implements ICalculator { | |
constructor(private strategy: IStrategy, private configuration: IConfiguration) {} | |
calculate(numPeople: number): number { | |
// Use the strategy's execute method to perform the calculation with the current configuration. | |
return this.strategy.execute(numPeople, this.configuration.slicesPerPerson, this.configuration.slicesPerPizza); | |
} | |
} | |
// Decorator class that adds event-driven logging functionality to the ICalculator interface. | |
// It wraps around an existing ICalculator instance and uses an EventEmitter for logging. | |
class EventLoggingCalculatorDecorator implements ICalculator { | |
constructor(private calculator: ICalculator, private eventEmitter: EventEmitter) { | |
// Initialize event listeners for logging when calculations are performed. | |
this.initializeEventListeners(); | |
} | |
calculate(numPeople: number): number { | |
// Perform the calculation using the wrapped calculator instance. | |
const result = this.calculator.calculate(numPeople); | |
// Emit an event with the calculation result. | |
this.eventEmitter.emit('calculationPerformed', result); | |
return result; | |
} | |
// Setup event listeners for the 'calculationPerformed' event to log the results. | |
private initializeEventListeners() { | |
this.eventEmitter.on('calculationPerformed', (result) => { | |
console.log(`Event: Calculation performed, result: ${result}`); | |
}); | |
} | |
} | |
// Setup and usage example: | |
// Initialize the Service Locator and register services. | |
const locator = ServiceLocator.getInstance(); | |
locator.registerService('strategy', new CalculationStrategy()); // Register the calculation strategy. | |
locator.registerService('configuration', new Configuration(3, 8)); // Register a custom configuration with 3 slices per person and 8 slices per pizza. | |
// Retrieve the EventEmitter service from the locator. | |
const eventEmitter = locator.getService('eventEmitter'); | |
// Create a basic PizzaCalculator instance with dependencies from the locator. | |
const basicCalculator = new PizzaCalculator( | |
locator.getService('strategy'), | |
locator.getService('configuration') | |
); | |
// Wrap the basic calculator with the EventLoggingCalculatorDecorator to add logging functionality. | |
const eventLoggingCalculator = new EventLoggingCalculatorDecorator(basicCalculator, eventEmitter); | |
// Example usage: Perform a calculation. This will also trigger logging through the event system. | |
let numPeople = 18; | |
console.log(eventLoggingCalculator.calculate(numPeople)); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment