Dart中错误处理的功能性方法

在向新技术过渡时,我们失去了通常的开发工具。 在某些情况下,由于某些技术限制,我们不得不忍受他们的缺席,但如果可能,我们会随身携带这些工具。 在开发android应用程序时,我以Fernando Cejas提出干净架构为例 。 了解Flutter中使用的设计模式后,我决定放弃此架构,转而使用BLoC。 我很快就习惯了这个模板,它与我之前使用的MVVM非常相似,但是我不想忍受一个细节。 调用存储库方法时,我必须捕获异常,将其转换为某种类型,并根据类型创建必要的状态。 在我看来,这非常混乱,我移植了先前在基于Fernando的android项目中使用的Either类型。


两者都来自函数式编程语言。 它提供以下一种可能类型的值:


  • 左(如果发生故障);
  • 正确(如果成功)。

Either的基本实现
/// Signature of callbacks that have no arguments and return right or left value. typedef Callback<T> = void Function(T value); /// Represents a value of one of two possible types (a disjoint union). /// Instances of [Either] are either an instance of [Left] or [Right]. /// FP Convention dictates that: /// [Left] is used for "failure". /// [Right] is used for "success". abstract class Either<L, R> { Either() { if (!isLeft && !isRight) throw Exception('The ether should be heir Left or Right.'); } /// Represents the left side of [Either] class which by convention is a "Failure". bool get isLeft => this is Left<L, R>; /// Represents the right side of [Either] class which by convention is a "Success" bool get isRight => this is Right<L, R>; L get left { if (this is Left<L, R>) return (this as Left<L, R>).value; else throw Exception('Illegal use. You should check isLeft() before calling '); } R get right { if (this is Right<L, R>) return (this as Right<L, R>).value; else throw Exception('Illegal use. You should check isRight() before calling'); } void either(Callback<L> fnL, Callback<R> fnR) { if (isLeft) { final left = this as Left<L, R>; fnL(left.value); } if (isRight) { final right = this as Right<L, R>; fnR(right.value); } } } class Left<L, R> extends Either<L, R> { final L value; Left(this.value); } class Right<L, R> extends Either<L, R> { final R value; Right(this.value); } 

该实现非常基础,不如其他语言的解决方案,但可以应付其任务。 我将这种类型用作存储库的所有方法的结果,并将异常处理转移到数据层。 这消除了对try / catch构造的需要,这使代码更具可读性。


尝试/捕获示例
 class ContactBloc { final ContactRepository contactRepository; ContactBloc(this.contactRepository); @override Stream<ContactState> mapEventToState(ContactEvent event) async* { if (event is GetContactEvent) { yield LoadContactState(); try { var contact = contactRepository.getById(event.id); yield ContactIsShowingState(contact); } on NetworkConnectionException catch (e) { yield NetworkExceptionState(e); } catch (e) { yield UnknownExceptionState(e); } } } } abstract class ContactRepository { Future<Contact>getById(int id); } 

任一示例
 class ContactBloc { final ContactRepository contactRepository; ContactBloc(this.contactRepository); @override Stream<ContactState> mapEventToState(ContactEvent event) async* { if (event is GetContactEvent) { yield LoadContactState(); final either = contactRepository.getById(event.id); if (either.isRight) { final contact = either.right; yield ContactIsShowingState(contact); } else { final failure = either.left; if (failure is NetworkFailure) yield NetworkFailureState(failure); if (failure is UnknownFailure) yield UnknownFailureState(failure); } } } } abstract class ContactRepository { Future<Either<Failure, Contact>>getById(int id); } 

关于可读性,可能有人会争论。 也许有人习惯尝试/捕捉并以他自己的方式正确,在大多数情况下,这就是调味。 另一个好处是,我们自己可以定义“失败”层次结构并在左侧返回。 让我们做一个抽象的故障,将其与所有继承继承的ServerFailure,NetworkFailure功能以及当前功能的某些ContactFailure特定功能通用。 在该区块中,我们将确切知道预期会发生哪种故障。


在Dart上执行Failure的缺点是缺乏象kotlin中那样的密封类 ,否则就不会使用强制转换。 该语言还很年轻,正在积极发展,我希望时间到了,我们将拥有可以更简洁地编写处理程序的工具。


有人可能不喜欢这种实现,可能会觉得毫无意义,但我只是想向您介绍Dart中采用功能性方法进行错误处理的可能性,尽管使用起来并没有其他语言那么优雅。


资源:


源代码

Source: https://habr.com/ru/post/zh-CN459757/


All Articles