Skip to content

Instantly share code, notes, and snippets.

@mducharme
Last active February 21, 2024 16:22
Show Gist options
  • Save mducharme/223604eb819def3e6337ec4ec05c41a6 to your computer and use it in GitHub Desktop.
Save mducharme/223604eb819def3e6337ec4ec05c41a6 to your computer and use it in GitHub Desktop.
Enterprise-Level Typescript Pizza Calculator
// 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