Больше техническая заметка, чем статья, поэтому постараюсь изложить мысли как можно кратче.
Приходя из 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/
Добавить комментарий