[Flutter] 依存性注入って何?

Flutterは、依存関係注入(Dependency Injection、DI)をサポートしています。依存関係注入は、アプリケーションのコンポーネント間で依存関係を緩和するための設計パターンであり、コードの再利用性、テスト容易性、そして可読性を向上させることができます。

目次

基本的な考え方

依存関係注入の基本的な考え方は、クラスが自分で依存しているオブジェクトを作成するのではなく、外部から提供されたオブジェクトを使用することです。これにより、クラスは、自身の機能に必要なオブジェクトを取得し、外部からの入力に対してより柔軟になります。

Flutterでは、依存関係注入を実現するために、依存性注入(Dependency Injection、DI)パッケージを利用することができます。DIパッケージは、アプリケーションで使用される各クラスやモジュールのインスタンスを管理するためのコンテナを提供します。コンテナには、各クラスやモジュールに関する情報が含まれており、アプリケーション内の異なる場所で簡単に取得できます。

モジュールは、依存性注入において関連する依存オブジェクトをグループ化するための仕組みです。モジュールは通常、特定の機能やコンポーネントの依存性をまとめ、依存オブジェクトの提供方法やライフサイクルを定義します。モジュールは、アプリケーション内での依存関係の整理と保守性の向上に役立ちます。

基本的な使い方

DIパッケージを使用するには、まず、依存性を注入するクラスを定義する必要があります。依存性を注入するためには、注入されるクラスのコンストラクターに依存するオブジェクトを引数として渡します。次に、依存性を注入するクラスのインスタンスを作成する際に、DIコンテナを使用して依存関係を解決します。

以下は、Flutterで依存性注入を使用するための簡単な例です。まず、注入されるクラスを定義します。

class MyService {
  final MyDependency myDependency;
  
  MyService(this.myDependency);
  
  void doSomething() {
    myDependency.doSomethingElse();
  }
}

class MyDependency {
  void doSomethingElse() {
    print("Did something else!");
  }
}

上記の例では、MyServiceクラスがMyDependencyクラスに依存しています。MyServiceのコンストラクターにMyDependencyオブジェクトを渡すことによって、依存性を注入します。MyServicedoSomething()メソッドは、MyDependencyオブジェクトを使用して何か別の処理を行うことができます。

次に、DIコンテナを使用して依存性を解決します。

void main() {
  final container = DIContainer();
  
  container.register<MyDependency>(() => MyDependency());
  container.register<MyService>(() => MyService(container.resolve<MyDependency>()));
  
  final myService = container.resolve<MyService>();
  myService.doSomething();
}

上記の例では、DIContainerオブジェクトを作成し、MyDependencyMyServiceのインスタンスをDIコンテナに登録します。MyServiceのインスタンスを作成する際に、container.resolve<MyDependency>()を使用して、MyDependencyオブジェクトを取得して、MyServiceのコンストラクターに渡します。最後に、myService.doSomething()を呼び出すことで、MyServicedoSomething()メソッドが実行され、MyDependencyオブジェクトが使用されます。

具体的に使ってみる


ここでは、Flutterの依存性注入を実現するために使用できるいくつかのパッケージのうち、代表的なものの一つであるget_itパッケージを使用したサンプルコードを紹介します。

get_itパッケージは、単純なシングルトンを簡単に実装するために使用されます。get_itパッケージを使用すると、クラスを登録し、必要に応じてインスタンスを取得できます。以下の例では、MyServiceクラスに依存するMyHomePageウィジェットがあります。

まず、get_itパッケージをプロジェクトに追加します。

dependencies:
  flutter:
    sdk: flutter
  get_it: ^7.6.0

インスタンスの登録・呼び出し

次に、get_itパッケージを使用して、MyServiceのインスタンスを登録します。

class MyService {
  void doSomething() {
    print('Hello, world!');
  }
}
import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart';

import 'my_service.dart';

void main() {
  // インスタンスを取得するための依存性注入コンテナを作成します。
  final getIt = GetIt.instance;

  // MyServiceクラスのインスタンスを登録します。
  getIt.registerSingleton<MyService>(MyService());

  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // MyServiceのインスタンスを取得します。
    final myService = GetIt.instance.get<MyService>();

    return Scaffold(
      appBar: AppBar(
        title: Text('My App'),
      ),
      body: Center(
        child: RaisedButton(
          onPressed: () {
            // MyServiceのメソッドを呼び出します。
            myService.doSomething();
          },
          child: Text('Say Hello'),
        ),
      ),
    );
  }
}

上記の例では、MyServiceクラスのインスタンスをget_itパッケージを使用して登録しました。MyHomePageウィジェットのbuildメソッドでは、GetIt.instance.get<MyService>()を使用して、MyServiceのインスタンスを取得し、onPressedコールバック内でMyServicedoSomethingメソッドを呼び出しています。

ファクトリー関数を作成する

get_itパッケージでは、registerFactoryメソッドを使用して、新しいインスタンスを生成するファクトリー関数を登録することができます。以下の例では、MyServiceに依存するMyOtherServiceクラスがあり、MyOtherServiceのインスタンスをファクトリー関数で作成する方法を示します。

ファクトリーインスタンスは、必要に応じて新しいインスタンスを生成するためのメソッドや関数です。依存性注入において、ファクトリーインスタンスを使用すると、各要求に対して新しいインスタンスが作成されます。これにより、同じクラスの異なるインスタンスを異なる要求ごとに提供することができます。

class MyService {
  void doSomething() {
    print('Hello, world!');
  }
}
import 'my_service.dart';

class MyOtherService {
  final MyService myService;

  MyOtherService(this.myService);

  void doSomethingElse() {
    myService.doSomething();
    print('Hello, again!');
  }
}
import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart';

import 'my_other_service.dart';
import 'my_service.dart';

void main() {
  // インスタンスを取得するための依存性注入コンテナを作成します。
  final getIt = GetIt.instance;

  // MyServiceクラスのインスタンスを登録します。
  getIt.registerSingleton<MyService>(MyService());

  // MyOtherServiceクラスのインスタンスをファクトリー関数で登録します。
  getIt.registerFactory<MyOtherService>(() => MyOtherService(getIt<MyService>()));

  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // MyOtherServiceのインスタンスを取得します。
    final myOtherService = GetIt.instance<MyOtherService>();

    return Scaffold(
      appBar: AppBar(
        title: Text('My App'),
      ),
      body: Center(
        child: RaisedButton(
          onPressed: () {
            // MyOtherServiceのメソッドを呼び出します。
            myOtherService.doSomethingElse();
          },
          child: Text('Say Hello Again'),
        ),
      ),
    );
  }
}

上記の例では、MyOtherServiceクラスのインスタンスをファクトリー関数で登録しました。MyOtherServiceクラスのコンストラクターは、MyServiceのインスタンスを受け取ります。MyHomePageウィジェットのbuildメソッドでは、GetIt.instance<MyOtherService>()を使用して、MyOtherServiceのインスタンスを取得し、onPressedコールバック内でMyOtherServicedoSomethingElseメソッドを呼び出しています。

シングルトンインスタンスを登録する

get_itパッケージには、シングルトンインスタンスを登録するためのregisterSingletonメソッドがあります。これは、アプリケーションのライフサイクル全体で1つのインスタンスを使用する場合に便利です。以下は、registerSingletonメソッドを使用して、MyServiceクラスのインスタンスを登録する例です。

シングルトンは、アプリケーション内で唯一のインスタンスを持つクラスのデザインパターンです。シングルトンクラスは通常、最初の要求時に初期化され、その後は同じインスタンスが再利用されます。これにより、アプリケーション内の複数の部分から共有されるデータや状態を効果的に管理することができます。

class MyService {
  void doSomething() {
    print('Hello, world!');
  }
}
import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart';

import 'my_service.dart';

void main() {
  // インスタンスを取得するための依存性注入コンテナを作成します。
  final getIt = GetIt.instance;

  // MyServiceクラスのインスタンスをシングルトンで登録します。
  getIt.registerSingleton<MyService>(MyService());

  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // MyServiceのインスタンスを取得します。
    final myService = GetIt.instance<MyService>();

    return MaterialApp(
      title: 'Flutter Demo',
      home: MyHomePage(myService: myService),
    );
  }
}

class MyHomePage extends StatelessWidget {
  final MyService myService;

  MyHomePage({@required this.myService});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('My App'),
      ),
      body: Center(
        child: RaisedButton(
          onPressed: () {
            // MyServiceのメソッドを呼び出します。
            myService.doSomething();
          },
          child: Text('Say Hello'),
        ),
      ),
    );
  }
}

上記の例では、MyServiceクラスのインスタンスをシングルトンで登録し、MyHomePageウィジェットのコンストラクターで、MyServiceのインスタンスを受け取っています。buildメソッド内で、myService.doSomething()を呼び出しています。

以上のように、依存性注入を使用することで、アプリケーションのコードをより疎結合にすることができます。また、テスト性や保守性も向上させることができます。get_itパッケージは、Flutterアプリケーションで簡単に依存性注入を実装するための優れたツールです。

まとめ

依存性注入を使用すると、アプリケーションの各部分がより疎結合になり、よりテストしやすい、より柔軟で、より再利用可能なコードを作成することができます。Flutterの依存性注入は、DIパッケージを使用することで実現され、コンストラクターインジェクションをサポートします。コンストラクターインジェクションは、Flutterのアプリケーションで依存関係注入を実現するための簡単な方法です。

コンストラクターインジェクションは、依存性をクラスのコンストラクターを介して注入する手法です。依存性を必要とするクラスのコンストラクターパラメーターに依存オブジェクトを渡すことで、クラス内での依存関係の作成と解決を実現します。これにより、クラスのテストや再利用が容易になり、疎結合なコードを実現できます。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次