Doldrums / flutter_interview

❓Список вопросов и ответов к техническому интервью на позицию flutter-разработчика

Home Page:https://github.com/p0dyakov/flutter_interview

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

image
Подготовка к интервью на позицию flutter - разработчика. В данном документе есть неточности и упущения, поэтому пул реквесты приветствуются
Другое: flutter_roadmap, flutter_acrticles

Технические вопросы

Общие

Почему для flutter был выбран именно dart?

  • Обе технологии были разработаны одной компанией. Это позволяет быстрее вносить изменения, если того требует другая технология.
  • Dart соответствует современным "стандартам": обладает ооп, имеет синтаксический сахар, достаточно прост для начинающих

Какие существуют структуры данных? Зачем они нужны?

Структуры данных нужны для хранения данных в подходящем виде

  • Массивы
  • Стеки (LIFO - последний вошёл, первый вышел)
  • Очереди (FIFO - первый вошёл, первый вышел)
  • Связные списки
  • Деревья
  • Графы
  • Хеш-таблицы

Подробнее


Что такое ООП? Какие есть принципы ООП?

Объеектно-ориентиированное программиирование — это парадигма программирования, основанная на представлении программы в виде совокупности объектов, каждый из которых является экземпляром определенного класса, а классы образуют иерархию наследования.

  • Абстракция Моделирование взаимодействий сущностей в виде абстрактных классов и интерфейсов для представления системы
  • Инкапсуляция Объединение данных и методов, работающих с ними, в классе
  • Наследование Создания новых классов на основе существующих
  • Полиморфизм Использование объектов с одинаковым интерфейсом без информации о типе и внутренней структуре объекта

Подробнее


Что такое компилятор?

Компилятор - это программа, которая переводит код на одном языке программирования на другой язык


Что такое анонимные функции?

Анонимные функции - это обычные функции, не имеющие названия. Зачастую они передаются в качестве параметров


Принципы SOLID

  • Single Responsibility Principle (Принцип единственной ответственности) Класс должен отвечать только за что-то одно.
  • Open-Closed Principle (Принцип открытости-закрытости) Программные сущности должны быть открыты для расширения, но закрыты для модификации.
  • Liskov Substitution Principle (Принцип подстановки Барбары Лисков) Наследующий класс должен дополнять, а не замещать поведение базового класса.
  • Interface Segregation Principle (Принцип разделения интерфейса) Клиенты не должны имплементировать логику, которую они не используют.
  • Dependency Inversion Principle (Принцип инверсии зависимостей) Модули верхних уровней не должны зависеть от модулей нижних уровней. Классы и верхних, и нижних уровней должны зависеть от одних и тех же абстракций (при чём абстракции не должны знать о деталях).

Императивное программирование vs декларативное

  • Императивный стиль Описываем, как добиться желаемого результата. Пример:
    — поставь сковородку на огонь;
    — возьми два яйца (куриных);
    — нанеси удар ножом по каждому;
    — вылей содержимое на сковородку;
    — выкинь скорлупу;

  • Декларативный стиль Описываете, какой именно результат вам нужен.
    Пример:
    — приготовь яичницу


Что такое стек?

Стек — это область оперативной памяти, которая создаётся для каждого потока. Он работает в порядке LIFO, то есть последний добавленный в стек кусок памяти будет первым в очереди на вывод из стека. Каждый раз, когда функция объявляет новую переменную, она добавляется в стек, а когда эта переменная пропадает из области видимости (например, когда функция заканчивается), она автоматически удаляется из стека. Когда стековая переменная освобождается, эта область памяти становится доступной для других стековых переменных.


Что такое куча?

Куча — это хранилище памяти, расположенное в ОЗУ, которое допускает динамическое выделение памяти. Представляет из себя склад для глобальных переменных. По завершению приложения все выделенные участки памяти освобождаются. Размер кучи задаётся при запуске приложения, но, в отличие от стека, он ограничен лишь физически, и это позволяет создавать динамические переменные.


Что такое DAO?

DAO (Data Access Object, объект доступа к данным) — абстрактный интерфейс к какому-либо типу базы данных или иному механизму хранения


Что такое DTO?

DTO (Data Transfer Object, объект переноса данных) - это объект для передачи данных (объектов без поведения) между слоями


Что такое VO?

VO (Value Object, объект-значение) ⎼ это объект без специальных методов, имеющий набор свойств (полей) примитивных типов данных или тоже Value object


Что такое BO?

BO (Business Object, объект бизнеса) - это объект, который представляют некую сущность из определенного «домена», то есть отрасли, для которой разработано приложение


DI vs Service Locator

  • DI - передача зависимостей класса через параметры конструктора
  • Service Locator - синглтон / класс с набором статических методов

Доступ к Service Locator может производиться из любого место в коде. В этом заключается его основной минус

Основы Dart

Что такое dart?

Dart - это объектно-ориентированный строготипизированный язык программирования, созданный Google. Изначально позиционировался в качестве замены/альтернативы JavaScript. Вышел в открытый доступ в 2011 году


Различия между final и const

  • final вычисляется в runtime-е. Константно только значение экземпляра. При использовании экземпляра final в памяти выделяется новая область памяти, даже если значение объекта будет идентично.
  • const вычисляется во время компиляции. Константно не только значение, но и сам экземпляр. При использовании const переменной новая область памяти не выделяется, а используется ссылка на уже существующий экземпляр

Какие примитивные типы данных есть в Dart?

  • bool
  • int
  • double
  • String
  • Runes
  • Symbol

Какие типы конструкторов сущестуют в Dart?

  • Неменованный Class(this.parametr1, this.parametr2)
  • Именованный Class({this.parametr1, this.parametr2})

Какие структуры данных есть в Dart?

  • Массивы
  • Хеш-таблицы
  • Set

Все остальные можно реализовать вручную
Пример реализации


Что такое Late в dart?

Late - это ключевое слово в dart, которое позволяет объявить non-nullable переменную и при этом не установить для нее значение. Значение инициализируется только тогда, когда мы к нему обращаемся


Что такое Null-safety?

Sound Null Safety – это дополнение к языку Dart, которое еще больше усиливает систему типов, отделяя типы, допускающие значение Null, от типов, не допускающих значения Null. Это позволяет разработчикам предотвращать ошибки, связанные с Null.


Можно ли получить null poiner exception?

Null poiner exception можно получить, если есть значение null и к нему обращаются с помощью bang-оператора !


HashCode

Хэш-код - геттер, у любого объекта, который возвращает int. Нужен при сохранении объекта в map или set. Хэш-коды должны быть одинаковыми для объектов, которые равны друг другу в соответствии с оператором ==
int get hashCode => Object.hash(runtimeType, ..., ...);


Какой есть модификатор доступа?

Для добавления приватности используется _ перед именем класса / поля / переменной / метода / функции


Что такое extension?

Extension — это синтаксический сахар, который позволяет расширить существующий класс (добавить методы, операторы, сеттеры и геттеры)


Что такое миксины?

Миксин - обычный класс за тем исключением, что он не имеет конструктора. Для использования миксинов применяется оператор with
Миксины нужны для множественного наследования.

Если у миксинов будет метод с одинаковым названием, то останется реализация, которая указана в последнем миксине. Так как миксины будут переопределять этот метод


Какие есть виды компиляции?

  • Just-in-time (JIT) компиляция - это вид компиляции, который выполняется непосредственно во время работы программы, что существенно ускоряет цикл разработки. Но стоит учитывать, что программа может притормаживать и выполняться медленнее
  • Ahead-of-time (AOT) компиляция - это вид компиляции, который полностью выполняется перед запуском программы. Занимает больше времени, чем JIT, но в результате программа работает куда быстрее.

В чём разница между hot restart и hot reload?

  • Hot Reload загружает изменения в Dart VM и ребилдит дерево виджетов, сохраняя состояние. Не перезапускает main() и initState()
  • Hot Restart загружает изменения в Dart VM и перезагружает всё приложение. Состояние не сохраняется

Какие есть правила именования?

  • Переменные и константы - lowerCamelCase
  • Классы, миксины, enum-ы - UpperCamelCase
  • Файлы - snake_case

Что такое дженерики?

Generics - это параметризованные типы. Они позволяют программе уйти от жесткой привязки к определенным типам, определить функционал так, чтобы он мог использовать данные любых типов и обеспечить их безопасность. Так же обобщения снижают повторяемость кода, дают вам возможность предоставить единый интерфейс и реализацию для многих типов.


Что такое тип Never?

Never - это тип, означающий, что ни один тип не разрешен и Never сам по себе не может быть создан.
Подробнее


Что такое covariant?

Covariant - это ключевое слово в dart, позволяющее изменить параметры имплементируемого метода


Что такое deferred?

Deferred - это ключевое слово в dart, позволяющее загрузить библиотеку только тогда, когда она будет использована


Что такое аннотации?

Аннотации — это синтаксические метаданные, которые могут быть добавлены к коду. Другими словами, это возможность добавить дополнительную информацию к любому компоненту кода, например, к классу или методу. Аннотации всегда начинаются с символа @ (@override, @required). Любой класс может служить аннотацией, если в нем определен const конструктор


Что такое @immutable перед классом?

@immutable - это аннотация, которая говорит о том, что каждая переменная в классе должна быть final


Что такое Dart VM?

Dart VM (Dart virtual machine) - среда выполнения Dart

Компоненты:

  • Среда исполнения
  • Сборщик мусора
  • Основные библиотеки и нативные методы
  • Система отладка
  • Профилировщик
  • Симулятор ARM архитектуры

Основы Flutter

Что такое flutter?

Flutter - это фреймворк с открытым исходным кодом для создания кроссплатформенных приложений с использованием языка программирования Dart, разработанный и развиваемый Google


Что такое Widget?

Widget - это иммутабельное описание части пользовательского интерфейса. Виджет связан с элементом, который управляет рендерингом. Виджеты образуют сруктуру, а не дерево


Что такое Element?

Element - это мутабельное представление виджета в определенном месте дерева. Управляют жизненым циклом, связывают виджеты и объекты рендеринга.


Что такое RenderObject?

RenderObject - это мутабельный объект дерева визуализации. У него есть родительский объект, а также поле с данными, которое родительский объект использует для хранения специфичной информации, касающейся самого этого объекта, например, его позицию. Данный объект отвечает за отрисовку, учёт размеров и ограничений, прослушивание и обработку нажатий. При необходимости перерисовки помечается как dirty. Перерисовывается, используя свой метод layer


Виды элементов

image ComponentElement - компоновочный элемент, который явно не содержит логику рисования/отображения. Есть метод build(), который возвращает виджет. Образуется только при создании виджетов StatelessWidget, StatefulWidget, InheritedWidget (ProxyWidget).

  • ProxyElement
  • StatelessElement
  • StatefulElement

RenderObjectElement - отображающий элемент, явно участвующий в рисовании компонентов на экране. Содержит renderObject и наследуется от класса Element. Образуется при создании виджетов Padding, Column, Row, Center и др.

  • LeafRenderObjectElement
  • ListWheelElement
  • MultiChildRenderObjectElement
  • RootRenderObjectElement
  • SingleChildRenderObjectElement
  • SliverMultiBoxAdaptorElement
  • SlottedRenderObjectElement

Виды виджетов

Proxy - это виджеты, которые хранят некоторую информацию и делают её доступной для потомков. Эти виджеты не принимают непосредственного участия в формировании пользовательского интерфейса, но используются для получения информации, которую они могут предоставить.

  • InheritedWidget
  • ParentDataWidget (LayoutId, Flexible, KeepAlive и т.д.)
  • NotificationListener

Renderer - это виджеты, которые имеют непосредственное отношение к компоновке экрана, поскольку они определяют размеры, положение, отрисовку

  • Row
  • Column
  • Stack
  • Padding
  • Align
  • Opacity

Component - это виджеты, которые предоставляют непосредственно не окончательную информацию, связанную с размерами, позициями, внешним видом, а скорее данные (или подсказки), которые будут использоваться для получения той самой финальной информации

  • RaisedButton
  • Scaffold
  • Text
  • GestureDetector
  • Container

В чем разница между Stateless и Stateful виджетами?

  • StatelessWidget - это виджет, который не имеет состояния, в процессе работы приложения не изменяет своих свойств. Такие виджеты не имеют состояния. Они могут изменяться лишь посредством внешних событий, которые возникают на родительских виджетах
  • StatefulWidget - это виджет, который хранит состояние, в процессе работы приложения он может его изменять динамически.

Какой жизенный цикл у Stateful виджета?

  1. createState() вызывается единожды и создает изменяемое состояние для этого виджета в заданном месте в дереве
  2. mounted is true
  3. initState() вызывается единожды при инициализации
  4. didChangeDependencies() вызывается единожды после инициализации и далее при уведомлениях от Inhherited-виджетов вверху по дереву, от которых зависит виджет
  5. build() вызывается каждый раз при перерисовке
  6. didUpdateWidget(Widget oldWidget) вызывается каждый раз при обновлении конфигурации виджета
  7. setState() вызывается императивно для перерисовки
  8. deactivate() вызывается, когда ранее активный элемент перемещается в список неактивных элементов, при этом удаляясь из дерева
  9. dispose() вызывается, когда этот объект удаляется из дерева навсегда
  10. mounted is false

Какой жизенный цикл у Element-а?

  1. Элемент создаётся посредством вызова метода Widget.createElement и конфигурируется экземпляром виджета, у которого был вызван метод.
  2. С помощью метода mount созданный элемент добавляется в заданную позицию родительского элемента. При вызове данного метода также ассоциируются дочерние виджеты и элементам сопоставляются объекты дерева рендеринга.
  3. Виджет становится активным и должен появиться на экране.
  4. В случае изменения виджета, связанного с элементом (например, если родительский элемент изменился), есть несколько вариантов развития событий. Если новый виджет имеет такой же runtimeType и key, то элемент связывается с ним. В противном случае, текущий элемент удаляется из дерева, а для нового виджета создаётся и ассоциируется с ним новый элемент.
  5. В случае, если родительский элемент решит удалить дочерний элемент, или промежуточный между ними, это приведет к удалению объекта рендеринга и переместит данный элемент в список неактивных, что приведет к деактивации элемента (вызов метода deactivate).
  6. Когда элемент считается неактивным, он не находится на экране. Элемент может находиться в неактивном состоянии только до конца текущего фрейма, если за это время он остается неактивным, он демонтируется (unmount), после этого считается несуществующим и больше не будет включен в дерево.
  7. При повторном включении в дерево элементов, например, если элемент или его предки имеют глобальный ключ, он будет удален из списка неактивных элементов, будет вызван метод activate, и рендер объект, сопоставленный данному элементу, снова будет встроен в дерево рендеринга. Это означает, что элемент должен снова появиться на экране.

Инструменты для дебаггинга приложений

  • DevTools (debugger, widget inspector, memory profiler, timeline view, logging view)
  • Debug console В неё могут логироваться различные данные
  • Breakpoints
  • Dart analyzer

С какими ОС работает Flutter?

  • Android
  • IOS
  • Windows
  • Linux
  • Web

Какие жесты распознает Flutter?

  • Нажатия
  • Двойные нажатия
  • Долгия нажатия
  • Свайпы
  • Тапы
  • Уменьшение / увеличивание двумя пальцами

Какие есть режимы сборки во Flutter?

  • Debug (JIT) для разработки
  • Release (AOT) для публикации приложения
  • Profile (AOT) для анализа производительности

Что такое BuildContext

BuildContext - это интерфейс, который имплементирует Element.

BuildContext может быть полезен, когда надо:

  • Получить ссылку на объект RenderObject, соответствующий виджету (или, если виджет не является Renderer, то виджету-потомку)
  • Получить размер RenderObject
  • Обратиться к дереву и получить ближайший родительский InheritedWidget. Это используется фактически всеми виджетами, которые обычно реализуют метод of (например, MediaQuery.of(context), Theme.of(context) и т.д.)

в чём разница между Package и Plugin?

  • Package написан только на dart
  • Plugin использует dart и специфичный код для платформы

В чём отличие между shared и transitive зависимостями?

  • Shared Dependencies подтягиваются напрямую и в других установленных пакетах
  • Transitive Dependencies подтягиваются со сторонними пакетами

Что такое FFI Plugin?

  • FFI Plugin - плагин, в котором для написания специфичных платформенных частей используется Dart FFI. Позволяет запустить код на C / C++

Какие есть проблемы во Flutter?


Что такое InheritedWidget?

InheritedWidget — это виджет, который предоставляет своим потомкам взаимодействовать с данными, хранящимися в нём. Решает проблему с передачей данных через конструкторы. Может уведомлять виджетов внизу по дереву об изменениях в собственных данных, тем самым провоцируя их перерисовку.
Для получения Inherited виджета необходимо вызвать context.dependOnInheritedWidgetOfExactType() в didChangeDependencies()


Как какая сложность у операции получения InheritedWidget?

O(1). Такая скорость достигается за счёт того, что Inherited виджеты хранятся в виде мапы в Element


Анимации

Для работы с анимациями обычно используется AnimationController

Процесс анимации:

  • Ticker просит SchedulerBinding зарегистрировать обратный вызов и сообщить Flutter Engine, что надо разбудить его, когда появится новый обратный вызов.
  • Когда Flutter Engine готов, он вызывает SchedulerBinding через запрос onBeginFrame.
  • SchedulerBinding обращается к списку обратных вызовов ticker и выполняет каждый из них.
  • Каждый tick перехватывается "заинтересованным" контроллером для его обработки.
  • Если анимация завершена, то ticker "отключён", иначе ticker запрашивает SchedulerBinding для планирования нового обратного вызова.
  • ...

Какие виды анимаций существуют?

  • Tween animation. Начало, конец, время, скорость заранее определенно
  • Physics-based animation. Имитируют реальное поведение

Что такое Tween?

Tween - это объект, который описывает между какими значениями анимируется виджет и отвечает за вычисление текущего значения анимации


Tween анимации

  • Implicit Animations - это набор Implicitly Animated Widgets, которые анимируются самостоятельно при их перестройке с новыми аргументами. (AnimatedAlign, AnimatedContainer, AnimatedPadding и т.д.)
  • Explicit Animations - это набор элементов управления анимационными эффектами. Предоставляют куда больше контроля над анимацией, чем Implicit Animations. Для использования необходимо подмешать к стейту вашего виджета SingleTickerProviderStateMixin / TickerProviderStateMixin, создать AnimationController и зависящие от него Animation, передать анимацию в Transition Widget (AlignTransition, DecoratedBoxTransition, SizeTransition и т.д.) SingleTickerProviderStateMixin / TickerProviderStateMixin создает Ticker
    Ticker вызывает callback на каждый фрейм анимации
    AnimationController пределяет все фреймы анимации - управляет анимацией (forward, reverse, repeat, stop, reset и т.д.)
    Animation отдает текущее значение анимации, а также позволяет подписаться на обновления значения/статуса анимации

Что такое CustomPaint?

CustomPaint - это класс, создает «холст» для рисования. В методе paint в качестве аргументов поступает canvas, который позволяет рисовать различные фигуры


Что такое GlobalKeys?

GlobalKeys - это ключи, которые предоставляют доступ к виджетам. Для виджетов с отслеживанием состояния глобальные ключи также предоставляют доступ к состоянию. Позволяют виджетам менять родителей в любом месте приложения без потери состояния. Должны быть уникальны для всего приложения.


Что такое LocalKeys?

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

  • ValueKey - это ключ, который использует значение определенного типа для идентификации самого себя. Переопределяет оператор сравнения. Если value одниковое, то ключи одинаковые
  • UniqueKey - это ключ, который равен только самому себе
  • ObjectKey - это ключ, который используется для привязки идентификатора виджета к идентификатору объекта, используемого для создания этого виджета

Зачем нужны билдеры?

  • Получение контекста конкретного виджета, а не родителя
  • Оборачивание создания виджета в коллбек
  • Обновление виджета в зависимости от асинхронного получения результата какой-либо операции

Скролящиеся списки

  • SingleChildScrollView
  • ListView - скролящийся статический список из виджетов. Все виджеты рисуются сразу и не перерисовываются при скролле. Подходит для небольшого количества элементов
  • ListView.builder / ListView.separated - скролящийся динамический список из виджетов. Рисуются только виджеты, находящиеся в области видимости вьюпорта. Подходит для большого количества элементов
  • GridView - скролящаяся статическия сетка из виджетов. Все виджеты рисуются сразу и не перерисовываются при скролле. Подходит для небольшого количества элементов
  • GridView.builder - скролящаяся динамическая сетка из виджетов. Все виджеты рисуются сразу и не перерисовываются при скролле. Рисуются только виджеты, находящиеся в области видимости вьюпорта. Подходит для большого количества элементов
  • CustomScrollView - кустомный список, состоящий из сливеров (скроллируемых областей), наследников RenderSliver (SliverAppBar, SliverList, SliverGrid, SliverToBoxAdapter и др.). Нужен для сложного поведения. Все вышеперечисленные виджеты под капотом работают со сливерами

Sliver Protocol

  • SliverConstraints - это класс, описывающий ограничения и текущее состояние скролла для RenderSliver по отношению к viewport
  • SliverGeometry - это класс, описывающий какие размеры занимает RenderSliver

Асинхронность

Какие есть операции?

  • Синхронные блокируют другие операции до своего выполнения.
  • Асинхронные позволяют другим операциям выполняться, пока текущая не закончится

Что такое Future?

Future - это обёртка над результатом выполнения асинхронной операции. Код Future НЕ выполняется параллельно, а выполняется в последовательности, определяемой Event Loop.
Состояния Future:

  • Uncompleted - операция не завершена
  • Completed with Result - операция завершена успешно
  • Completed with Error - операция завершена с ошибкой

Какие есть конструкторы у Future?

  • Future(FutureOr<T> computation()): создает объект future, который с помощью метода Timer.run запускает функцию computation асинхронно и возвращает ее результат.
  • FutureOr<T>: указывает, что функция computation должна возвращать либо объект Future либо объект типа T. Например, чтобы получить объект Future, функция computation должна возвращать либо объект Future, либо объект int
  • Future.delayed(Duration duration, [FutureOr<T> computation()]): создает объект Future, который запускается после временной задержки, указанной через первый параметр Duration. Второй необязательный параметр указывает на функцию, которая запускается после этой задержки.
  • Future.error(Object error, [StackTrace stackTrace]): создает объект Future, который содержит информацию о возникшей ошибке.
  • Future.microtask(FutureOr<T> computation()): создает объект Future, который с помощью функции scheduleMicrotask запускает функцию computation асинхронно и возвращает ее результат.
  • Future.sync(FutureOr<T> computation()): создает объект Future, который содержит результат немедленно вызываемой функции computation.
  • Future.value([FutureOr<T> value]): создает объект Future, который содержит значение value.

Что такое Event Loop?

Event Loop - вечный цикл, выполняющий все поступающие в изолят задачи. В нём есть две FIFO очереди задач:

Очередь MicroTask
Используется для очень коротких действий, которые должны быть выполнены асинхронно, сразу после завершения какой-либо инструкции перед тем, как передать управление обратно Event Loop. Очередь MicroTask имеет приоритет перед очередью Event

Очередь Event
Используется для планирования операций, которые получают результат от внешних событий (операции ввода/вывода, жесты, рисование, таймеры, потоки) и Futures


Порядок выполнения Futures

  1. Future.sync(FutureOr<T> computation()) / синхронный код
  2. Future.microtask(FutureOr<T> computation())
  3. Остальное
    Duration.zero выполняется сразу

Что происходит, когда вы создаёте экземпляр Future?

  1. Экземпляр создаётся и хранится во внутреннем массиве, управляемом Dart
  2. Код, который должен быть исполнен данным экземпляром Future, добавляется напрямую в очередь Event
  3. Код функции, выполняется синхронно до тех пор, пока не встретится ключевое слово await
  4. Ожидается выполнение операции с await
  5. Код продолжает выполняться

Что такое Completer?

Completer позволяет поставлять Future, отправлять событие о выполнении или событие об ошибке. Это может быть полезно, когда нужно сделать цепочку Future и вернуть результат.


Что такое стримы?

Stream - это последовательность асинхронных событий. Stream сообщает вам, что есть событие и когда оно будет готово


Какие есть виды стримов?

  • Single subscription - это вид потока, при котором может быть только один подписчик.
  • Broadcast - это вид потока, при котором может быть много подписчиков. При этом Broadcast стримы отдают свои данные вне зависимости от того, подписан ли кто-нибудь на них или нет. Подписчики стрима получают события только с момента подписки, а не с момента старта жизни стрима

Что такое Генераторы (sync* / async*)?

Генератор это ключевое слово, которое позволяет создавать последовательность значений с помощью yield

  • sync* - это синхронный генератор. Возвращает Iterable
  • async* - это aсинхронный генератор. Возвращает Stream

Как работает await?

Под капотом await перемещает весь последующий код в then у Future, которую мы дожидаемся

Многопоточность

Многопоточность в Dart и Flutter

Dart — однопоточный язык программирования. Он исполняет одновременно одну инструкцию. Но при этом мы можем запустить код в отдельном поток с помощью Isolate


Что такое Isolate?

Isolate - это класс в dart, позволяющий выполнить код в другом потоке.

  • Имеют собственную область памяти
  • Имеют собственный сборщик мусора
  • Взаимодействие между разными изолятами реализовано посредством сообщений. Данные каждый раз копируются, поскольку у каждого изолята своя область памяти
  • Количество изолятов не должно привышать количество потоков процесора - 1
  • Платформенные взаимодействия возможны только в главном изоляте. Этот тот изолят, который создается при запуске вашего приложения
  • Создание изолята - достаточно дорогая операция

Как работет Compute?

Compute - это функция, которая создаёт изолят и запускает переданный код.


Проблемы многопоточности

  • Deadlock — каждый из потоков ожидают событий, которые могут предоставить другие потоки
  • Race conditions — проявление недетерминизма исполнителя программы при различном относительном порядке исполнения команд в различных потоках
  • Lock Contention — основное время потока проводится не в исполнении полезной работы, а в ожидании блокированного другим потоком ресурса
  • Live Lock — поток захватывает ресурс, но после того, как убедится, что завершить работу не может, освобождает ресурс, аннулируя результаты

Архитектура

Что такое архитектура?

Архитектура - это набор решений по организации программы. Таких, как деление программы на слои, построение связей между ними, управление состоянием, связь с UI. Хорошая архитектура делает слои в приложении слабо связанными, что упрощает внесение изменений, повышает тестируемость кода, упрощает систему


Чистая архитектура

По чистой архитектуре система делится на три слоя:

  • Data (datasources, models, repositories) получение данных извне
  • Domain (entities, repositories interfaces, usecases) бизнес правила
  • Presentation (bloc, pages, widgets) отображение

Пример


Управление состоянием

Vanilla

Плюсы

  • Низкий порог вхождения.
  • Не требуются сторонние библиотеки.

Минусы

  • При изменении состояния виджета дерево виджетов каждый раз целиком пересоздается.
  • Нарушает принцип единственной ответственности. Виджет отвечает не только за создание UI, но и за загрузку данных, бизнес-логику и управление состоянием.
  • Решения о том как именно отображать текущее состояние принимаются прямо в UI коде. Если состояние станет более сложным, то читаемость кода сильно понизится.

Использование:

  • Widget State

BLoC

Плюсы

  • Четкое разделение ответственности
  • Предсказуемые преобразования Event to State
  • Реактивность. Нет необходимости в вызове дополнительных методов

Минусы

  • Обязательность состояния в Bloc-е
  • Частые изменения библиотеки
  • Зависимость от сторонней библиотеки

Использование:

  • Widget State
  • App State

Redux

Плюсы

  • Единое состояние
  • Все экшены доступны всем

Минусы

  • Единое состояние
  • Зависимость от сторонней библиотеки
  • Большое количество boilerplate кода
  • Все экшены доступны всем

Использование:

  • Widget State
  • App State

Provider

Плюсы

  • Скоупы на поддеревья
  • Flutter ориентирован
  • Нет статики
  • Готовые провайдеры

Минусы

  • Завязка на фреймворк
  • Только 1 провайдер одного типа
  • Зависимость от сторонней библиотеки

Использование:

  • App State
  • Частично DI

Riverpod

Плюсы:

  • Скоупы на поддеревья
  • Flutter ориентирован
  • Нет статики
  • Готовые провайдеры

Минусы:

  • Зависимость от сторонней библиотеки
  • Циклические зависимости падают в runtime-е

Использование:

  • Widget State
  • App State
  • DI

Dependency Injection

Dependency injection (DI) - это механизм, который позволяет сделать взаимодействующие в приложении объекты слабосвязанными с помощью интерфейсов. Это делает всю систему более гибкой, более адаптируемой и расширяемой

GetIt + Injectable

Плюсы:

  • Нет завязки на UI
  • Compile safe

Минусы:

  • Статика
  • Нельзя слушать изменения
  • Не Flutter ориентирован

Фреймворки

Fish Redux

Плюсы

  • Coming soon

Минусы

  • Большое количество boilerplate кода
  • Coming soon

Использование:

  • Coming soon

GetX

Плюсы

  • Coming soon

Минусы

  • Coming soon

Использование:

  • Coming soon

Архитектурные патерны

MVVM

Части:

  • Model содержит в себе всю логику приложения, она хранит и обрабатывает данные, при этом не взаимодействуя с пользователем напрямую
  • View отображает данные, которые ему передали
  • ViewModel связывает модель и представление (передаёт данные между ними)

Признаки View-модели:

  • Двухсторонняя коммуникация с представлением;
  • View-модель — это абстракция представления. Обычно означает, что свойства представления совпадают со свойствами View-модели / модели
  • View-модель не имеет ссылки на интерфейс представления (IView). Изменение состояния View-модели автоматически изменяет представление и наоборот, поскольку используется механизм связывания данных (Bindings)
  • Один экземпляр View-модели связан с одним отображением._

Использование:

  • Используется в ситуации, когда возможно связывание данных без необходимости ввода специальных интерфейсов представления (т.е. отсутствует необходимость реализовывать IView);
  • Частым примером является технология WPF.

Подробнее Пример

MVC

Части:

  • Model содержит в себе всю логику приложения, она хранит и обрабатывает данные, при этом не взаимодействуя с пользователем напрямую
  • View отображает данные, которые ему передали
  • Controller перехватывает событие извне и в соответствии с заложенной в него логикой, реагирует на это событие изменяя Mодель, посредством вызова соответствующего метода. После изменения Модель использует событие о том что она изменилась, и все подписанные на это события Представления, получив его, обращаются к Модели за обновленными данными, после чего их и отображают

Признаки контроллера:

  • Контроллер определяет, какие представление должно быть отображено в данный момент;
  • События представления могут повлиять только на контроллер.контроллер может повлиять на модель и определить другое представление.
  • Возможно несколько представлений только для одного контроллера;

Использование:

  • Используется в ситуации, когда невозможно связывание данных (нельзя использовать Binding);

Пример

MVP

Части:

  • Model содержит в себе всю логику приложения, она хранит и обрабатывает данные, при этом не взаимодействуя с пользователем напрямую
  • View отображает данные, которые ему передали
  • Presenter подписывается на события представления, по запросу изменяет модель, обновляет представление

Признаки презентера:

  • Двухсторонняя коммуникация с представлением;
  • Представление взаимодействует напрямую с презентером, путем вызова соответствующих функций или событий экземпляра презентера;
  • Презентер взаимодействует с View путем использования специального интерфейса, реализованного представлением;
  • Один экземпляр презентера связан с одним отображением.

Использование:

  • Используется в ситуации, когда связь между представление и другими частями приложения невозможна (и Вы не можете использовать MVVM или MVP);

Способы осуществления навигации. Плюсы / минусы каждого варианта

Navigator

  • Идёт из коробки

Go Router

  • Парсинг пути и параметров запроса (например, "user/:id")
  • Поддержка deep-links
  • Поддержка перенаправления - вы можете перенаправить пользователя на другой URL-адрес в зависимости от состояния приложения

Auto Route

  • Парсинг пути и параметров запроса (например, "user/:id")
  • Поддержка deep-links
  • Защищённые маршруты
  • Именованные роуты
  • Разные варианты анимаций

Базы данных

Нереляционные (NoSQL):
Hive

Плюсы:

  • Реализация на чистом Dart, без платформенного кода, за счёт чего одинаково работает на разных платформах
  • Прост в использовании
  • Быстрая запись / чтение
  • Мало boilerplate кода
  • Наличие кодогенерации
  • Поддерживается на всех платформах

Минусы:

  • Связи между объектами нужно поддерживать вручную
  • Ограничение по количеству адаптеров для объектов - 224
  • Ограничение по количеству полей в адаптере - 255
  • Плохо подходит для работы с большим объемом данных
  • Выгружает всю бд в память
  • Нельзя получить доступ к box-у, созданному в другом изоляте
  • Наличие кодогенерации
  • Нет миграций

Использование:

  • Небольшой объём данных
  • Необходимость в сохранении своих дата моделей

Shared Preferences

Плюсы:

  • Прост в использовании
  • Синхронное чтение из кэша в памяти
  • Поддерживается на всех платформах

Минусы:

  • Разные реализации для Android/iOS и других платформ
  • Обращение к платформенному коду
  • Не гарантируется запись на диск после успешного выполнения метода
  • Нельзя сохранять сложные объекты из коробки

Использование:

  • Небольшой объём данных
  • Необходимость в быстром внедрении решения
  • Сохранение настроек приложения в виде примитивных данных

Sembast

Плюсы:

  • Coming soon

Минусы:

  • Coming soon

Использование:

  • Coming soon

ObjectDB

Плюсы:

  • Coming soon

Минусы:

  • Coming soon

Использование:

  • Coming soon

Реляционные (SQL):
SQFLite

Плюсы

  • Есть миграции
  • Поддерживает связи между сущностями
  • Не выгружает бд в оперативную память
  • Возможность написания сложных запросов на SQL

Минусы:

  • Сложен в использовании
  • Не поддерживается в Web, Linux, Windows
  • Скорость работы ниже чем у NoSQL
  • На разных OS и версиях могут быть разные версии SQLite

Использование:

  • Большой объём данных
  • Хранение сложно структурированных данных

Drift

  • Есть миграции
  • Поддерживает связи между сущностями
  • Не выгружает бд в оперативную память
  • Возможность написания сложных запросов на SQL

Плюсы

  • Есть миграции
  • Быстрый
  • Поддерживается на всех платформах

Минусы:

  • Сложен в использовании
  • Скорость работы ниже чем у NoSQL

Использование:

  • Большой объём данных
  • Хранение сложно структурированных данных

Floor

Плюсы:

  • Coming soon

Минусы:

  • Coming soon

Использование:

  • Coming soon

Bindings

Что такое WidgetsFlutterBinding?

WidgetsFlutterBinding — конкретная реализация привязки приложений на основе инфраструктуры виджетов. По сути своей — это клей, соединяющий фреймворк и движок Flutter. WidgetsFlutterBinding состоит из множества связей: GestureBinding, ServicesBinding, SchedulerBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding.

Метод scheduleAttachRootWidget является отложенной реализацией attachRootWidget. Принадлежит данный метод WidgetsBinding. В описании к нему сказано, что он присоединяет переданный виджет к renderViewElement — корневому элементу дерева элементов.

Метод scheduleWarmUpFrame принадлежит SchedulerBinding и используется для того, чтобы запланировать запуск кадра как можно скорее, не ожидая системного сигнала Vsync.


Какие есть Bindings?

image

Bindings - это классы для обмена данными между Flutter Framework и Flutter Engine. Каждая привязка отвечает за обработку набора конкретных задач, действий, событий, сгруппированных по области деятельности.

BaseBinding — базовый абстрактный класс, давайте тогда рассмотрим конкретные реализации биндингов. Среди них мы увидим:

ServicesBinding отвечает за перенаправление сообщений от текущей платформы в обработчик данных сообщений (BinaryMessenger);

PaintingBinding отвечает за связь с библиотекой отрисовки.

RenderBinding отвечает за связь между деревом рендеринга и движком Flutter.

WidgetBinding отвечает за связь между деревом виджетов и движком Flutter.

SchedulerBinding — планировщик очередных задач, таких как:

  • вызовы приходящих колбеков, которые инициирует система в Window.onBeginFrame — например события тикеров и контроллеров анимаций;
  • вызовы непрерывных колбеков, которые инициирует система Window.onDrawFrame, например, события для обновления системы отображения после того, как отработают приходящие колбеки;
  • посткадровые колбеки, которые вызываются после непрерывных колбеков, перед возвратом из Window.onDrawFrame;
  • задачи не связанные с рендерингом, которые должны быть выполнены между кадрами.

SemanticsBinding отвечает за связь слоя семантики и движком Flutter.

GestureBinding отвечает за работу с подсистемой жестов.

Каналы платформы

image (!) Платформенные взаимодействия возможны только в главном изоляте. Этот тот изолят, который создается при запуске вашего приложения.

Канал платформы — это двусторонний канал связи между кодом на dart и нативом, который объединяет имя канала и кодек для кодирования сообщений в двоичную форму и обратно. Вызовы асинхронны. Каждый канал должен иметь уникальный идентификатор.

Каналы сообщений - это каналы платформы, предназначенные для обмена сообщениями между нативным кодом и flutter-приложением.
Кодеки сообщений:

  • BinaryCodec Реализуя сопоставление идентификаторов в байтовых буферах, этот кодек позволяет вам наслаждаться удобством объектов канала в тех случаях, когда вам не требуется кодирование/декодирование. Каналы сообщений Dart с этим кодеком имеют тип BasicMessageChannel.
  • JSONMessageCodec Работает с «JSON-подобными» значениями (строки, числа, логические значения, null, списки этих значений и мапы строка-ключ с этими данными). Списки и мапы неоднородны и могут быть вложены друг в друга. Во время кодирования значения преобразуются в строки JSON, а затем в байты с использованием UTF-8. Каналы сообщений Dart имеют тип BasicMessageChannel с этим кодеком.
  • StandardMessageCodec Работает с несколько более обобщенными значениями, чем кодек JSON, поддерживая также однородные буферы данных (UInt8List, Int32List, Int64List, Float64List) и мапы с нестроковыми ключами. Обработка чисел отличается от JSON тем, что целые числа Dart поступают на платформу как 32- или 64-битные целые числа со знаком, в зависимости от величины никогда как числа с плавающей запятой. Значения кодируются в специальном, достаточно компактном и расширяемом двоичном формате. Стандартный кодек предназначен для выбора по умолчанию для канала связи во Flutter. Что касается JSON, каналы сообщений Dart, созданные с использованием стандартного кодека, имеют тип BasicMessageChannel.

Каналы методов — это каналы платформы, предназначенные для вызова нативного кода из flutter-приложения.
Кодеки методов:

  • StandardMethodCodec делегирует кодирование значений полезной нагрузки (payload) в StandardMessageCodec. Поскольку последний является расширяемым, то же самое можно сказать и о первом.
  • JSONMethodCodec делегирует кодирование значений полезной нагрузки (payload) в JSONMessageCodec.

Каналы событий — это специализированные каналы платформы, предназначенные для использования в случае представления событий платформы Flutter в виде потока Dart. Работает как обычный Stream

Подробнее

Flutter под капотом

Как устроен Flutter

image

Уровень фреймворка — всё, с чем мы работаем в момент написания приложения, и все служебные классы, позволяющие взаимодействовать написанному нами с уровнем движка. Всё, относящееся к данному уровню написано на Dart. Flutter Framework взаимодействует с Flutter Engine через слой абстракции, называемый Window

Уровень движка — более низкий уровень, чем уровень фреймворка, содержит классы и библиотеки, позволяющие работать уровню фреймворка. В том числе виртуальная машина Dart, Skia и тд.

Уровень платформы — специфичные механизмы, относящиеся к конкретной платформе запуска.

Flutter Engine уведомляет Flutter Framework, когда:

  • Событие, представляющее интерес, происходит на уровне устройства (изменение ориентации, изменение настроек, проблема с памятью, состояние работы приложения…)
  • Какое-то событие происходит на уровне стекла (=жест)
  • Канал платформы отправляет некоторые данные
  • Но также и в основном, когда Flutter Engine готов к рендерингу нового кадра

Модель выполнения во Flutter

  1. Создается и запускается новый процесс — Thread (Isolate). Это единственный процесс, в котором будет выполняться ваше приложение.
  2. Инициализируются две очереди (Queues) с именами MicroTask (микрозадания) и Event (событие), тип очередей FIFO (прим.: first in first out, т.е. сообщение, пришедшие раньше, будут раньше обработаны)
  3. Исполняется функция main()
  4. Запускается Event Loop (цикл событий). Он управлет порядком исполнения вашего кода, в зависимости от содержимого двух очередей: MicroTask и Event. Представляет собой "бесконечный" цикл.
  5. Event Loop с определённой частотой проверяет MicroTask и Event. Если есть что-то код в MicroTask, то оно выполняется перед очередью Event.

Построение кадра

  1. Некоторые внешние события приводят к необходимости обновления отображения.
  2. Schedule Frame отправляется к Flutter Engine
  3. Когда Flutter Engine готов приступить к обновлению рендеринга, он создает Begin Frame запрос
  4. Этот Begin Frame запрос перехватывается Flutter Framework, который выполняет задачи, связанные в основном с Tickers (например, анимацию)
  5. Эти задачи могут повторно создать запрос для более поздней отрисовки (пример: анимация не закончила своё выполнение, и для завершения ей потребуется получить еще один Begin Frame на более позднем этапе)
  6. Далее Flutter Engine отправляет Draw Frame, который перехватывается Flutter Framework, который будет искать любые задачи, связанные с обновлением макета с точки зрения структуры и размера
  7. После того, как все эти задачи выполнены, он переходит к задачам, связанным с обновлением макета с точки зрения отрисовки
  8. Если на экране есть что-то, что нужно нарисовать, то новая сцена для визуализации отправляется в Flutter Engine, который обновит экран
  9. Затем Flutter Framework выполняет все задачи, которые будут выполняться после завершения рендеринга (PostFrame callbacks), и любые другие последующие задачи, не связанные с рендерингом

Какие деревья доступны во Flutter?

  • Widget Tree состоит из Widget, которые используются для описания пользовательского интерфейса
  • Element Tree состоит из Element, которые управляют жизненым циклом виджета и связывают виджеты и объекты рендеринга.
  • Render Tree состоит из RenderObject, которые используются для определения размеров, положения, геометрии, определения зон экрана, на которые могут повлиять жесты

Подробнее


Расчёт макета

  • Ограничения спускаются вниз по дереву, от родителей к детям.
  • Размеры идут вверх по дереву от детей к родителям.
  • Родители устанавливают положение детей.
  1. Если дочерний объект не пометил свой собственный макет требующим обновления, он может не выполнять расчёт, пока родитель дает ему те же самые ограничения.
  2. Каждый раз, когда родитель вызывает у дочернего объекта метод layout, родитель указывает, использует ли он информацию о размере, возвращаемую дочерним объектом. Часто бывает, что родительский элемент не использует эту информацию. Значит, ему не приходится повторно вычислять свой размер, даже если дочерний элемент меняет свой размер. Ему гарантируется, что новый размер будет соответствовать существующим ограничениям.
  3. Жёсткие ограничения — это те ограничения, которым может удовлетворять только один допустимый размер. Если максимальная и минимальная высота равны между собой, а максимальная и минимальная ширина также идентичны, единственный подходящий размер — заданные ограничения. В случае задания жёстких ограничений родительский элемент не должен повторно вычислять свой размер при перерасчёте дочернего элемента. Даже в случае использования родителем размеров ребёнка в своём макете, потому что дочерний элемент не может изменить размер без новых ограничений от своего родителя.
  4. RenderObject может объявить, что использует для вычисления своих размеров только ограничения, предоставленные родителем. Это значит, что родительскому объекту этого объекта рендеринга не нужно выполнять перерасчёт при повторном вычислении у самого объекта. Даже если ограничения не жёсткие или макет родительского элемента зависит от размера дочернего элемента, потому что дочерний элемент не может изменить свои размеры без новых ограничений от его родителя.

Что такое BuildOwner?

BuildOwner — менеджер сборки и обновления дерева элементов. Он активно участвует в двух фазах — сборки и завершения сборки. Поскольку BuildOwner управляет процессом сборки дерева, в нем хранятся списки неактивных элементов и списки элементов, нуждающихся в обновлении.
Методы:

  • scheduleBuildFor даёт возможность пометить элемент как нуждающийся в обновлении.
  • lockState защищает элемент от неправильного использования, утечек памяти и пометки на обновления в процессе уничтожения.
  • buildScope осуществляет пересборку дерева. Работает с элементами, которые помечены как нуждающиеся в обновлении.
  • finalizeTree завершает построение дерева. Удаляет неиспользуемые элементы и осуществляет дополнительные проверки в режиме отладки — в том числе на дублирование глобальных ключей.
  • reassemble обеспечивает работу механизма HotReload. Этот механизм позволяет не пересобирать проект при изменениях, а отправлять новую версию кода на DartVM и инициировать обновление дерева.

Что такое PipelineOwner?

PipelineOwner — менеджер сборки, который занимается работой с деревом отображения.


Что такое сборщик мусора?

Garbage Collector - это алгоритм, наблюдает за ссылками и очищает память с целью предотвращения её переполнения.

(!) В процессе сборки мусора слой Dart Framework создает канал взаимодействия со слоем Flutter Engine, посредством которого узнает о моментах простоя приложения и отсутствия пользовательского взаимодействия. В эти моменты Dart Framework запускает процесс оптимизации памяти, что позволяет сократить влияния на пользовательский опыт и стабильность приложения.

Сборщик молодого мусора image

Используемый объём памяти можно разделить на два пространства: активное и неактивное. Новые объекты располагаются в активной части, где по мере её заполнения, живые объекты переносятся из активной области памяти в неактивную, игнорируя мёртвые объекты. Затем неактивная половина становится активной. Этот процесс имеет цикличный характер.

Сборщик старого мусора (Parallel Marking and Concurrent Sweeping)

image

  1. Осуществляется обход дерева объектов, используемые объекты помечаются специальной меткой.
  2. Во время второго этапа происходит повторный проход по дереву объектов, в ходе которого непомеченные в первом этапе объекты перерабатываются
  3. Все метки стираются

Подробнее


Task Runners во Flutter

image

  • Platform Task Runner
  • UI Task Runner
  • Raster Task Runner
  • IO Task Runner

Подробнее

Разное

Что такое тестирование?

Тестирование — это процесс испытания приложения для проверки соответствия между реальным поведением и ожидаемым


Виды тестов

  • Модульный тест тестирует одну функцию, метод или класс. Его цель - проверить правильность работы определенной функции, метода или класса. Внешние зависимости для тестируемого модуля обычно передаются как параметр.
  • Виджет тест тестирует один виджет. Цель такого теста — убедиться, что пользовательский интерфейс виджета выглядит и взаимодействует, как запланировано. Тестирование виджета происходит в тестовой среде, которая обеспечивает контекст жизненного цикла виджета. Также тестируемый виджет должен иметь возможность получать действия и события пользователя и отвечать на них .
  • Интеграционный тест тестирует все приложение или его большую часть. Цель интеграционного теста — убедиться, что все тестируемые виджеты и сервисы работают вместе, как ожидалось. Кроме того, вы можете использовать интеграционные тесты для проверки производительности вашего приложения. Как правило, интеграционный тест выполняется на реальном устройстве или эмуляторе.

Что такое TDD?

TDD — это методика разработки приложений, при которой сначала пишется тест, покрывающий желаемое изменение, а затем — код, который позволит пройти тест.


Основные git команды

  • git init Позволяет проинициализировать репозиторий в текущей папке
  • git status Показывает текущий статус
  • git add Отслеживает изменения файлов
  • git add Добавляет все файлы
  • git commit Сохраняет изменения в коммит
  • git commit -m 'commit message' Создает коммит с сообщением
  • git branch Показывает список веток
  • git branch branch-name Создает новую ветку branch-name
  • git branch -D branch-name Удаляет ветку branch-name
  • git checkout Переключается на другую ветку
  • git checkout -b branch-name Создает и переключается на ветку branch-name
  • git merge Совмещает текущую ветку с выбранной
  • git merge branch-name Совмещает текущую ветку с branch-name
  • git push Заливает текущие локальные коммиты в удаленный репозиторий
  • git pull Забирает изменения с удаленного репозитория в локальный
  • git clone Клонирует проект с удаленного репозитория
  • git cherry-pick Смержить коммит с одной ветки в другую ветку

Git Flow

Фича

  1. Начало новой фичи. Создаём новую ветку feature/future_name из develop
  2. Завершение фичи. Сливаем feature/future_name в develop, удаляем feature/future_name
  3. Начало релиза. Создаём ветку релиза release/vX.X.X, ответляя от ветки develop
  4. Завершение релиза. Ветка релиза release/vX.X.X сливается в master, релиз помечается тегом, ветка релиза сливается в develop, ветка релиза удаляется

Фикс

  1. Начало исправления. От ветки master создаём hotfix/fix_name
  2. Завершение исправления. Из ветки hotfix/fix_name исправление сливается в develop и master, ветка фикса удаляется

Паттерны разработки

Подробнее

Компоновщик

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

Проблема
Есть два объекта: Продукт и Коробка. Коробка может содержать несколько Продуктов и других Коробок поменьше. Те, в свою очередь, тоже содержат либо Продукты, либо Коробки и так далее. Нам нужно сформировать заказ, который состоит из сложной структуры коробок и продуктов

Решение

  1. Создайте общий интерфейс компонентов, который объединит операции контейнеров и простых компонентов дерева
  2. Создайте класс, не имеющих дальнейших ответвлений
  3. Создайте класс компонентов-контейнеров и добавьте в него массив для хранения ссылок на вложенные компоненты. Этот массив должен быть способен содержать как простые, так и составные компоненты
  4. Реализуйте в контейнере методы интерфейса компонентов. Контейнеры должны делегировать основную работу своим дочерним компонентам.
  5. Добавьте операции добавления и удаления дочерних компонентов в класс контейнеров.

Декоратор

Декоратор - структурный паттерн проектирования, который позволяет динамически добавлять объектам новую функциональность, оборачивая их в полезные «обёртки»

Проблема
Есть два класса. Один возвращает данные, второй их принимает. Нужно их связать, но проблема в том, что класс работют с разными видами данных

Решение

  1. Создайте класс-адаптер, который получает, преобразует данные в нужный нам вид и возвращает их
  2. Оберните этим адаптером класс, который отдаёт данные

Посетитель

Посетитель - поведенческий паттерн проектирования, который позволяет добавлять в программу новые операции, не изменяя классы объектов, над которыми эти операции могут выполняться

Проблема
Есть несколько объектов разных классов, нужно пройтись по ним и собрать какие-то данные

Решение

  1. Создайте интерфейс посетителя и объявите в нём методы «посещения» для каждого класса элемента, который существует в программе.
  2. Создайте интерфейс элементов. Объявите в нём абстрактный метод принятия посетителей
  3. Реализуйте методы принятия во всех конкретных элементах. Они должны переадресовывать вызовы тому методу посетителя, в котором тип параметра совпадает с текущим классом элемента.
  4. Для каждого нового поведения создайте конкретный класс посетителя. Приспособьте это поведение для работы со всеми типами элементов, реализовав все методы интерфейса посетителей.
  5. Создайте посетителя, а затем передайте их элементам, используя метод принятия.

Все

Порождающие. Отвечают за удобное и безопасное создание новых объектов или даже целых семейств объектов.

  • Factory Method (Фабричный Метод). Порождающий паттерн проектирования, который определяет общий интерфейс для создания объектов в суперклассе, позволяя подклассам изменять тип создаваемых объектов.
  • Abstract Factory (Абстрактная Фабрика). Порождающий паттерн проектирования, который позволяет создавать семейства связанных объектов, не привязываясь к конкретным классам создаваемых объектов.
  • Builder (Строитель). Порождающий паттерн проектирования, который позволяет создавать сложные объекты пошагово. Строитель даёт возможность использовать один и тот же код строительства для получения разных представлений объектов.
  • Prototype (Прототип). Порождающий паттерн проектирования, который позволяет копировать объекты, не вдаваясь в подробности их реализации.
  • Singleton (Одиночка). Порождающий паттерн проектирования, который гарантирует, что у класса есть только один экземпляр, и предоставляет к нему глобальную точку доступа.

Структурные. Отвечают за построение удобных в поддержке иерархий классов.

  • Adapter (Адаптер). Структурный паттерн проектирования, который позволяет объектам с несовместимыми интерфейсами работать вместе.
  • Bridge (Мост). Структурный паттерн проектирования, который разделяет один или несколько классов на две отдельные иерархии — абстракцию и реализацию, позволяя изменять их независимо друг от друга.
  • Composite (Компоновщик). Структурный паттерн проектирования, который позволяет сгруппировать множество объектов в древовидную структуру, а затем работать с ней так, как будто это единичный объект.
  • Decorator (Декоратор). Структурный паттерн проектирования, который позволяет динамически добавлять объектам новую функциональность, оборачивая их в полезные «обёртки».
  • Facade (Фасад). Структурный паттерн проектирования, который предоставляет простой интерфейс к сложной системе классов, библиотеке или фреймворку.
  • Flyweight (Легковес). Паттерн проектирования, который позволяет вместить бóльшее количество объектов в отведённую оперативную память. Легковес экономит память, разделяя общее состояние объектов между собой, вместо хранения одинаковых данных в каждом объекте.
  • Proxy (Заместитель). Структурный паттерн проектирования, который позволяет подставлять вместо реальных объектов специальные объекты-заменители. Эти объекты перехватывают вызовы к оригинальному объекту, позволяя сделать что-то до или после передачи вызова оригиналу.

Поведенческие. Решают задачи эффективного и безопасного взаимодействия между объектами программы.

  • Chain of Responsibility (Цепочка Обязанностей). Поведенческий паттерн проектирования, который позволяет передавать запросы последовательно по цепочке обработчиков. Каждый последующий обработчик решает, может ли он обработать запрос сам и стоит ли передавать запрос дальше по цепи.
  • Command (Команда). Поведенческий паттерн проектирования, который превращает запросы в объекты, позволяя передавать их как аргументы при вызове методов, ставить запросы в очередь, логировать их, а также поддерживать отмену операций.
  • Iterator (Итератор). Поведенческий паттерн проектирования, который даёт возможность последовательно обходить элементы составных объектов, не раскрывая их внутреннего представления.
  • Mediator (Посредник). Поведенческий паттерн проектирования, который позволяет уменьшить связанность множества классов между собой, благодаря перемещению этих связей в один класс-посредник.
  • Memento (Снимок). Поведенческий паттерн проектирования, который позволяет сохранять и восстанавливать прошлые состояния объектов, не раскрывая подробностей их реализации.
  • Observer (Наблюдатель). Поведенческий паттерн проектирования, который создаёт механизм подписки, позволяющий одним объектам следить и реагировать на события, происходящие в других объектах.
  • State (Состояние). Поведенческий паттерн проектирования, который позволяет объектам менять поведение в зависимости от своего состояния. Извне создаётся впечатление, что изменился класс объекта.
  • Strategy (Стратегия). Поведенческий паттерн проектирования, который определяет семейство схожих алгоритмов и помещает каждый из них в собственный класс, после чего алгоритмы можно взаимозаменять прямо во время исполнения программы.
  • Template Method (Шаблонный Метод). Поведенческий паттерн проектирования, который определяет скелет алгоритма, перекладывая ответственность за некоторые его шаги на подклассы. Паттерн позволяет подклассам переопределять шаги алгоритма, не меняя его общей структуры.
  • Visitor (Посетитель). Поведенческий паттерн проектирования, который позволяет добавлять в программу новые операции, не изменяя классы объектов, над которыми эти операции могут выполняться.

Подробнее

Алгоритмы

Подробнее

BinarySearch

binarySearch(List  list,  int  value,  int  min,  int  max) {
    if (min  >  max) {
        return  null;
    }
    final  mid  = (max  +  min) ~/  2;
    if (value  <  list[mid]) {
        return  binarySearch(list,  value,  min,  mid  -  1);
    } else  if (value  >  list[mid]) {
	    return  binarySearch(list,  value,  mid  +  1,  max);
    } else {
	    return  mid;
    }
}

About

❓Список вопросов и ответов к техническому интервью на позицию flutter-разработчика

https://github.com/p0dyakov/flutter_interview