Рефакторинг

Кирилл Корняков (Intel, ННГУ)
Антон Бевзюк (Додо Пицца)

Сентябрь 2018

Содержание

Software Entropy

Технический долг

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

Wikipedia

Технический долг

  • Давление бизнеса
  • Недостаток процессов или понимания
  • Неумение создания слабо связанных компонентов
  • Недостаток тестов
  • Недостаток документации
  • Недостаток взаимодействия
  • Параллельная разработка
  • Отложенный рефакторинг
  • Нехватка знаний

Что делать?

Инженерные практики:

Определение рефакторинга

Изменение внутренней структуры программы
без изменений ее видимого поведения
с целью облегчить понимание и удешевить модификацию.

М. Фаулер

Мартин Фаулер

Кент Бек

Цели рефакторинга

Сокращение долгов

Метафоры указывают на необходимость непрерывной работы по поддержанию кода в чистоте! - Роберт Мартин: правило бойскаута, уборка посуды - Энди Хант и Дейв Томас: теория разбитых окон (из криминологии)

Процесс рефакторинга

Техника безопасности

Code Smells

Code Smells

Design Smells

  1. Missing abstraction
  2. Multifaceted abstraction
  3. Duplicate abstraction
  4. Deficient encapsulation
  5. Unexploited encapsulation
  6. Broken modularization
  7. Insufficient modularization
  8. Cyclically-dependent modularization
  9. Unfactored hierarchy
  10. Broken hierarchy
  11. Cyclic hierarchy

https://en.wikipedia.org/wiki/Design_smell

Разумные причины выполнения рефакторинга

  1. Код повторяется
  2. Метод слишком велик
  3. Цикл слишком велик или вложен в другие циклы
  4. Класс имеет плохую связанность
  5. Интерфейс класса не формирует согласованную абстракцию
  6. Метод принимает слишком много параметров
  7. Отдельные части класса изменяются независимо от остальных частей
  8. При изменении программы требуется параллельно менять несколько классов
  9. Вам приходится параллельно менять несколько иерархий наследования
  10. Родственные элементы данных, используемые вместе, не организованы в классы
  11. Метод использует больше элементов другого класса, чем своего собственного
  12. Элементарный тип данных перегружен

"Совершенный код" С. Макконнелл

Разумные причины выполнения рефакторинга (2)

  1. Класс имеет слишком ограниченную функциональность
  2. По цепи методов передаются бродячие данные
  3. Объект-посредник ничего не делает
  4. Один класс слишком много знает о другом классе
  5. Метод имеет неудачное имя
  6. Данные-члены сделаны открытыми
  7. Подкласс использует только малую долю методов своих предков
  8. Сложный код объясняется при помощи комментариев
  9. Код содержит глобальные переменные
  10. Перед вызовом метода выполняется подготовительный код (после вызова метода выполняется код «уборки»)
  11. Программа содержит код, который может когда-нибудь понадобиться

"Совершенный код" С. Макконнелл

Refactorings Catalog

Популярные рефакторинги

Unit Tests

[TestMethod]
public void FetchUsingCreatedBySpecification() {
            var user1 = new User(new Distributor("", "2"));
            var user2 = new User(new Distributor("", "3"));
            var user3 = new User("", "3", Role.Administrator);

            var distributor1 = new Distributor { CreatedBy = user1 };
            var distributor2 = new Distributor { CreatedBy = user2 };
            var distributor3 = new Distributor { CreatedBy = user3 };

            var specification = new CreatedBy<Distributor>(user1);

            Assert.IsTrue(specification.IsSatisfiedBy(distributor1));
            Assert.IsFalse(specification.IsSatisfiedBy(distributor2));
            Assert.IsFalse(specification.IsSatisfiedBy(distributor3));
}

Unit Tests

[TestMethod]
public void FetchUsingCreatedBySpecification() {
            var john = new User(new Distributor("", "john"));
            var mike = new User(new Distributor("", "mike"));
            var bob = new User("", "bob", Role.Administrator);

            var sonOfJohn = new Distributor {CreatedBy = john};
            var sonOfMike = new Distributor {CreatedBy = mike};
            var daughterOfBob = new Distributor {CreatedBy = bob};

            var createdByJohn = new CreatedBy<Distributor>(john);

            Assert.IsTrue(createdByJohn.IsSatisfiedBy(sonOfJohn));
            Assert.IsFalse(createdByJohn.IsSatisfiedBy(sonOfMike));
            Assert.IsFalse(createdByJohn.IsSatisfiedBy(daughterOfBob));
}

Unit Tests

new DateTime(2009, 11, 12)

public static DateTime of2009(this double ddMM) {
   var day = (int) Math.Round(ddMM, 0);
   var month = (int) Math.Round(ddMM*100, 0) - day * 100;
   return new DateTime(2009, month, day);
}

12.11.of2009();
var month = new CalendarMonth(2009, 10);

var month = Oct.of2009();

internal static class Oct {
    public static CalendarMonth of2009 {
        get { return new CalendarMonth(2009, 10); }
    }
}

Unit Tests

var customer = new Customer();
customer.Name = "Vasya Pupkin";
var order = new  Order();
order.Customer = customer;
order.Date = new DateTime(2009, 10, 11);
var orderItem = new orderItem();
orderItem.Product = new Product("Lays");
orderItem.Amount = 3;
orderItem.Price = 45.30;
order.Items.Add(orderItem);
orderItem.Product = new Product("Beer");
orderItem.Amount = 3;
orderItam.Price = 20.50;
order.Items.Add(orderItem)

Unit Tests

var order = new  OrderBuilder()
    .WithCustomer(“Vasya Pupkin”)
    .WithDate(11.10.of2009())
    .WithLineItem()
        .WithProduct(“Lays”)
        .WithAmount(3)
        .WithPrice(45.30)
    .WithLineItem()
        .WithProduct(“Beer”)
        .WithAmount(3)
        .WithPrice(20.50)
    .GetResult();

Книги

Контрольные вопросы

  1. Определение рефакторинга и его цели.
  2. Техника безопасности.
  3. Понятие технического долга.
  4. Примеры запахов (code smell) и способы их устранения (3-5 примеров).
  5. Примеры рефакторингов (конкретных приемов), не менее 6 штук.

Спасибо!

Вопросы?