Hone logo
Hone
Problems

Dependency Injection Implementation in TypeScript

Dependency injection (DI) is a powerful design pattern that promotes loose coupling and testability in software. This challenge asks you to implement different types of dependency injection – constructor injection, property injection, and method injection – in TypeScript, demonstrating your understanding of this crucial concept. Successfully completing this challenge will allow you to build more modular and maintainable applications.

Problem Description

You are tasked with creating a TypeScript module that showcases three common dependency injection patterns: constructor injection, property injection, and method injection. The module should define an interface ILogger with a single method log(message: string) and then implement three classes: ServiceA, ServiceB, and ServiceC. Each service should demonstrate a different DI pattern to receive an instance of ILogger.

What needs to be achieved:

  1. Define the ILogger interface.
  2. Create a concrete Logger class that implements ILogger.
  3. Implement ServiceA using constructor injection.
  4. Implement ServiceB using property injection.
  5. Implement ServiceC using method injection.
  6. Provide a main function that instantiates each service, injects the Logger instance, and calls a method on each service that utilizes the injected logger.

Key Requirements:

  • The code must be written in TypeScript.
  • Each service must correctly utilize the injected logger.
  • The code should be well-structured and readable.
  • Demonstrate the three DI patterns clearly and distinctly.

Expected Behavior:

When the main function is executed, each service should log a message to the console using its injected logger. The messages should be distinct for each service to confirm that the dependency injection is working correctly.

Edge Cases to Consider:

  • What happens if a service is instantiated without the required dependency? (While not strictly required to handle this in the solution, consider it for robustness).
  • How does each injection type affect the testability of the service? (This is a conceptual consideration, not a coding requirement).

Examples

Example 1:

Input:  No specific input, the code will be executed directly.
Output:
ServiceA: Logging message from ServiceA.
ServiceB: Logging message from ServiceB.
ServiceC: Logging message from ServiceC.
Explanation: Each service successfully receives and uses the injected logger.

Example 2:

Input:  A different message to be logged by the Logger.
Output:
ServiceA: Logging [new message] from ServiceA.
ServiceB: Logging [new message] from ServiceB.
ServiceC: Logging [new message] from ServiceC.
Explanation: The logger's message is updated, and all services reflect this change.

Constraints

  • The code must be valid TypeScript.
  • The log method in ILogger should accept a string as input.
  • The solution should be concise and demonstrate the core concepts of each DI pattern.
  • No external libraries are allowed.

Notes

  • Constructor injection is generally considered the preferred method as it makes dependencies explicit.
  • Property injection can be useful for optional dependencies.
  • Method injection is less common but can be useful in specific scenarios.
  • Focus on demonstrating the concept of each DI pattern rather than complex error handling or advanced features. The goal is to show you understand how to inject dependencies using different approaches.
  • Think about how each injection type might impact unit testing.
Loading editor...
typescript