SlideShare a Scribd company logo
Сила рефакторинга


«Управление сложностью — самый важный технический аспект разработки ПО.»
   Стивен Макконнелл, «Совершенный код».
Disclaimer


Все нижеизложенное — это IMHO без претензии на абсолютность.
При создании презентации ни один класс не пострадал.
К черту все! Берись и делай!


Доклад носит сугубо практический характер, чтобы можно было сразу брать и
   применять в работе. Многие теоретические выкладки упрощены, либо опущены
   вовсе.
Что же такое рефакторинг?


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

На практике рефакторинг – это прежде всего мышление. Можно выделить два
   направления, над которыми можно работать:
    умение видеть те места архитектуры, которые можно улучшить;
    знание нескольких вариантов улучшения и умение выбрать соответствующий
       условиям.
… структура пакета не может быть спроектирована в соответствии с методикой
   разработки ”сверху вниз”. Это означает, что изначально невозможно продумать все
   детали будущего проекта. Они появляются по мере роста и изменения системы. (С)
   Роберт Мартин (Uncle Bob), ”Быстрая разработка ПО”, 2004

Мартин Фаулер об эволюционном проектировании:
  http://www.maxkir.com/sd/designDead_RUS.html

Из практики, лучшее – это итерационный рефакторинг.
Из нашей практики


Перед началом проекта/функционала знание предметной сферы 0%, заблуждение на
   эту тему и готовность сразу сделать идеально - 100%

Ничто нельзя спроектировать без стадии живых прототипов, испытания функционала
  системы в боевых условиях (или близких к таковым) . Самолеты у конструкторов
  на чертежах никогда не падают, и у дизайнеров никогда макет не едет (нередко из-за
  текста Lorem ipsum dolor)

Живые прототипы чаще всего делаются быстро за счет хардкода. Это нормально,
  главное – сделать быстро, чтобы не жаль было выкинуть. PROFIT:

    ваше понимание предметной сферы возрастет на порядок    ;
    требования заказчика, довольного оперативной реакцией на изменения,
       устаканятся на время;
    заодно вы получите информацию о том, какие в системе сущности, какие между
       ними связи, где неизменяемая (временно) часть системы и т.д.
Таким образом, решение парадокса ”проектирование сразу vs частые изменения” - это
   выделение двух чередующихся фаз в спирали развития проекта:

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


DRY – Don’t repeat yourself (не повторяй себя), или SPOT – Single point of truth (единая
  точка правды)

KISS – keep it simple stupid (делайте вещи проще)

YAGNI - You ain’t gonna need it – вам это не понадобится

Подробнее на Хабре в статье http://habrahabr.ru/post/144611/
Это слишком абстрактно! А конкретней?


Эти принципы, конечно, хороши. Но они скорее носят философский характер, ибо
   сфера их применимости очень широка.

Для практики хорошо подходят принципы SOLID (и некоторые другие).
  Сформулировал Robert C. Martin aka Uncle Bob, один из авторов Agile Manifest,
  выдающийся разработчик, в совершенстве ругающийся на Java и C.

Книга ”Rapid Software Development” (”Быстрая разработка ПО”) раскрывает тему SOLID
   на пять. Must read.
Почему проекты приходится переписывать с нуля


         N (атомарные изменения)



  Nmin
                                   Kdead — точка невозврата




                                                     K (итерации)



         График изменений существующего кода
         без рефакторинга и соблюдения принципов SOLID
Почему проекты приходится переписывать с нуля


         N (атомарные изменения)




  Nmin                                    реальная жизнь




                                            идеал



                                        K (итерации)



         График изменений существующего кода
         при соблюдении принципа Open-Close,
         с регулярным рефакторингом
Вся соль в управлении зависимостями


                      Направленный ациклический граф -- случай направленного графа, в
                      котором отсутствуют направленные циклы, то есть пути,
                      начинающиеся и кончающиеся в одной и той же вершине




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




«Граф зависимостей между компонентами ПО (классы, пакеты, методы) должен быть ациклическим»
Роберт Мартин, 1996
Принципы SOLID



Single responsibility principle – принцип единой ответственности
Open/Close principle – принцип открытия/закрытия
Liskov substitution principle – принцип подстановки Лискоу
Interface segregation principle – принцип отделения интерфейса
Dependency inversion principle - принцип инверсии зависимостей
Принцип единой ответственности
Принцип единой ответственности


Принцип единой ответственности – сущность (класс, пакет, метод и т.д.) обладает
  только 1 ответственностью, поэтому существует только 1 причина, приводящая к
  ее изменению.

Зачем нужен? Потому что это позволяет быстро и безболезненно реагировать на
   изменение бизнес-требований.
Высшая раса нарушающая SRP – это God Object


Что такое ”плохо”:
Борьба с высшей расой

Лучше так:
Принцип открытия/закрытия
Принцип открытия/закрытия


Принцип открытия/закрытия – код должен быть открыт для расширения, но закрыт для
  модификации
Чем плох этот код?


class Logger {
    public function log($text) {
         // Сохраняем текст в лог (лог у нас будет храниться в файлах)
    }
}

class Product {
    private $_logger;

    public function __construct() {
        $this->_logger = new Logger();
    }

    /* Продать товар */
    public function sale() {
         // … продаем товар

        // Записываем дату продажи в лог
        $this->_logger->log('Sale time: '. time());
    }
}
Лог продаж в файлах?! Это же отстой!
          Изменение требований: лог надо хранить в БД

class DBLogger {
    public function log($text) {
        // Сохраняем что-то в лог (лог у нас будет храниться в БД)
    }
}

class Product {
    private $_logger;

    public function __construct() {
        // Меняем класс Product, чтоб поменять логер (помните про SRP?)
        $this->_logger = new DBLogger();
    }

    /* Продать товар*/
    public function sale() {
         // Продаем товар
         // ...

        // Записываем дату продажи в лог
        $this->_logger->log('Sale time: '. time());
    }
}
Готовимся к борьбе с изменениями требований (а не к
                  борьбе с менеджерами)
interface ILogger {
       public function log($text);
}

class Logger implements ILogger {
      public function log($text) {
            // Сохраняем что-то в лог (лог у нас будет храниться в файлах)
      }
}

class DBLogger implements ILogger {
      public function log($text) {
            // Сохраняем что-то в лог (лог у нас будет храниться в БД)
      }
}

class Product {
      private $_logger;

      public function __construct(ILogger $logger) {
            $this->_logger = $logger;
      }

      /* Продать товар */
      public function sale() {
            // Продаем товар
            // ...

             // Записываем дату продажи в лог
             $this->_logger->log('Sale time: '. time());
      }
}
Принцип подстановки Лискоу
Принцип подстановки Лискоу


Принцип подстановки Лискоу – дочерний класс не должен отрицать поведение
  родительского класса и должна быть возможность использовать дочерний класс
  везде, где использовался родительский класс.
Принцип отделения интерфейса
Принцип отделения интерфейса


Принцип отделения интерфейса – клиенты не должны попадать в зависимость от
  методов, которыми они не пользуются

Клиенты определяют, какие интерфейсы им нужны.
Делаем трансформера (пока что все хорошо)




interface IMegaTransformer {
     public function transformToCar();
     public function transformToShip();
     public function transformToPlane();
}

class MegaTransformer implements IMegaTransformer {
    public function transformToCar() {
        echo 'Я преобразовался и стал спортивной машиной!';
    }
    public function transformToShip() {
        echo 'Я преобразовался и стал сверхбыстрым катером!';
    }
    public function transformToPlane() {
        echo 'Я преобразовался и стал истребителем!';
    }
}
А теперь нужно сделать менее крутых трансформеров (в
             каждом из них теперь костыли)
class TaxiTransformer implements IMegaTransformer {
      public function transformToCar() {
            echo 'Я преобразовался и стал такси!';
      }
      public function transformToShip() {
            throw new Exception('Данная трансформация не поддерживается');
      }
      public function transformToPlane() {
            throw new Exception('Данная трансформация не поддерживается');
      }
}
class StelthTransformer implements IMegaTransformer {
      public function transformToCar() {
            throw new Exception('Данная трансформация не поддерживается');
      }
      public function transformToShip() {
            throw new Exception('Данная трансформация не поддерживается');
      }
      public function transformToPlane() {
            echo 'Я преобразовался и стал стелсом!';
      }
}
class IcebreakerTransformer implements IMegaTransformer {
      public function transformToCar() {
            throw new Exception('Данная трансформация не поддерживается');
      }
      public function transformToShip() {
            echo 'Я преобразовался и стал ледоколом!';
      }
      public function transformToPlane() {
            throw new Exception('Данная трансформация не поддерживается');
      }
}
Лучше иметь такой набор интерфейсов




interface ICarTrsansformer {
     public function transformToCar();
}

interface IShipTrsansformer {
     public function transformToShip();
}

interface IPlaneTrsansformer {
     public function transformToPlane();
}
И такой набор классов

class MegaTransformer implements ICarTransformer, IShipTransformer, IPlaneTransformer {
     public function transformToCar() {
          echo 'Я преобразовался и стал спортивной машиной!';
     }
     public function transformToShip() {
          echo 'Я преобразовался и стал сверхбыстрым катером!';
     }
     public function transformToPlane() {
          echo 'Я преобразовался и стал истребителем!';
     }
}

class TaxiTransformer implements ICarTransformer {
     public function transformToCar() {
          echo 'Я преобразовался и стал такси!';
     }
}

class StelthTransformer implements IPlaneTransformer {
     public function transformToPlane() {
           echo 'Я преобразовался и стал стелсом!';
     }
}

class IcebreakerTransformer implements IShipTransformer {
     public function transformToShip() {
          echo 'Я преобразовался и стал ледоколом!';
     }
}
Принцип инверсии зависимостей
Принцип инверсии зависимостей


Самый важный принцип IMHO

Форма IoC (Inversion of Control)

Принцип инверсии зависимостей – зависимости внутри системы стоятся на основе
  абстракций. Модули верхнего уровня не зависят от модулей нижнего уровня.
  Абстракции не зависят от подробностей.

На практике помогает избежать циклов в схеме зависимостей.
Анатомия зависимостей
      (X зависит от Y + циклическая зависимость)




Зависимости бывают:
- Прямые;
- Транзитивные;
- Циклические.
Зависимость модуля верхннго уровня от модулей нижнего
                       уровня




class siteMapBuilder {
    private $_dataStorage;
    private $_webGrabber;

    public function __construct() {
        $this->_dataStorage = new FileDataStorage();
        $this->_webGrabber = new CurlWebGrabber();
    }

    // ... Здесь какие-то методы для построения карты сайта
}
Освобождаем SiteMapBuilder от
              зависимостей (инвертируем зависимости)




class siteMapBuilder {
    private $_dataStorage;
    private $_webGrabber;

    public function __construct(WebGrabber $dataStorage, DataStorage $webGrabber) {
        $this->_dataStorage = $dataStorage;
        $this->_webGrabber = $webGrabber;
    }

    // ... Здесь какие-то методы для построения карты сайта
}
Разделение архитектуры по слоям




«... любые хорошо структурированные объектно-ориентированные
архитектуры имеют четко определенные слои, каждый из которых
поддерживает некоторый компактный набор служб с помошью хорошо
определенного и контролируемого интерфейса» Г. Буч
Усовершенствованное разделение архитектуры по слоям




Здесь мы избавились не только от транзитивной зависимости
между TopLayer и DeepLayer, но и от прямых зависимостей
Как оценивать рефакторинг


Рефакторинг, по сути дела – улучшение архитектуры, и в оптимальном варианте
  хорошо бы понимать, куда идет движение.

Различных качественных и количественных характеристик существует очень много,
   нужно подбирать под себя.

Распространенные качественные:

    Жесткость, хрупкость, монолитность
Количественные:
    Связанность (coupling, нужно стремиться к низкой связанности);
    Связность или сцепление (cohesion, тут все наоборот);
    Метрики от Роберта Мартина
    Процент покрытия тестами
    Over 9000
Эволюционным подходом можно подобрать оптимальные (у нас пока в процессе)
А как же паттерны?


Паттерны – не панацея, у каждого есть свои плюсы и минусы

Системный подход при принятии решения о применении паттерна

    какие из выбранных показателей архитектуры мы улучшаем и ухудшаем?
    => какие принципы здесь следует применить?
    => выбор паттерна из подходящих, либо отказ от него.
Пример
    в данном участке кода очень высокая связанность, в одном классе куча
       зависимостей от других.
    => здесь нарушены: принцип единой ответствености, принцип инверсии
       зависимостей, принцип открытия/закрытия
    => условный блок с выбором, какой из трех классов для работы БД использовать,
       заменяем паттерном Стратегия.
    и т.д.
Все!


                                 Спасибо за внимание!

                              Подборка ссылок и т.д.:
  https://docs.google.com/document/d/1mUigVpqtQ-ZtQfN5fW1qOzQsoKRsf3t0xyz9At8dHLA/edit



Вопросы?

  Ершов Александр

  Skype: alex.cord

  E-mail: ershoff@lightsoft.ru

More Related Content

PPTX
Как писать красивый код или основы SOLID
PDF
ук 03.001.02 2011
PPTX
SOLID Principles in the real world
PPTX
Модульная структура
PDF
C++ STL & Qt. Занятие 10.
PPTX
Как приручить реактивное программирование
PPTX
GRASP – паттерны Объектно-Ориентированного Проектирования
Как писать красивый код или основы SOLID
ук 03.001.02 2011
SOLID Principles in the real world
Модульная структура
C++ STL & Qt. Занятие 10.
Как приручить реактивное программирование
GRASP – паттерны Объектно-Ориентированного Проектирования

What's hot (20)

PPTX
Yuri Trukhin - Software developement best practices
PDF
SOLID & GRASP
PPT
Шаблоны разработки ПО. Часть 1. Введние
PDF
C++ STL & Qt. Занятие 03.
PDF
C# Web. Занятие 02.
PPT
Шаблоны разработки ПО. Рефакторинг
PPTX
PDF
Погружение в SObjectizer 5.5. Вводная часть
PPTX
PostSharp - Threading Model Library
PDF
PostSharp - Threading Model
PPTX
PDF
CodeFest 2014. Гайдаренко О. — Промисы и jQuery Промисы
PDF
C++ STL & Qt. Занятие 04.
PPT
Введение в язык программирования «Java»
PDF
API design in java project
PDF
Денис Кормалев — Qt. Как выжить на минном поле. Советы сапёру
PDF
Глава 3: примитивные типы и операции с ними в Java
PDF
C++ STL & Qt. Занятие 11.
DOC
Конспект лекций по курсу "Шаблоны разработки ПО"
Yuri Trukhin - Software developement best practices
SOLID & GRASP
Шаблоны разработки ПО. Часть 1. Введние
C++ STL & Qt. Занятие 03.
C# Web. Занятие 02.
Шаблоны разработки ПО. Рефакторинг
Погружение в SObjectizer 5.5. Вводная часть
PostSharp - Threading Model Library
PostSharp - Threading Model
CodeFest 2014. Гайдаренко О. — Промисы и jQuery Промисы
C++ STL & Qt. Занятие 04.
Введение в язык программирования «Java»
API design in java project
Денис Кормалев — Qt. Как выжить на минном поле. Советы сапёру
Глава 3: примитивные типы и операции с ними в Java
C++ STL & Qt. Занятие 11.
Конспект лекций по курсу "Шаблоны разработки ПО"
Ad

Similar to Refactoring (20)

PPTX
разработка бизнес приложений (7)
PPT
Design Principles
PDF
Принципы программирования [NoBugs WTF PRO уровень].pdf
PPTX
SOLID – принципы объектно-ориентированного дизайна
PPTX
Практические аспекты разработки ПО #3
PPTX
разработка бизнес приложений (8)
PPTX
SOLID
PPT
Евгений Кривошеев: Фундаментальные правила и принципы проектирования ПО
PPT
Design Rules And Principles
PPTX
Tdd php
PDF
Архитектура в Agile: слабая связность
PPTX
Переписать нельзя рефакторить
PDF
Netpeak Talks #3: Масштабируемое приложение на PHP
PPTX
PPTX
Принципы объектно-ориентированного дизайна
PPTX
разработка бизнес приложений (6)
PDF
Как разраба
PPTX
Промышленная разработка ПО. Лекция 3. Особенности работы программиста. Часть...
PDF
CodeFest 2011. Макаров А. — Как разрабатывается Yii
PPTX
Software engineering. Введение в специальность. Проектирование, требования
разработка бизнес приложений (7)
Design Principles
Принципы программирования [NoBugs WTF PRO уровень].pdf
SOLID – принципы объектно-ориентированного дизайна
Практические аспекты разработки ПО #3
разработка бизнес приложений (8)
SOLID
Евгений Кривошеев: Фундаментальные правила и принципы проектирования ПО
Design Rules And Principles
Tdd php
Архитектура в Agile: слабая связность
Переписать нельзя рефакторить
Netpeak Talks #3: Масштабируемое приложение на PHP
Принципы объектно-ориентированного дизайна
разработка бизнес приложений (6)
Как разраба
Промышленная разработка ПО. Лекция 3. Особенности работы программиста. Часть...
CodeFest 2011. Макаров А. — Как разрабатывается Yii
Software engineering. Введение в специальность. Проектирование, требования
Ad

Refactoring

  • 1. Сила рефакторинга «Управление сложностью — самый важный технический аспект разработки ПО.» Стивен Макконнелл, «Совершенный код».
  • 2. Disclaimer Все нижеизложенное — это IMHO без претензии на абсолютность. При создании презентации ни один класс не пострадал.
  • 3. К черту все! Берись и делай! Доклад носит сугубо практический характер, чтобы можно было сразу брать и применять в работе. Многие теоретические выкладки упрощены, либо опущены вовсе.
  • 4. Что же такое рефакторинг? Рефакторинг – это улучшение внутренней структуры системы без изменения функциональности. Оптимизация производительности нередко включается сюда, но на самом деле она не относится к рефакторингу. На практике рефакторинг – это прежде всего мышление. Можно выделить два направления, над которыми можно работать: умение видеть те места архитектуры, которые можно улучшить; знание нескольких вариантов улучшения и умение выбрать соответствующий условиям. … структура пакета не может быть спроектирована в соответствии с методикой разработки ”сверху вниз”. Это означает, что изначально невозможно продумать все детали будущего проекта. Они появляются по мере роста и изменения системы. (С) Роберт Мартин (Uncle Bob), ”Быстрая разработка ПО”, 2004 Мартин Фаулер об эволюционном проектировании: http://www.maxkir.com/sd/designDead_RUS.html Из практики, лучшее – это итерационный рефакторинг.
  • 5. Из нашей практики Перед началом проекта/функционала знание предметной сферы 0%, заблуждение на эту тему и готовность сразу сделать идеально - 100% Ничто нельзя спроектировать без стадии живых прототипов, испытания функционала системы в боевых условиях (или близких к таковым) . Самолеты у конструкторов на чертежах никогда не падают, и у дизайнеров никогда макет не едет (нередко из-за текста Lorem ipsum dolor) Живые прототипы чаще всего делаются быстро за счет хардкода. Это нормально, главное – сделать быстро, чтобы не жаль было выкинуть. PROFIT: ваше понимание предметной сферы возрастет на порядок ; требования заказчика, довольного оперативной реакцией на изменения, устаканятся на время; заодно вы получите информацию о том, какие в системе сущности, какие между ними связи, где неизменяемая (временно) часть системы и т.д. Таким образом, решение парадокса ”проектирование сразу vs частые изменения” - это выделение двух чередующихся фаз в спирали развития проекта: быстрое прототипирование с использованием живых данных, сценариев работы и т.д.; превращение прототипа в качественный функционал, рефакторинг.
  • 6. Три основополагающих принципа разработки ПО DRY – Don’t repeat yourself (не повторяй себя), или SPOT – Single point of truth (единая точка правды) KISS – keep it simple stupid (делайте вещи проще) YAGNI - You ain’t gonna need it – вам это не понадобится Подробнее на Хабре в статье http://habrahabr.ru/post/144611/
  • 7. Это слишком абстрактно! А конкретней? Эти принципы, конечно, хороши. Но они скорее носят философский характер, ибо сфера их применимости очень широка. Для практики хорошо подходят принципы SOLID (и некоторые другие). Сформулировал Robert C. Martin aka Uncle Bob, один из авторов Agile Manifest, выдающийся разработчик, в совершенстве ругающийся на Java и C. Книга ”Rapid Software Development” (”Быстрая разработка ПО”) раскрывает тему SOLID на пять. Must read.
  • 8. Почему проекты приходится переписывать с нуля N (атомарные изменения) Nmin Kdead — точка невозврата K (итерации) График изменений существующего кода без рефакторинга и соблюдения принципов SOLID
  • 9. Почему проекты приходится переписывать с нуля N (атомарные изменения) Nmin реальная жизнь идеал K (итерации) График изменений существующего кода при соблюдении принципа Open-Close, с регулярным рефакторингом
  • 10. Вся соль в управлении зависимостями Направленный ациклический граф -- случай направленного графа, в котором отсутствуют направленные циклы, то есть пути, начинающиеся и кончающиеся в одной и той же вершине Идеальный случай схемы зависимостей — ациклический направленный граф. «Граф зависимостей между компонентами ПО (классы, пакеты, методы) должен быть ациклическим» Роберт Мартин, 1996
  • 11. Принципы SOLID Single responsibility principle – принцип единой ответственности Open/Close principle – принцип открытия/закрытия Liskov substitution principle – принцип подстановки Лискоу Interface segregation principle – принцип отделения интерфейса Dependency inversion principle - принцип инверсии зависимостей
  • 13. Принцип единой ответственности Принцип единой ответственности – сущность (класс, пакет, метод и т.д.) обладает только 1 ответственностью, поэтому существует только 1 причина, приводящая к ее изменению. Зачем нужен? Потому что это позволяет быстро и безболезненно реагировать на изменение бизнес-требований.
  • 14. Высшая раса нарушающая SRP – это God Object Что такое ”плохо”:
  • 15. Борьба с высшей расой Лучше так:
  • 17. Принцип открытия/закрытия Принцип открытия/закрытия – код должен быть открыт для расширения, но закрыт для модификации
  • 18. Чем плох этот код? class Logger { public function log($text) { // Сохраняем текст в лог (лог у нас будет храниться в файлах) } } class Product { private $_logger; public function __construct() { $this->_logger = new Logger(); } /* Продать товар */ public function sale() { // … продаем товар // Записываем дату продажи в лог $this->_logger->log('Sale time: '. time()); } }
  • 19. Лог продаж в файлах?! Это же отстой! Изменение требований: лог надо хранить в БД class DBLogger { public function log($text) { // Сохраняем что-то в лог (лог у нас будет храниться в БД) } } class Product { private $_logger; public function __construct() { // Меняем класс Product, чтоб поменять логер (помните про SRP?) $this->_logger = new DBLogger(); } /* Продать товар*/ public function sale() { // Продаем товар // ... // Записываем дату продажи в лог $this->_logger->log('Sale time: '. time()); } }
  • 20. Готовимся к борьбе с изменениями требований (а не к борьбе с менеджерами) interface ILogger { public function log($text); } class Logger implements ILogger { public function log($text) { // Сохраняем что-то в лог (лог у нас будет храниться в файлах) } } class DBLogger implements ILogger { public function log($text) { // Сохраняем что-то в лог (лог у нас будет храниться в БД) } } class Product { private $_logger; public function __construct(ILogger $logger) { $this->_logger = $logger; } /* Продать товар */ public function sale() { // Продаем товар // ... // Записываем дату продажи в лог $this->_logger->log('Sale time: '. time()); } }
  • 22. Принцип подстановки Лискоу Принцип подстановки Лискоу – дочерний класс не должен отрицать поведение родительского класса и должна быть возможность использовать дочерний класс везде, где использовался родительский класс.
  • 24. Принцип отделения интерфейса Принцип отделения интерфейса – клиенты не должны попадать в зависимость от методов, которыми они не пользуются Клиенты определяют, какие интерфейсы им нужны.
  • 25. Делаем трансформера (пока что все хорошо) interface IMegaTransformer { public function transformToCar(); public function transformToShip(); public function transformToPlane(); } class MegaTransformer implements IMegaTransformer { public function transformToCar() { echo 'Я преобразовался и стал спортивной машиной!'; } public function transformToShip() { echo 'Я преобразовался и стал сверхбыстрым катером!'; } public function transformToPlane() { echo 'Я преобразовался и стал истребителем!'; } }
  • 26. А теперь нужно сделать менее крутых трансформеров (в каждом из них теперь костыли) class TaxiTransformer implements IMegaTransformer { public function transformToCar() { echo 'Я преобразовался и стал такси!'; } public function transformToShip() { throw new Exception('Данная трансформация не поддерживается'); } public function transformToPlane() { throw new Exception('Данная трансформация не поддерживается'); } } class StelthTransformer implements IMegaTransformer { public function transformToCar() { throw new Exception('Данная трансформация не поддерживается'); } public function transformToShip() { throw new Exception('Данная трансформация не поддерживается'); } public function transformToPlane() { echo 'Я преобразовался и стал стелсом!'; } } class IcebreakerTransformer implements IMegaTransformer { public function transformToCar() { throw new Exception('Данная трансформация не поддерживается'); } public function transformToShip() { echo 'Я преобразовался и стал ледоколом!'; } public function transformToPlane() { throw new Exception('Данная трансформация не поддерживается'); } }
  • 27. Лучше иметь такой набор интерфейсов interface ICarTrsansformer { public function transformToCar(); } interface IShipTrsansformer { public function transformToShip(); } interface IPlaneTrsansformer { public function transformToPlane(); }
  • 28. И такой набор классов class MegaTransformer implements ICarTransformer, IShipTransformer, IPlaneTransformer { public function transformToCar() { echo 'Я преобразовался и стал спортивной машиной!'; } public function transformToShip() { echo 'Я преобразовался и стал сверхбыстрым катером!'; } public function transformToPlane() { echo 'Я преобразовался и стал истребителем!'; } } class TaxiTransformer implements ICarTransformer { public function transformToCar() { echo 'Я преобразовался и стал такси!'; } } class StelthTransformer implements IPlaneTransformer { public function transformToPlane() { echo 'Я преобразовался и стал стелсом!'; } } class IcebreakerTransformer implements IShipTransformer { public function transformToShip() { echo 'Я преобразовался и стал ледоколом!'; } }
  • 30. Принцип инверсии зависимостей Самый важный принцип IMHO Форма IoC (Inversion of Control) Принцип инверсии зависимостей – зависимости внутри системы стоятся на основе абстракций. Модули верхнего уровня не зависят от модулей нижнего уровня. Абстракции не зависят от подробностей. На практике помогает избежать циклов в схеме зависимостей.
  • 31. Анатомия зависимостей (X зависит от Y + циклическая зависимость) Зависимости бывают: - Прямые; - Транзитивные; - Циклические.
  • 32. Зависимость модуля верхннго уровня от модулей нижнего уровня class siteMapBuilder { private $_dataStorage; private $_webGrabber; public function __construct() { $this->_dataStorage = new FileDataStorage(); $this->_webGrabber = new CurlWebGrabber(); } // ... Здесь какие-то методы для построения карты сайта }
  • 33. Освобождаем SiteMapBuilder от зависимостей (инвертируем зависимости) class siteMapBuilder { private $_dataStorage; private $_webGrabber; public function __construct(WebGrabber $dataStorage, DataStorage $webGrabber) { $this->_dataStorage = $dataStorage; $this->_webGrabber = $webGrabber; } // ... Здесь какие-то методы для построения карты сайта }
  • 34. Разделение архитектуры по слоям «... любые хорошо структурированные объектно-ориентированные архитектуры имеют четко определенные слои, каждый из которых поддерживает некоторый компактный набор служб с помошью хорошо определенного и контролируемого интерфейса» Г. Буч
  • 35. Усовершенствованное разделение архитектуры по слоям Здесь мы избавились не только от транзитивной зависимости между TopLayer и DeepLayer, но и от прямых зависимостей
  • 36. Как оценивать рефакторинг Рефакторинг, по сути дела – улучшение архитектуры, и в оптимальном варианте хорошо бы понимать, куда идет движение. Различных качественных и количественных характеристик существует очень много, нужно подбирать под себя. Распространенные качественные: Жесткость, хрупкость, монолитность Количественные: Связанность (coupling, нужно стремиться к низкой связанности); Связность или сцепление (cohesion, тут все наоборот); Метрики от Роберта Мартина Процент покрытия тестами Over 9000 Эволюционным подходом можно подобрать оптимальные (у нас пока в процессе)
  • 37. А как же паттерны? Паттерны – не панацея, у каждого есть свои плюсы и минусы Системный подход при принятии решения о применении паттерна какие из выбранных показателей архитектуры мы улучшаем и ухудшаем? => какие принципы здесь следует применить? => выбор паттерна из подходящих, либо отказ от него. Пример в данном участке кода очень высокая связанность, в одном классе куча зависимостей от других. => здесь нарушены: принцип единой ответствености, принцип инверсии зависимостей, принцип открытия/закрытия => условный блок с выбором, какой из трех классов для работы БД использовать, заменяем паттерном Стратегия. и т.д.
  • 38. Все! Спасибо за внимание! Подборка ссылок и т.д.: https://docs.google.com/document/d/1mUigVpqtQ-ZtQfN5fW1qOzQsoKRsf3t0xyz9At8dHLA/edit Вопросы? Ершов Александр Skype: alex.cord E-mail: ershoff@lightsoft.ru