Flutterでポケモンバトルを実装する(Step6: DomainServiceを実装する)

## はじめに
前回までの続きです。今回はDomainServieでポケモンバトルを実装していきます。

https://qiita.com/koji0705/items/942c9c1fcdc889e2e5cb

## ソースコード
```shell:ディレクトリ構成
├── domain
│   ├── entities
│   │   ├── move.dart
│   │   ├── pokemon.dart
│   │   └── stats.dart
│   ├── enums
│   │   └── pokemon_type.dart
│   ├── repositories
│   │   └── pokemon_repository.dart
│   └── services
│       └── battle_service.dart
```

```dart:lib/domain/services/battle_service.dart
import '../repositories/pokemon_repository.dart';
import '../entities/pokemon.dart';
import '../entities/move.dart';

class BattleService {
  final PokemonRepository _repository;

  // 名前付きパラメータを定義
  BattleService({required PokemonRepository repository})
      : _repository = repository;

  Future<void> startBattle(String name1, String name2) async {
    final pokemon1 = await _repository.getPokemon(name1);
    final pokemon2 = await _repository.getPokemon(name2);
    print('Battle started between ${pokemon1.name} and ${pokemon2.name}');
    // バトルロジックをここに追加
  }

  void useMove(Pokemon attacker, Pokemon defender, Move move) {
    print('${attacker.name} uses ${move.name} on ${defender.name}');
    // ダメージ計算やその他のバトルロジックをここに追加
  }

  void calculateDamage(Pokemon attacker, Pokemon defender, Move move) {
    // 簡単なダメージ計算ロジック
    int damage = (move.power * attacker.stats.attack) ~/ defender.stats.defense;
    print('${defender.name} receives $damage damage');
    // ここで防御側のHPを減らすロジックを追加する
  }
}
```

:::note info
コンストラクタでインスタンスを作成。インスタンスに紐づくデータアクセスを実現している。
:::

## テストコードの実装
```dart:test/domain/services/battle_service_test.dart
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';
import 'package:mockito/annotations.dart';
import 'package:pokemon_battle_app/domain/entities/move.dart';
import 'package:pokemon_battle_app/domain/entities/pokemon.dart';
import 'package:pokemon_battle_app/domain/entities/stats.dart';
import 'package:pokemon_battle_app/domain/enums/pokemon_type.dart';
import 'package:pokemon_battle_app/domain/repositories/pokemon_repository.dart';
import 'package:pokemon_battle_app/domain/services/battle_service.dart';

import 'battle_service_test.mocks.dart';

@GenerateMocks([PokemonRepository])
void main() {
  group('BattleService', () {
    final mockRepository = MockPokemonRepository();
    final battleService = BattleService(repository: mockRepository);

    final pikachu = Pokemon(
      name: 'Pikachu',
      imageUrl:
          'https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/25.png',
      moves: [Move(name: 'Thunderbolt', power: 90)],
      stats: Stats(hp: 35, attack: 55, defense: 40, speed: 90),
      type: PokemonType.electric,
    );

    final charmander = Pokemon(
      name: 'Charmander',
      imageUrl:
          'https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/4.png',
      moves: [Move(name: 'Flamethrower', power: 90)],
      stats: Stats(hp: 39, attack: 52, defense: 43, speed: 65),
      type: PokemonType.fire,
    );

    setUp(() {
      // setUp関数内でモックの設定を行う
      when(mockRepository.getPokemon('Pikachu'))
          .thenAnswer((_) async => pikachu);
      when(mockRepository.getPokemon('Charmander'))
          .thenAnswer((_) async => charmander);
    });

    test('should start a battle and print the correct message', () async {
      await battleService.startBattle('Pikachu', 'Charmander');

      verify(mockRepository.getPokemon('Pikachu')).called(1);
      verify(mockRepository.getPokemon('Charmander')).called(1);
      // コンソール出力の検証は通常のユニットテストでは行いませんが、
      // 必要であれば `print` のモックを作成することもできます
    });

    test('should use a move and print the correct message', () {
      battleService.useMove(pikachu, charmander, pikachu.moves.first);
      // コンソール出力の検証は通常のユニットテストでは行いませんが、
      // 必要であれば `print` のモックを作成することもできます
    });

    test('should calculate damage correctly', () {
      battleService.calculateDamage(pikachu, charmander, pikachu.moves.first);
      // コンソール出力の検証は通常のユニットテストでは行いませんが、
      // 必要であれば `print` のモックを作成することもできます
    });
  });
}

```

:::note info
今回はMockを利用しています。Mockは`build_runner: ^2.1.11`で作るので、事前にインストールしてください。
その後は、`flutter pub run build_runner build`でMockが作成できます。
:::