Dart / Flutter — применяя zero / empty объекты ко всему

от автора

Больше техническая заметка, чем статья, поэтому постараюсь изложить мысли как можно кратче.

Приходя из JS/TS мира, когда я впервые написал на Dart, самой прекрасной вещью, помимо многих было использование функций isEmpty или isNotEmpty для String, List, Map, и так далее. Это было невероятно просто и прекрасно не писать каждый раз  .length == 0.

Также, очень полезным паттерном были empty/zero значения как Duration.zero, Offset.zero, и другие.

Спустя время, я нашел привычку использовать похожий принцип для работы с различными случаями, а также пришел к мысли — что если мы используем такие значения для большей части объектов, избавляясь от null (не для всех случаев, но тем не менее)? Немного поискав, нашел похожий паттерн в Go и других языках, и продолжил думать:

Проблема

представим ситуацию:

String? value;  // do something  value = "new value”; 

Какие проверки мы должны сделать, чтобы быть уверенными что значение действительно существует и мы могли увидеть случай когда нужно применить значение, например по умолчанию? 🤔

final String newValue; if(value != null && value.isEmpty){   //    newValue = “defaultValue”; }  else {   newValue = value; } 

Какова разница проверки null и isEmpty в такой ситуации? 🤔
Что если мы напишем так:

String value = “”; final String newValue;  if(value.isEmpty){   // do something   newValue = “defaultValue”; } else {   newValue = value; }  // use newValue 

Лучше, но всё ещё verbose.
Возможно было бы здорово использовать inline ? Попробуем добавить extension:

extension StringX on String {   String whenEmptyUse(String value) => isEmpty ? value : this; }  final newValue = value.whenEmptyUse("defaultValue"); 

Что если мы добавим такой паттерн к lists, maps, values, и numbers?

extension ListX<T> on List<T> {   List<T> whenEmptyUse(List<T> values) => isEmpty ? values : this; }  final newValues = values.whenEmptyUse(['defaultValue']);  extension DoubleX on double {   bool get isZero => this == 0;   double whenZeroUse(final double value) =>; isZero ? value : this; }  final opacity = 0;  final newOpacity = opacity.whenZeroUse(0.5); 

Такой паттерн также может оказаться очень полезен с zero/empty значениями для объектов. Например:

class Person {   const Person({this.name=''});   final String name;    static const empty = Person();    bool get isEmpty => name.isEmpty;   bool get isNotEmpty => name.isNotEmpty;   // или можно использовать другие названия чтобы сохранить читабельность   bool get isInitialized => name.isNotEmpty;   bool get isNotInitialized => name.isEmpty; }  extension PersonX on Person {  Person whenEmptyUse(Person value) => isEmpty ? value : this; }  

И так мы можем использовать его везде — проверяя такой объект так же просто как String:

var person = Person.empty;  // do something dangerous  final newPerson = person.whenEmptyUse(Person(name:'Frodo')); 

Такой подход может быть полезен и для IDs, Lists с типами, и т.д.

Таким образом получается более простой API, мы всегда можем быть уверены в том, что мы знаем значение, и можем его валидировать значения по умолчанию (empty / zero).

Надеюсь, что этот концепт окажется полезным 🙂

Пожалуйста делитесь своими мыслями в комментариях:-) это поможет сделать эту статью видимой для других и будет здоровской поддержкой и мотивацией:-)

Спасибо за ваше время и хорошего дня!

Антон


ссылка на оригинал статьи https://habr.com/ru/articles/896632/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *