Skip to content

Commit

Permalink
Merge pull request #3 from zooper-lib/feature/pass-parameters
Browse files Browse the repository at this point in the history
passing parameters
  • Loading branch information
DirtyNative authored Feb 26, 2024
2 parents 86fecc8 + b17cfbd commit 536084c
Show file tree
Hide file tree
Showing 7 changed files with 247 additions and 113 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
## 1.0.0

- Possibility to pass arguments to factory resolutions
- replaced `getFirst()` with `resolve()`
- replaced `getFirstOrNull()` with `resolveOrNull()`
- replaced `getAll()` with `resolveAll()`

## 0.0.9

- Implemented usage of zef_log_core
Expand Down
118 changes: 109 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,25 +36,125 @@ void main() {
}
```

### Registering Services
### Instances

#### Simple registration

To register an instance you directly pass an instance of the object you want to have registered:

```dart
ServiceLocator.I.registerInstance(MyService());
```

And to resolve that instance you call the `resolve()` method:

```dart
final MyService myService = ServiceLocator.I.resolve<MyService>();
```

---

**NOTE**

You can register the same instance multiple times if you have set this in `ServiceLocatorConfig`. This is turned on by default.
The method `resolve()` will then return the first registered instance by default, but you can also get the last registered with:

```dart
final MyService myService = ServiceLocator.I.resolve<MyService>(resolveFirst: false);
```

The same principle applies to the following registration option

---

#### Named registration

You can pass a name with your registration.

```dart
ServiceLocator.I.registerInstance(MyService(), name: 'One');
ServiceLocator.I.registerInstance(MyService(), name: 'Two');
```

This way you can resolve different instances with ease:

```dart
final MyService myService = ServiceLocator.I.resolve<MyService>(name: 'one'); // Will return the instance with name `one`
final MyService myService = ServiceLocator.I.resolve<MyService>(name: 'two'); // Will return the instance with name `two`
```

#### Keyed registration

The same principle as named registrations, but with a different property

#### Environmental registration

The same principle as named registrations, but with a different property. Mostly used to define your instances under different environments like "dev", "test", "prod", ...

### Factory registration

#### Simple registration

The difference here to the instance registration is, that you provide a function which tells the framework how to create the instance.

```dart
ServiceLocator.I.registerFactory<MyService>(
(serviceLocator, namedArgs) => MyService(),
);
```

And to resolve, you do the same as with the instance resolving:

```dart
ServiceLocator.I.registerInstance(YourService());
ServiceLocator.I.registerFactory(() => YourService());
final MyService myService = ServiceLocator.I.resolve<MyService>();
```

### Resolving Services
So every time we call `resolve()` on a as factory registered type we will create a new instance of this class.

---

**NOTE**

The difference to `registerInstance()` is, that with a factory you construct the object at runtime.

---

#### Resolving with parameters

Regardless of the underlying DI framework, you can resolve services through our unified interface:
One feature for factories is, that you can pass arguments to resolve the instance.
First you need to tell the framework how to resolve the factory:

```dart
// For single service resolution
final myService = ServiceLocator.I.getFirst<MyServiceInterface>();
ServiceLocator.I.registerFactory<UserService>(
(ServiceLocator locator, Map<String, dynamic> namedArgs) => UserService(
id: namedArgs['theUserId'] as UserId, // This is how your parameter will be provided
username: namedArgs['theUsername'] as String, // This is how your parameter will be provided
password: namedArgs['thePassword'] as String, // This is how your parameter will be provided
),
);
```

As you can see the Function to register a factory has two parameters:

- ServiceLocator locator
- Map<String, dynamic> namedArgs

// For multiple services under the same interface
final myServices = ServiceLocator.I.getAll<MyServiceInterface>();
The locator is just an instance of the `ServiceLocator`. As this is a singleton, you definitly can discard it and directly use `ServiceLocator.I.` if you want to pass an object which is already registered within the DI system.
The namedArgs is a Map of arguments you will pass when trying to resolve a factory:

```dart
final UserService userService =
ServiceLocator.I.resolve<UserService>(
namedArgs: {
'theUserId': UserId('1'),
'theUsername': 'HansZimmer123',
'thePassword': 'blafoo1!',
},
);
```

If you don't pass a required named argument, a `TypeError` will be thrown.

## Customization and Extensibility

Our package's design encourages customization and extensibility. By creating adapters for your chosen DI frameworks, you can leverage our wrapper's features while utilizing the specific functionalities and optimizations of those frameworks.
Expand Down
145 changes: 59 additions & 86 deletions lib/src/concrete_service_locator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class ConcreteServiceLocator implements ServiceLocator {
name: name,
key: key,
environment: environment,
allowMultipleInstances: _config.allowMultipleInstances,
);

// On conflict
Expand All @@ -43,22 +44,16 @@ class ConcreteServiceLocator implements ServiceLocator {

// On internal error
if (response.isThird) {
if (_config.throwErrors) {
throw StateError(internalErrorOccurred(response.third.message));
} else {
Logger.I.fatal(
message: internalErrorOccurred(response.third.message),
error: response.third.message,
stackTrace: StackTrace.current,
);
return;
}
throw StateError(internalErrorOccurred(response.third.message));
}
}

@override
void registerFactory<T extends Object>(
T Function(ServiceLocator serviceLocator) factory, {
T Function(
ServiceLocator serviceLocator,
Map<String, dynamic> namedArgs,
) factory, {
List<Type>? interfaces,
String? name,
dynamic key,
Expand All @@ -70,6 +65,7 @@ class ConcreteServiceLocator implements ServiceLocator {
name: name,
key: key,
environment: environment,
allowMultipleInstances: _config.allowMultipleInstances,
);

// On conflict
Expand All @@ -84,30 +80,56 @@ class ConcreteServiceLocator implements ServiceLocator {

// On internal error
if (response.isThird) {
if (_config.throwErrors) {
throw StateError(internalErrorOccurred(response.third.message));
} else {
Logger.I.fatal(
message: internalErrorOccurred(response.third.message),
error: response.third.message,
stackTrace: StackTrace.current,
);
return;
}
throw StateError(internalErrorOccurred(response.third.message));
}
}

@override
T resolve<T extends Object>({
Type? interface,
String? name,
dynamic key,
String? environment,
Map<String, dynamic>? namedArgs,
bool resolveFirst = true,
}) {
final response = _adapter.resolve<T>(
name: name,
key: key,
environment: environment,
namedArgs: namedArgs ?? {},
resolveFirst: resolveFirst,
);

// On not found
if (response.isSecond) {
// No need to check if throwErrors is true, as we cannot return null here
throw StateError(noRegistrationFoundForType(T));
}

// On internal error
if (response.isThird) {
throw StateError(internalErrorOccurred(response.third.message));
}

return response.first;
}

@override
T? getFirst<T extends Object>({
T? resolveOrNull<T extends Object>({
Type? interface,
String? name,
dynamic key,
String? environment,
Map<String, dynamic>? namedArgs,
bool resolveFirst = true,
}) {
final response = _adapter.getFirst<T>(
final response = _adapter.resolve<T>(
name: name,
key: key,
environment: environment,
namedArgs: namedArgs ?? {},
resolveFirst: resolveFirst,
);

// On not found
Expand All @@ -122,32 +144,25 @@ class ConcreteServiceLocator implements ServiceLocator {

// On internal error
if (response.isThird) {
if (_config.throwErrors) {
throw StateError(internalErrorOccurred(response.third.message));
} else {
Logger.I.fatal(
message: internalErrorOccurred(response.third.message),
error: response.third.message,
stackTrace: StackTrace.current,
);
return null;
}
throw StateError(internalErrorOccurred(response.third.message));
}

return response.first;
}

@override
List<T> getAll<T extends Object>({
List<T> resolveAll<T extends Object>({
Type? interface,
String? name,
dynamic key,
String? environment,
Map<String, dynamic>? namedArgs,
}) {
final response = _adapter.getAll<T>(
final response = _adapter.resolveAll<T>(
name: name,
key: key,
environment: environment,
namedArgs: namedArgs ?? {},
);

// On not found
Expand All @@ -162,16 +177,7 @@ class ConcreteServiceLocator implements ServiceLocator {

// On internal error
if (response.isThird) {
if (_config.throwErrors) {
throw StateError(internalErrorOccurred(response.third.message));
} else {
Logger.I.fatal(
message: internalErrorOccurred(response.third.message),
error: response.third.message,
stackTrace: StackTrace.current,
);
return [];
}
throw StateError(internalErrorOccurred(response.third.message));
}

return response.first;
Expand All @@ -193,22 +199,16 @@ class ConcreteServiceLocator implements ServiceLocator {

// On internal error
if (response.isSecond) {
if (_config.throwErrors) {
throw StateError(response.second.message);
} else {
Logger.I.fatal(
message: internalErrorOccurred(response.second.message),
error: response.second.message,
stackTrace: StackTrace.current,
);
return;
}
throw StateError(internalErrorOccurred(response.second.message));
}
}

@override
void overrideFactory<T extends Object>(
T Function(ServiceLocator serviceLocator) factory, {
T Function(
ServiceLocator serviceLocator,
Map<String, dynamic> namedArgs,
) factory, {
String? name,
dynamic key,
String? environment,
Expand All @@ -222,16 +222,7 @@ class ConcreteServiceLocator implements ServiceLocator {

// On internal error
if (response.isSecond) {
if (_config.throwErrors) {
throw StateError(response.second.message);
} else {
Logger.I.fatal(
message: internalErrorOccurred(response.second.message),
error: response.second.message,
stackTrace: StackTrace.current,
);
return;
}
throw StateError(internalErrorOccurred(response.second.message));
}
}

Expand Down Expand Up @@ -259,16 +250,7 @@ class ConcreteServiceLocator implements ServiceLocator {

// On internal error
if (response.isThird) {
if (_config.throwErrors) {
throw StateError(response.third.message);
} else {
Logger.I.fatal(
message: internalErrorOccurred(response.third.message),
error: response.third.message,
stackTrace: StackTrace.current,
);
return;
}
throw StateError(internalErrorOccurred(response.second.message));
}
}

Expand All @@ -278,16 +260,7 @@ class ConcreteServiceLocator implements ServiceLocator {

// On internal error
if (response.isSecond) {
if (_config.throwErrors) {
throw StateError(response.second.message);
} else {
Logger.I.fatal(
message: internalErrorOccurred(response.second.message),
error: response.second.message,
stackTrace: StackTrace.current,
);
return;
}
throw StateError(internalErrorOccurred(response.second.message));
}
}
}
Loading

0 comments on commit 536084c

Please sign in to comment.