ℹ️ Select 'Choose Exercise', or randomize 'Next Random Exercise' in selected language.

Choose Exercise:
Timer 00:00
WPM --
Score --
Acc --
Correct chars --

DI Workflows: Service Locator Pattern

Dart

Goal -- WPM

Ready
Exercise Algorithm Area
1import 'dart:async';
2
3// --- Service Interface ---
4// Defines the contract for services.
5abstract class Service {}
6
7// --- Concrete Service 1: Logger ---
8// A simple logger service.
9class Logger implements Service {
10void log(String message) {
11print('[LOG] $message');
12}
13}
14
15// --- Concrete Service 2: DataService ---
16// A service that depends on the Logger.
17class DataService implements Service {
18final Logger _logger;
19
20// Constructor takes the dependency.
21// In a Service Locator pattern, this dependency is resolved by the locator.
22DataService(this._logger);
23
24void fetchData() {
25_logger.log('Fetching data...');
26// Simulate data fetching logic
27Future.delayed(const Duration(milliseconds: 500), () {
28_logger.log('Data fetched successfully.');
29});
30}
31}
32
33// --- Service Locator ---
34// Manages the registration and resolution of services.
35class ServiceLocator {
36// A map to store registered services. Keys are service types (as Symbols).
37final Map<Symbol, Service> _services = {};
38
39// Singleton instance of the Service Locator.
40static final ServiceLocator _instance = ServiceLocator._internal();
41
42// Private constructor for singleton pattern.
43ServiceLocator._internal();
44
45// Factory constructor to return the singleton instance.
46factory ServiceLocator() => _instance;
47
48// Registers a service instance with a unique key.
49// The key is typically the service's type represented as a Symbol.
50void register<T extends Service>(T service) {
51final serviceKey = #T; // Creates a Symbol for the type T.
52if (_services.containsKey(serviceKey)) {
53print('Warning: Service of type ${T} already registered. Overwriting.');
54}
55_services[serviceKey] = service;
56print('Registered service: ${T}');
57}
58
59// Resolves and returns a registered service instance.
60// Throws an error if the service is not registered.
61T get<T extends Service>() {
62final serviceKey = #T;
63final service = _services[serviceKey];
64if (service == null) {
65throw StateError('Service of type ${T} not found. Register it first.');
66}
67// Type cast is safe because we ensure T is a subtype of Service.
68return service as T;
69}
70
71// Checks if a service of a given type is registered.
72bool isRegistered<T extends Service>() {
73final serviceKey = #T;
74return _services.containsKey(serviceKey);
75}
76
77// Clears all registered services.
78void unregisterAll() {
79_services.clear();
80print('All services unregistered.');
81}
82}
83
84// --- Application Entry Point ---
85Future<void> main() async {
86// 1. Initialize the Service Locator and register services.
87final locator = ServiceLocator();
88
89// Register the Logger service.
90locator.register(Logger());
91
92// Register the DataService. Note: DataService requires a Logger.
93// The Service Locator will resolve this dependency when DataService is requested.
94// For this simple example, we manually inject the logger here.
95// A more advanced locator would handle constructor injection automatically.
96final logger = locator.get<Logger>();
97locator.register(DataService(logger));
98
99// 2. Resolve and use services.
100print('\n--- Using Services ---');
101
102// Get the DataService from the locator.
103final dataService = locator.get<DataService>();
104dataService.fetchData();
105
106// Get the Logger service directly if needed.
107final loggerService = locator.get<Logger>();
108loggerService.log('Application started.');
109
110// Example of trying to get an unregistered service.
111try {
112locator.get<String>(); // This should throw an error.
113} catch (e) {
114print('\nCaught expected error: $e');
115}
116
117// Example of checking registration.
118print('\nIs Logger registered? ${locator.isRegistered<Logger>()}');
119print('Is String registered? ${locator.isRegistered<String>()}');
120
121// Clean up (optional, for demonstration).
122// locator.unregisterAll();
123}
Algorithm description viewbox

DI Workflows: Service Locator Pattern

Algorithm description:

This Dart code implements the Service Locator pattern, a dependency injection technique. It defines a `ServiceLocator` class that acts as a central registry for services. Services like `Logger` and `DataService` are registered with the locator. When a component needs a service, it requests it from the locator, which then provides the appropriate instance. This decouples components from the concrete implementations of their dependencies, simplifying management and testing.

Algorithm explanation:

The `ServiceLocator` class uses a `Map<Symbol, Service>` to store registered service instances, keyed by their type represented as `Symbol`s (e.g., `#Logger`). It follows the singleton pattern to ensure only one instance of the locator exists. The `register` method adds a service, and the `get` method retrieves it, throwing an error if the service is not found. The `DataService` depends on `Logger`, and in this implementation, the `Logger` is manually injected into `DataService` before registering `DataService`. A more advanced Service Locator could automatically resolve constructor dependencies. The `main` function demonstrates registering `Logger` and `DataService`, then resolving and using them. Time complexity for registration and retrieval is O(1) on average due to hash map operations. Space complexity is O(S) where S is the number of registered services. Edge cases include attempting to get an unregistered service or registering a service of the same type multiple times.

Pseudocode:

Define `Service` abstract class.

Define `Logger` class implementing `Service`:
  method `log(message)`: prints message.

Define `DataService` class implementing `Service`:
  private `Logger` instance `_logger`.
  constructor `DataService(_logger)`.
  method `fetchData()`:
    call `_logger.log('Fetching data...')`.
    simulate async operation.
    call `_logger.log('Data fetched successfully.')`.

Define `ServiceLocator` class:
  private static `ServiceLocator` instance `_instance`.
  private `Map<Symbol, Service>` `_services`.
  private constructor `_internal()`.
  factory `ServiceLocator()` returns `_instance`.

  method `register<T extends Service>(T service)`:
    create Symbol `serviceKey` from type `T`.
    if `_services` contains `serviceKey`, print warning.
    add `service` to `_services` with `serviceKey`.

  method `get<T extends Service>()`:
    create Symbol `serviceKey` from type `T`.
    get `service` from `_services` using `serviceKey`.
    if `service` is null, throw `StateError`.
    return `service` cast to `T`.

  method `isRegistered<T extends Service>()`:
    create Symbol `serviceKey` from type `T`.
    return `_services.containsKey(serviceKey)`.

  method `unregisterAll()`:
    clear `_services`.

In `main` function:
  create `ServiceLocator` instance.
  register `Logger` instance.
  get `Logger` instance from locator.
  register `DataService` instance, passing the obtained `Logger`.
  get `DataService` instance from locator.
  call `fetchData()` on `DataService` instance.
  get `Logger` instance again and call `log()`.
  try to get an unregistered service (e.g., `String`) and catch the error.
  use `isRegistered` to check for services.