Как стать автором
Обновить

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

Уровень сложностиПростой
Время на прочтение2 мин
Количество просмотров1.5K
Автор оригинала: Anton Malofeev

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

До того, как начать, если есть желание перейти непосредственно к коду - недавно опубликовал библиотеку.

Приходя из 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).

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

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

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

Антон

Теги:
Хабы:
Всего голосов 2: ↑2 и ↓0+4
Комментарии2

Публикации

Работа

Ближайшие события