get_itで依存性注入を行う

 # はじめに
Flutterの開発で依存性注入（DI: Dependency Injection）を
シンプルに行うためのパッケージとして `get_it` があります。
この記事では、 `get_it` パッケージの基本的な使い方やメリットについて説明します。

:::note info
**依存性注入とは？**
依存性注入とは、クラスが必要とする依存関係（他のクラスやサービス）を
外部から注入する設計パターンです。
これにより、コードの再利用性やテストのしやすさが向上します。
:::



## get_itとは？

`get_it` は、シンプルなサービスロケーターパッケージです。
主に以下のような機能を提供しており、依存関係管理を容易にします。


### 機能
- **サービスの登録と取得:** 抽象クラスや実装クラスを登録し、
アプリ全体で簡単にアクセス可能
- **依存関係の注入:** コンポーネントに必要なサービスを自動的に生成・提供
- **モックオブジェクトの利用:** テストにおいて、実際のサービスを
モックオブジェクトに置き換えて容易に検証可能
- **スコープ管理:** 登録されたサービスの有効範囲を制御し、不要なメモリ使用を抑制

### 基本メソッド
| メソッド名 | 説明|
|----|----|
|registerSingleton<T>(T instance) |シングルトンとしてインスタンスを登録する。アプリ全体で同一インスタンスが使用される。|
|registerFactory<T>(T Function() factoryFunc) |	ファクトリーとして登録し、呼び出しごとに新しいインスタンスを生成する。|	
|get<T>()|	登録された依存性を取得する。	|
|isRegistered<T>({Object instance})|	指定されたタイプの依存性が登録されているかどうかを確認する。|
|unregister<T>({Object instance, bool disposing})|	指定されたタイプの依存性を登録解除する。disposingがtrueの場合、登録解除時にインスタンスの破棄を行う。|
|reset()|すべての登録された依存性を解除する。|
|resetLazySingleton<T>( {Object instance}) |指定されたタイプの遅延シングルトンをリセットする。次回アクセス時に再作成される。|
|allowReassignment |同じタイプの依存性を再登録できるようにするフラグを設定する。|
|registerLazySingleton<T>(T Function() factoryFunc)	|初回アクセス時にインスタンスを生成する遅延シングルトンを登録する。|
|getAsync<T>() |非同期に依存性を取得する。|

## get_it パッケージの導入
`pubspec.yaml` に `get_it` を追加します。

https://pub.dev/packages/get_it

```yaml
dependencies:
  flutter:
    sdk: flutter
  get_it: ^7.2.0  # 最新バージョンを確認してください
```

# 基本的な使い方
`get_it` を使うためには、まず、シングルトンインスタンスを取得し、
必要なクラスやサービスを登録する必要があります。

## 1. シングルトンのインスタンス取得
まず、 `GetIt` のシングルトンインスタンスを取得します。

```dart
import 'package:get_it/get_it.dart';

final getIt = GetIt.instance;
```

## 2. クラス(依存性)の登録
次に、依存性を登録します。
必要なクラスやサービスを登録します。
※ここでは、 `MyService` というサービスクラスを登録しています。

```dart
class MyService {
  void doSomething() {
    print('Doing something...');
  }
}

void setupLocator() {
  getIt.registerSingleton<MyService>(MyService());
}
// setupLocator 関数はアプリのエントリーポイントで一度呼び出します。
```

```dart
void main() {
  setupLocator();
  runApp(MyApp());
}
```

## 3. クラス(依存性)の取得
登録したクラスやサービスを取得して使用します。

```dart
import 'package:flutter/material.dart';

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final myService = getIt<MyService>();

    return Scaffold(
      appBar: AppBar(
        title: Text('GetIt Example'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            myService.doSomething();
          },
          child: Text('Do Something'),
        ),
      ),
    );
  }
}
```

# get_it のメリット
- **グローバルアクセス:** どこからでも依存関係にアクセスできるため、コードの管理が容易になります。
- **テストのしやすさ:** 依存関係を簡単にモックに置き換えることができるため、テストが容易になります。
- **ライフサイクル管理:** シングルトンやファクトリーのライフサイクルを明確に管理することができます。
- **可読性と保守性の向上:** 依存性の管理がシンプルになることで、コードの可読性と保守性が向上する。

# get_itを使用した実践例
より具体的なユーザー認証アプリの例を通じて、get_itの使用方法を説明します。

## ステップ1：依存性の登録
まず、setupLocator関数で必要な依存性を登録します

```dart
import 'package:get_it/get_it.dart';

final getIt = GetIt.instance;

void setupLocator() {
  getIt.registerSingleton<ApiService>(ApiService());
  getIt.registerSingleton<AuthService>(AuthService(getIt<ApiService>()));
  getIt.registerSingleton<UserRepository>(UserRepository(getIt<AuthService>()));
}

void main() {
  setupLocator();
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: MyApp(),
    );
  }
}
```

## ステップ2：サービスクラスの実装
次に、ApiServiceとAuthServiceのサービスクラスを実装します

```dart
class ApiService {
  String fetchData() {
    return 'Fetched Data';
  }
}

class AuthService {
  final ApiService apiService;

  AuthService(this.apiService);

  void login() {
    // ログイン処理
    apiService.fetchData();
    print('User logged in');
  }
}
```

## ステップ3：リポジトリクラスの実装
リポジトリクラスを実装して、サービスクラスを利用します。

```dart
class UserRepository {
  final AuthService authService;

  UserRepository(this.authService);

  void authenticateUser() {
    authService.login();
    print('User authenticated');
  }
}
```
## ステップ4：UIの実装
最後に、UIを実装します

```dart
class MyApp extends StatelessWidget {
  final _userRepository = getIt<UserRepository>();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Home Page'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            _userRepository.authenticateUser();
          },
          child: Text('Authenticate User'),
        ),
      ),
    );
  }
}
```

## 5. テストでのget_itの活用
get_itを使用すると、依存性の注入を簡単にモックに置き換えることができます。

```dart
void main() {
  setUpAll(() {
    getIt.registerSingleton<ApiService>(MockApiService());
    getIt.registerSingleton<AuthService>(MockAuthService(getIt<ApiService>()));
    getIt.registerSingleton<UserRepository>(UserRepository(getIt<AuthService>()));
  });

  tearDown(() {
    getIt.reset();
  });

  test('authenticateUser test', () {
    final userRepository = getIt<UserRepository>();
    userRepository.authenticateUser;
  });
}
```

- **依存性の登録:** `setupLocator` 関数内で依存性を登録します。 
`ApiService` , `AuthService` , `UserRepository` の各クラスを `get_it` に登録します。

- **サービスクラスの実装:** `ApiService` と `AuthService` を実装し、
 `AuthService` では `ApiService` を利用します。

- **リポジトリクラスの実装:** `UserRepository` を実装し、 
`AuthService` を利用してユーザー認証を行います。

- **UIの実装:** UI でリポジトリを利用し、ボタンが押されたときに
ユーザー認証を実行します。

- **テストでのget_itの活用:** テスト環境で `get_it` を使用し、
モックサービスを登録して依存性注入を行い、テストケースを実行します。

# まとめ
`get_it` パッケージを使用することで、依存性管理が大幅に簡潔になります。
これにより、コードの保守性が向上し、テストの作成も容易になるため、
アプリケーションの設計がより柔軟で保守しやす形になります。
これを機にぜひ活用してみてください。

# 告知
最後にお知らせとなりますが、イーディーエーでは一緒に働くエンジニアを
募集しております。詳しくは採用情報ページをご確認ください。

https://eda-inc.jp/recruit/

みなさまからのご応募をお待ちしております。