Giter Site home page Giter Site logo

todo-list's People

Contributors

angular-cli avatar tamazlykar avatar

Stargazers

 avatar

Watchers

 avatar  avatar

todo-list's Issues

[Рефакторинг и обратное проектирование] Задание 2

Перед рефакторингом необходимо покрыть код тестами. Недочеты, выбранные в Задании 1 невозможно покрыть юнит-тестами, так как или для случая нельзя написать тест или код будет перенесен/удален/изменен и тесты придется писать снова. Поэтому было решено написать e2e тесты.
Тестовый файл
Вспомогательный файл страницы

Результат запуска тестов:
image

[Рефакторинг и обратное проектирование] Задание 1

  1. Ленивый класс и класс данных. Класс не делает ничего. В текущей реализации можно заменить на интерфейс или выделить функции и добавить в класс.

    export class Todo {
    title: string;
    isCompleted: boolean;
    created: any;
    constructor(title: string) {
    this.title = title;
    this.isCompleted = false;
    }
    }

  2. Каждый элемент имеет текущее состояние (Добавлен, Изменен или Удален). Из-за этого код содержит множество switch условий, в разных классах, что будет затруднять дальнейшее сопровождение. Если потребуется вносить изменения, то придется найти все switch условия и изменить каждый.

    private handleData(todoList: TodoMetadata[]) {
    todoList.forEach(todo => {
    switch (todo.type) {
    case ChangeType.added:
    this.addListData(todo);
    break;
    case ChangeType.modified:
    case ChangeType.removed:
    this.updateListData(todo);
    break;
    }
    })
    }

    switch (tempTodo.type) {
    case ChangeType.modified:
    this.handleModifiedChangeType(tempTodo, newTitle);
    break;
    case ChangeType.removed:
    this.handleRemovedChangeType(newTitle);
    break;
    }

Ангуляр это MVVM фреймворк, основными блоками которого являются "компоненты" и "сервисы".

  • Компонент это класс, связанный с View, который может передавать данные во View и реагировать на события View. Он является View Model из MVVM. Класс должен получать данные и отображать их. В идеале без какой либо логики. Компоненты делятся на smart и dumb, которые содержат логику (знают как получить данные, создать что-то и тд) и которые не содержат логику, а только отображают данные, соответственно.
  • Сервис это класс с Dependency Injection. Сервисы должны содержать всю бизнес логику и обратку в приложении.
  1. Компонент todo-item.component.ts, который отвечает за отображение на экране одного элемента списка, содержит больше количество логики внутри себя. Это затрудняет понимание класса и создает путаницу. Из очевидного:
  • Необходимо вынести код, который отвечает за открытие диалоговых окон и их вид.
  • Необходим вынести код, который отвечает за обработку конфликтов при параллельном изменении одних и тех-же данных в разных сессиях.
  1. Странное решение сгруппировать все типы действий, которые можно совершить над элементов списка и передать их через один "Output" (выход) в родительский класс, вместо того чтобы создать на каждое действие свой выход.

    private create() {
    this.emitAction(ActionType.add);
    }
    private update() {
    this.emitAction(ActionType.update);
    }
    public remove() {
    this.emitAction(ActionType.remove);
    }
    private emitAction(type: ActionType) {
    this.action.emit({ type, todo: this.todo });
    }

    В итоге из-за этого имеем лишний switch.
    public onItemAction(action: Action) {
    switch (action.type) {
    case ActionType.add:
    this.add(action.todo.data);
    break;
    case ActionType.remove:
    if (action.todo.type !== ChangeType.removed) {
    this.remove(action.todo);
    }
    this.removeListData(action.todo);
    break;
    case ActionType.update:
    this.update(action.todo);
    break;
    }
    }

    Если не вдаваться в код, то так же странно, что элемент списка имеет действие "создать". По идее, над элементом можно совершить 2 действия или обновить или удалить. В идеале необходимо избавиться от этого "лишнего" действия.

  2. Компонент todo-list.component.ts сам создает новые элементы списка. Если у нас будут другие компоненты, где можно создать новый элемент, то потом придется менять код создания во всех местах. Создание должно проходить в единственном месте. Необходимо его вынести в сервис.

    .then(() => this.todoService.add(new Todo(title)));
    return;
    }
    this.add(new Todo(title));

  3. Компонент todo-list.component.ts оперирует двумя интерфейсами для элементов. Где-то используется Todo, а где-то TodoMetadata. Это вводит путаницу.

    private add(todo: Todo) {
    this.todoService.add(todo);
    }
    private update(todo: TodoMetadata) {
    this.todoService.update(todo);
    }
    private remove(todo: TodoMetadata) {
    this.todoService.remove(todo);
    }

    Тем более класс TodoMetadata зависит от Todo. Можно было бы изначально использовать 2 класса. Первый, как модель хранится на сервере, второй, серверная модель + дополнительные данные, типо id и прочее, которые уже используются приложением, без лишней вложенности.
    export class Todo {
    title: string;
    isCompleted: boolean;
    created: any;
    constructor(title: string) {
    this.title = title;
    this.isCompleted = false;
    }
    }
    export interface TodoMetadata {
    id: string;
    type: ChangeType;
    data: Todo;
    }

  4. Временное поле.
    Данное поле заполняется и используется только когда существует конфликт при одновременном изменении одного и того же элемента. Данное поле должно удалиться при выделении логики и конфликтах в самостоятельный класс.

    private tempTodo: TodoMetadata;

  5. Компонент todo-item.component.ts, имеет функциональность изменения заголовка элемента. В текущей реализации мы сами проверяем целостность данных, введенных пользователем. Данный функционал можно реализовать через Reactive Form и добавить валидацию предоставляемую формами, и сделать более гибкую настройку. К тому же при изменении требований по валидации, очень легко и можно добавлять свои собственные функции для валидации ввода.

  6. Компонент todo-item.component.ts содержит много логики, которой он содержать не должен. Данный компонент должен лишь отображать список Todo. Компонент в свою очередь знает как получить данные, как обрабатывать эти данные в зависимости от типа с сервера и на основании решения добавляет или удаляет в список, создает новые списки Todo.

  7. Отсутствует обработки ошибок.
    Сервис может выбросить ошибку, при неверных данных, а так как используется класс без геттеров и сеттверов, то поле может быть не валидным или быть удалено.

    if (!id) {
    throw Error('Todo list ID should be provided!');
    }

    if (!this.currentList) {
    throw Error('Сurrent list reference is not installed');
    }

    Но, в коде который вызывает эти функции отсутствует перехватчик ошибок и код может поломаться.

[Рефакторинг и обратное проектирование] Задание 3

До рефакторинга система имела следующий вид, изображенный на схеме:
image
Данной системе был произведен рефакторинг с учетом задания #1 и еще некоторых размышлений автора.
В итоге была реализованная следующая система, изображенная на схеме:
image

Что было сделано:

  • Вся логика была вынесена из компонентов. Компонент TodoListPageComponent знает как получать список задач и передает его компонентам для отображения. Остальные компоненты только отображают данные и могут хранить некоторое внутренне состояние компонента.
  • Компоненты используют один интерфейс модели Todo.
  • Выделен отдельный сервис для работы с диалоговыми окнами.
  • Добавлен Store это библиотека ngrx, реализующая паттерн Flux. Таким образом имеем единый источник хранения данных в системе и все действия проходят через него.
  • Выделены FirebaseTodoBackendService и TodoService. FirebaseTodoBackendService отвечает за работу с Backend. TodoService предоставляет интерфейс для работы приложению и содержит логику обработки конфликтов.

После рефакторинга была проверена работоспособность системы используя тесты написанные в рамках задания #2
image

Код с изменениям можно найти в PR #4

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.