Как провести рефакторинг обработки функциональных ошибок во флаттере с помощью dartz

У меня есть два метода

@override
  Future<Option<StorageFailure>> init() async {
    final root = await getRootDir();
    return root.fold(
      (failure) => some(failure),
      (dir) {
        final images = Directory("$dir/images");
        final videos = Directory("$dir/videos");
        images.create(); // more error handling here (try|either)
        videos.create();
      },
    );
  }

  @override
  Future<Either<StorageFailure, Directory>> getRootDir() async {
    try {
      final root = await getApplicationDocumentsDirectory();
      return right(Directory("${root.path}/files"));
    } catch (e) {
      return left(StorageFailure(reason: e.toString()));
    }
  }

На initпосле сворачивания мне нужно больше обрабатывать ошибки, но я не люблю слишком много вкладывать свой код. Также я не знаю, как вернутьFailure из right функция.

Как лучше связать эти ценности?

1 ответ

Решение

Самое замечательное в Either заключается в том, что вы можете объединить действия в цепочку и позаботиться об обработке ошибок только один раз в конце. Если программе не удается получить корневой каталог, часть создания подкаталогов мультимедиа никогда не выполняется. Таким образом, нет необходимости во вложенной обработке ошибок.

Я сам изучаю функциональное программирование. Так что может быть даже лучшее решение, но вот как я бы это сделал:

// I intentionally added all type annotations for better understanding.
Future<Option<StorageFailure>> init() async {
  final Either<StorageFailure, Directory> root = await getRootDir();
  final Either<StorageFailure, Either<StorageFailure, Success>> result = await root.traverseFuture(createImagesAndVideosSubfolders);
  final Either<StorageFailure, Success> flattenedResult = result.flatMap(id);
  return flattenedResult.fold((failure) => some(failure), (success) => none());
}

Future<Either<StorageFailure, Success>> createImagesAndVideosSubfolders(Directory dir) async {
  try {
    await Directory('${dir.path}/images').create();
    await Directory('${dir.path}/videos').create();
    return right(Success('success'));
  } catch (e) {
    return left(StorageFailure(reason: e.toString()));
  }
}
  1. получить корневой каталог
  2. создать каталоги мультимедиа (если getRootDir не удалось, метод root.traverseFuture просто возвращает существующий StorageFailure)
  3. Сгладьте вложенные Eithers с помощью flatMap и функции id. Метод traverseFuture - это версия карты для фьючерсов. Если бы существовал эквивалент flatMap и для фьючерсов, результат не был бы заключен в один, и последний шаг для сглаживания результата не понадобился бы.

Вы можете еще больше упростить функцию инициализации, просто вернув Either вместо преобразования ее в Option.

Вот так:

Future<Either<StorageFailure, Success>> init() async {
  ...
  return result.flatMap(id);
}
Другие вопросы по тегам