Техника: Перемещение функций между объектами (рефакторинг М. Фаулера)

от автора

Начало Код с душком
Техника: Составление методов

В продолжении, техника рефакторинга по книге Рефакторинг. Улучшение существующего кода Мартин Фаулер.

Синтаксис будет на C#, но главное понимать идею, а её можно использовать в любом другом языке программирования.

Перемещение функций между объектами

  1. Перемещение метода (создайте новый метод с аналогичным телом в классе, который чаще им пользуется).
    Метод должен быть в классе, который лучше отражает его предметную область
    From
    class View {     private Shop shop;      private List<User> FilterUsers(List<User> users, decimal koef)     {         List<User> activeUsers = new List<User>();         foreach(User user  in users)         {             if(shop.IsActiveUser(user, koef))             {                 activeUsers.Add(user);             }         }         return activeUsers;     } }  class Shop {     public bool IsActiveUser(User user, decimal koef)     {         decimal rate = 1;         if(koef > 1000)         {             rate  = koef * 1.75;         }         return user.Boss || user.HasDiscount() && rate > 10;     } }  class User {     private bool boss;      public bool Boss     {         get { return boss; }     }          public bool HasDiscount()     {          // some condition     } } 

    To

    class View {     private Shop shop;      private List<User> FilterUsers(List<User> users, decimal koef)     {         List<User> activeUsers = new List<User>();         foreach(User user  in users)         {             if(user.IsActiveUser(koef))             {                 activeUsers.Add(user);             }         }         return activeUsers;     } }  class Shop { }  class User {     private bool boss;      public bool Boss     {         get { return boss; }     }          public bool HasDiscount()     {          // some condition     }      public bool IsActiveUser(decimal koef)     {         decimal rate = 1;         if(koef > 1000)         {             rate  = koef * 1,75;         }         return Boss || HasDiscount() && rate > 10;     } } 

  2. Перемещение поля (создайте в целевом классе новое поле).
    Поле должно быть в классе, который лучше отражает его предметную область
    From
    class Shop {     private decimal rate;     private DiscountType discountType;      public Shop(DiscountType discountType, decimal rate)     {         this.rate = rate;         this.discountType = discountType;     }      public decimal Rate     {         get{ return rate; }         set{ rate = value; }     }      public bool HasDiscount(DateTime from, DateTime to)     {         // some calculation of flag               return discountType.IsValid(rate) && flag;     }      public decimal GetDiscount(decimal amount, bool fullSum)     {         // some calculation of koef         return discountType.GetAmount(rate, fullSum) * koef;     } }  class DiscountType {         public bool IsValid(decimal rate)     {         // some calculation using rate     }      public decimal GetAmount(decimal rate, bool fullSum)     {         // some calculation using rate     } } 

    To

    class Shop {     private DiscountType discountType;      public Shop(DiscountType discountType)     {         this.discountType = discountType;     }      public bool HasDiscount(DateTime from, DateTime to)     {        // some calculation of flag              return discountType.IsValid() && flag;     }      public decimal GetDiscount(decimal amount, bool fullSum)     {        // some calculation of koef        return discountType.GetAmount() * koef;     } }  class DiscountType {         private decimal rate;      public DiscountType(decimal rate)     {         this.rate = rate;     }      public decimal Rate     {         get{ return rate; }         set{ rate = value; }     }      public bool IsValid()     {         // some calculation using rate     }      public decimal GetAmount(bool fullSum)     {         // some calculation using rate     } } 

  3. Выделение класса (создайте новый класс и переместите поля и методы из старого класса в новый).
    Класс должен содержать свою модель данных и методы для работы с ней, иначе он превращается в God object.
    From
    class User {     private string name;     private string street;     private string city;      public string Name     {         get{ return name; }         set{ name = value; }     }      public string Street     {         get{ return street; }         set{ city = value; }     }      public string City     {         get{ return city; }         set{ city = value; }     }      public string GetAddressInfo()     {         return string.Format("{0} \ {1}", city, street);     } } 

    To

    class User {     private string name;     private Address address;      public string Name     {         get{ return name; }         set{ name = value; }     }      public string GetAddressInfo()     {         return address.GetFullAddress();     } }  class Address {     private string city;     private string street;      public string Street     {         get{ return street; }         set{ city = value; }     }      public string City     {         get{ return city; }         set{ city = value; }     }        public string GetFullAddress()     {         return string.Format("Adress: {0} \ {1}", city, street);     } } 

  4. Встраивание класса (переместите все функции в другой класс и удалите исходный).
    Класс выполняет слишком мало функций.
    From
    class User {     private string name;     private Address address;      public string Name     {         get{ return name; }         set{ name = value; }     }      public Address GetAddress()     {         return address     } }  class Address {     private string areaCode;     private string country;      public string AreaCode     {         get{ return areaCode; }         set{ areaCode = value; }     }      public string Country     {         get{ return country; }         set{ country = value; }     } } 

    To

    class User {     private string name;     private string areaCode;     private string country;      public string AreaCode     {         get{ return areaCode; }         set{ areaCode = value; }     }      public string Country     {         get{ return country; }         set{ country = value; }     }     public string Name     {         get{ return name; }         set{ name = value; }     } } 

  5. Сокрытие делегирования (создайте методы, скрывающие делегирование).
    Инкапсуляция, некоторых частей системы.
    From
    class View {     private void Init()     {         // some code        User manager = currentUser.Office.Manager;     } }  class User {     private Department department;      public Department Office     {         get{ return department; }         set{ department = value; }     } }  class Department {     private User manager;      public Department(User manager)     {         this.manager = manager;     }      public User Manager         {         get{ return manager; }     } } 

    To

    class View {     private void Init()     {         // some code        User manager = currentUser.Manager;     } }  class User {     private Department department;      public User Manager         {         get{ return department.Manager; }     } }  class Department {     private User manager;      public Department(User manager)     {         this.manager = manager;     }      public User Manager         {         get{ return manager; }     } } 

  6. Удаление посредника (заставьте клиента обращаться к делегату непосредственно).
    Класс слишком занят простым делегированием.
    From
    class View {     private void Init()     {         // some code        User manager = currentUser.Manager;        decimal rate = currentUser.Rate;     } }  class User {     private Department department;     private UserType userType;      public User Manager         {         get{ return department.Manager; }     }     public decimal Rate        {         get{ return userType.Rate; }     } }  class Department {     private User manager;      public User Manager         {         get{ return manager; }     } }  class UserType {     private decimal rate;      public decimal Rate     {         get{ return rate; }     } } 

    To

    class View {     private void Init()     {         // some code        User manager = currentUser.Office.Manager;        decimal rate = currentUser.Type.Rate;     } }  class User {     private Department department;     private UserType userType;      public Department Office     {         get{ return department; }     }     public UserType Type         {         get{ return userType; }     } }  class Department {     private User manager;      public User Manager         {         get{ return manager; }     } }  class UserType {     private decimal rate;      public decimal Rate     {         get{ return rate; }     } } 

  7. Введение внешнего метода (у клиента добавьте метод, которому в качестве 1-го параметра передается класс сервера).
    Необходимо внести метод, но отсутсвует возможность модификации класса.
    From
    class View {     private string GetUserName()     {         // some code        return "\"" + userName + "\"";     }       private string GetCompanyName()     {         // some code        return "\"" + companyName + "\"";     } } 

    To

    class View {     private string GetUserName()     {         // some code        return userName.InDoubleQuote();     }       private string GetCompanyName()     {         // some code        return companyName.InDoubleQuote();     } }  static class StringExtension {     public static string InDoubleQuote(this string str)     {        return "\"" + str + "\"";     } } 

  8. Введение локального расширения (создайте обёртку для класса (или с помощью наследования) и дополните необходимыми методами).
    Необходимо внести методы, но отсутствует возможность модификации класса.
    From
    sealed class Account {     public decimal CalculateSum()     {         // calculation summ        return summ;     } } 

    To

    class ImportantAccount {     private Account account;      public decimal CalculateMaxSum()     {         // calculation rate        return account.CalculateSum() * rate;     } } 

Конфликты интересов (в рамках Review).

Как правило, всё споры/разногласия возникающие между review-ером и comitt-ерром можно разделить на следующие:

  • Коллеги обсуждают выбор подходящего решения, т.к. возникло разногласие по реализации. Всё в рамках корректного общения, они понимают, что делают общее дело и нужно просто выбрать решение (10%).
  • Коллегам нужно поболтать: пятница, тяжелая рабочая неделя или просто не хватает общения. Есть и такая категория людей, которым нужно просто потрещать. В принципе это не лишено здравого смысла, т.к. общение и обсуждения это здорово (10%).
  • Споры, которые приводят к новым правилам. Например, когда в .NET появилось ключевое слово var кто-то из коллег стал его использовать везде, кто-то наоборот. Поэтому при ревью возникали споры, что лучше. В итоге после некоторых дискуссий, было принято решение (10%).
    // если операнд с правой стороны, однозначно трактует возвращаемый тип, то можно использовать var var users = new List<User>(); var departments = user.Departmens.Cast<Department>();  // плохо var users = departments.Select(d => d.User).Where(u => !u.Deleted); var departments = service.GetNotDeletedDepartments(); 

  • Жаркие споры по поводу того, чьё решение лучше. И если программисты одного уровня, страсти могут разгораться. Причём все эти споры, лишь трата времени и желание показать себя крутыми (70%). Тут хочется дать совет, если вы ввязались в такой спор, никогда не переходите не личности. И фразу «Твое решение не подходит», лучше заменить на «Решение не совсем верно и объяснить свою точку зрения». Т.е. акцент надо делать на обсуждении решения, а не навыков коллеги.

    Забавные случаи:
    1) Review-ер — «необходимо поправить код»
    Commiter — «тебе не нравится ты и правь»/«ты нашёл ты и исправляй»

    2) Или как правильно

    void Method() { ...     if(flag)         koef = GetMaxRate(); ... } 

    или

    void Method() { ...     if(flag)     {         koef = GetMaxRate();     } ... } 

Надеюсь продолжить и дальше «Техника: Организация данных».

ссылка на оригинал статьи http://habrahabr.ru/post/174779/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *