├── Replace_Array_with_Object.py ├── Preserve_Whole_Object.py ├── Replace_Array_with_Object.js ├── Preserve_Whole_Object.js ├── Split_Temporary_Variable.py ├── Encapsulate_Field.js ├── Remove Middle Man (Удаление посредника).md ├── Remove_ Assignments_Parameters.py ├── Replace_Magic_Number_with_Symbolic_Constant.py ├── Encapsulate_Field.java ├── Split_Temporary_Variable.js ├── Consolidate_Duplicate_Conditional_Fragments.py ├── Replace_Constructor_with_Factory_Method.java ├── Replace_Constructor_with_Factory_Method.js ├── Replace_Magic_Number_with_Symbolic_Constant.js ├── Replace_Parameter_with_Explicit_Methods.py ├── Consolidate_Conditional_Expression.py ├── Replace_Parameter_with_Method_Call.py ├── Decompose_Conditional.py ├── Remove_ Assignments_Parameters.js ├── Consolidate_Duplicate_Conditional_Fragments.js ├── Replace_Error_Code_with_Exception.py ├── Replace_Exception_with_Test.py ├── Pull_Up_Constructor_Body.py ├── Replace_Parameter_with_Method_Call.js ├── Inline_Method.py ├── Replace_Parameter_with_Explicit_Methods.js ├── Decompose_Conditional.js ├── Pull_Up_Constructor_Body.js ├── Replace_Exception_with_Test.js ├── Replace_Error_Code_with_Exception.js ├── Consolidate_Conditional_Expression.js ├── Extract_Method.py ├── Inline_Method.js ├── Self_Encapsulate_Field.js ├── Self_Encapsulate_Field.java ├── Introduce_Foreign_Method.py ├── Replace_Temp_with_Query.py ├── Extract_Method.js ├── Introduce_Assertion.py ├── Remove_Control_Flag.py ├── Introduce_Null_Object.py ├── Introduce_Foreign_Method.js ├── Replace_Temp_with_Query.js ├── Substitute_Algorithm.py ├── README.md ├── Introduce_Null_Object.js ├── Extract_Variable.py ├── Introduce_Assertion.js ├── Replace_Method_with_Method_Object.py ├── Extract_Variable.js ├── Replace_Method_with_Method_Object.js ├── Replace_Nested_Conditional_with_Guard_Clauses.py ├── Substitute_Algorithm.js ├── Remove_Control_Flag.js ├── Remove Setting Method (Удаление сеттера).md ├── Replace_Nested_Conditional_with_Guard_Clauses.js ├── Replace Type Code with Subclasses (Замена кодирования типа подклассами).md ├── Lazy Class (Ленивый класс).md ├── Replace_Conditional_with_Polymorphism.py ├── Pull Up Field (Подъём поля).md ├── Push Down Field (Спуск поля).md ├── Middle Man (Посредник).md ├── Parallel Inheritance Hierarchies.md ├── Replace_Conditional_with_Polymorphism.js ├── Inline_Method (Встраивание метода).md ├── Incomplete Library Class (Неполнота библиотечного класса).md ├── Push Down Method (Спуск метода).md ├── Dead Code (Мёртвый код).md ├── Rename Method (Переименование метода).md ├── Refused Bequest (Отказ от наследства).md ├── Inline Temp (Встраивание переменной).md ├── Hide Method (Сокрытие метода).md ├── Temporary Field (Временное поле).md ├── Inline Class (Встраивание класса).md ├── Move Field (Перемещение поля).md ├── Decompose Conditional (Разбиение условного оператора).md ├── Shotgun Surgery (Стрельба дробью).md ├── Message Chains (Цепочка вызовов).md ├── Extract_Variable (Извлечение переменной).md ├── Consolidate_Duplicate_Conditional_Fragments (Объединение дублирующихся фрагментов в условных операторах).md ├── Replace_Parameter_with_Explicit_Methods (Замена параметра набором специализированных методов).md ├── Alternative Classes with different Interfaces.md ├── Parameterize Method (Параметризация метода).md ├── Replace_Exception_with_Test (Замена исключения проверкой условия).md ├── Divergent Change (Расходящиеся модификации).md ├── Extract Superclass (Извлечение суперкласса).md ├── Self_Encapsulate_Field (Самоинкапсуляция поля).md ├── Remove_Control_Flag (Удаление управляющего флага).md ├── Pull Up Method (Подъём метода).md ├── Replace Delegation with Inheritance (Замена делегирования наследованием).md ├── Remove Parameter (Удаление параметра).md ├── Hide Delegate (Сокрытие делегирования).md ├── Introduce_Null_Object (Введение Null-объекта).md ├── Preserve_Whole_Object (Передача всего объекта).md ├── Change Unidirectional Association to Bidirectional (Замена однонаправленной связи двунаправленной).md ├── Inappropriate Intimacy (Неуместная близость).md ├── Replace_Array_with_Object (Замена поля-массива объектом).md ├── Substitute_Algorithm (Замена алгоритма).md ├── Data Clumps (Группы данных).md ├── Split_Temporary_Variable (Расщепление переменной).md ├── Pull_Up_Constructor_Body (Подъём тела конструктора).md ├── Speculative Generality (Теоретическая общность).md ├── Change Reference to Value (Замена ссылки значением).md ├── Replace_Parameter_with_Method_Call (Замена параметра вызовом метода).md ├── Extract Interface (Извлечение интерфейса).md ├── Remove_ Assignments to Parameters Удаление присваиваний.md ├── Comments.md ├── Feature Envy (Завистливые функции).md ├── Consolidate_Conditional_Expression (Объединение условных операторов).md ├── Introduce Parameter Object (Замена параметров объектом).md ├── Replace Inheritance with Delegation (Замена наследования делегированием).md ├── Large Class (Большой класс).md ├── Replace_Nested_Conditional_with_Guard_Clauses (Замена вложенных условных операторов граничным оператором).md ├── Change Value to Reference (Замена значения ссылкой).md ├── Replace_Error_Code_with_Exception (Замена кода ошибки исключением).md ├── Collapse Hierarchy (Свёртывание иерархии).md ├── Add Parameter (Добавление параметра).md ├── Replace Subclass with Fields (Замена подкласса полями).md ├── Data Class (Класс данных).md ├── Form Template Method (Создание шаблонного метода).md ├── Move Method (Перемещение метода).md ├── Separate Query from Modifier (Разделение запроса и модификатора).md ├── Replace_Constructor_with_Factory_Method (Замена конструктора фабричным методом).md ├── Encapsulate_Field (Инкапсуляция поля).md ├── Replace_Method_with_Method Object (Замена метода объектом методов).md ├── Introduce Local Extension (Введение локального расширения).md ├── Extract_Method (Извлечение метода).md ├── Long Parameter List.md ├── Change Bidirectional Association to Unidirectional (Замена двунаправленной связи однонаправленной).md ├── Introduce_Assertion (Введение проверки утверждения).md ├── Switch Statements.md ├── Introduce_Foreign_Method (Введение внешнего метода).md ├── Long Method.md ├── Extract Class (Извлечение класса).md ├── Replace_Temp_with_Query (Замена переменной вызовом метода).md ├── Replace Data Value with Object (Замена простого поля объектом).md ├── Encapsulate Collection (Инкапсуляция коллекции).md ├── Extract Subclass (Извлечение подкласса).md ├── Replace_Conditional_with_Polymorphism (Замена условного оператора полиморфизмом).md ├── Duplicate Observed Data (Дублирование видимых данных).md ├── Replace_Magic_Number_with_Symbolic_Constant (Замена магического числа символьной константой).md ├── Primitive Obsession (Одержимость элементарными типами).md ├── Replace Type Code with State (Strategy) (Замена кодирования типа состоянием (стратегией)).md ├── Replace Type Code with Class (Замена кодирования типа классом).md └── Dublicated Code.md /Replace_Array_with_Object.py: -------------------------------------------------------------------------------- 1 | # After 2 | row = [None * 2] 3 | row[0] = "Liverpool" 4 | row[1] = "15" 5 | 6 | # Refactoring 7 | row = Performance(); 8 | row.setName("Liverpool") 9 | row.setWins("15") 10 | -------------------------------------------------------------------------------- /Preserve_Whole_Object.py: -------------------------------------------------------------------------------- 1 | # After 2 | low = daysTempRange().getLow() 3 | high = daysTempRange().getHigh() 4 | withinPlan = plan.withinRange(low, high) 5 | 6 | # Refactoring 7 | withinPlan = plan.withinRange(daysTempRange()) 8 | -------------------------------------------------------------------------------- /Replace_Array_with_Object.js: -------------------------------------------------------------------------------- 1 | // After 2 | var row = [None * 2]; 3 | var row[0] = "Liverpool"; 4 | var row[1] = "15"; 5 | 6 | // Refactoring 7 | var row = Performance(); 8 | row.setName("Liverpool"); 9 | row.setWins("15"); 10 | -------------------------------------------------------------------------------- /Preserve_Whole_Object.js: -------------------------------------------------------------------------------- 1 | // After 2 | var low = daysTempRange().getLow(); 3 | var high = daysTempRange().getHigh(); 4 | var withinPlan = plan.withinRange(low, high); 5 | 6 | // Refactoring 7 | var withinPlan = plan.withinRange(daysTempRange()); 8 | -------------------------------------------------------------------------------- /Split_Temporary_Variable.py: -------------------------------------------------------------------------------- 1 | # After 2 | temp = 2 * (height + width) 3 | print(temp) 4 | temp = height * width 5 | print(temp) 6 | 7 | # Refactoring 8 | perimeter = 2 * (height + width) 9 | print(perimeter); 10 | area = height * width 11 | print(area) 12 | -------------------------------------------------------------------------------- /Encapsulate_Field.js: -------------------------------------------------------------------------------- 1 | // After 2 | function Person { 3 | name; 4 | } 5 | 6 | // Refactoring 7 | function Person { 8 | var name; 9 | 10 | function getName() { 11 | return name; 12 | } 13 | 14 | function setName(arg) { 15 | name = arg; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Remove Middle Man (Удаление посредника).md: -------------------------------------------------------------------------------- 1 | Удаление посредника 2 | 3 | Проблема 4 | 5 | Класс имеет слишком много методов, которые просто делегируют работу другим объектам. 6 | 7 | Решение 8 | 9 | Удалите эти методы и заставьте клиента вызывать конечные методы напрямую. 10 | -------------------------------------------------------------------------------- /Remove_ Assignments_Parameters.py: -------------------------------------------------------------------------------- 1 | # Before 2 | def discount(inputVal, quantity): 3 | if inputVal > 50: 4 | inputVal -= 2 5 | 6 | # After 7 | def discount(inputVal, quantity): 8 | result = inputVal 9 | if inputVal > 50: 10 | result -= 2 11 | -------------------------------------------------------------------------------- /Replace_Magic_Number_with_Symbolic_Constant.py: -------------------------------------------------------------------------------- 1 | # After 2 | def potentialEnergy(mass, height): 3 | return mass * height * 9.81 4 | 5 | # Refactoring 6 | GRAVITATIONAL_CONSTANT = 9.81 7 | 8 | def potentialEnergy(mass, height): 9 | return mass * height * GRAVITATIONAL_CONSTANT 10 | -------------------------------------------------------------------------------- /Encapsulate_Field.java: -------------------------------------------------------------------------------- 1 | // After 2 | class Person { 3 | public String name; 4 | } 5 | 6 | // Refactoring 7 | class Person { 8 | private String name; 9 | 10 | public String getName() { 11 | return name; 12 | } 13 | public void setName(String arg) { 14 | name = arg; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Split_Temporary_Variable.js: -------------------------------------------------------------------------------- 1 | // After 2 | var temp = 2 * (height + width); 3 | console.log(temp); 4 | temp = height * width; 5 | console.log(temp); 6 | 7 | // Refactoring 8 | var perimeter = 2 * (height + width); 9 | console.log(perimeter); 10 | var area = height * width; 11 | console.log(area); 12 | -------------------------------------------------------------------------------- /Consolidate_Duplicate_Conditional_Fragments.py: -------------------------------------------------------------------------------- 1 | # After 2 | if isSpecialDeal(): 3 | total = price * 0.95 4 | send() 5 | else: 6 | total = price * 0.98 7 | send() 8 | 9 | # Refactoring 10 | if isSpecialDeal(): 11 | total = price * 0.95 12 | else: 13 | total = price * 0.98 14 | send() 15 | -------------------------------------------------------------------------------- /Replace_Constructor_with_Factory_Method.java: -------------------------------------------------------------------------------- 1 | # After 2 | class Employee { 3 | Employee(int type) { 4 | this.type = type; 5 | } 6 | } 7 | 8 | # Refactoring 9 | class Employee { 10 | static Employee create(int type) { 11 | employee = new Employee(type); 12 | return employee; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Replace_Constructor_with_Factory_Method.js: -------------------------------------------------------------------------------- 1 | // After 2 | class Employee { 3 | function employee(type) { 4 | this.type = type; 5 | } 6 | } 7 | 8 | // Refactoring 9 | class Employee { 10 | function Employee(type) { 11 | var employee = new Employee(type); 12 | return employee; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Replace_Magic_Number_with_Symbolic_Constant.js: -------------------------------------------------------------------------------- 1 | // After 2 | function potentialEnergy(mass, height) { 3 | return mass * height * 9.81; 4 | } 5 | 6 | // Refactoring 7 | var GRAVITATIONAL_CONSTANT = 9.81; 8 | 9 | function potentialEnergy(mass, height) { 10 | return mass * height * GRAVITATIONAL_CONSTANT; 11 | } 12 | -------------------------------------------------------------------------------- /Replace_Parameter_with_Explicit_Methods.py: -------------------------------------------------------------------------------- 1 | # After 2 | def output(self, type): 3 | if name == "banner" 4 | # Print the banner. 5 | 6 | if name == "info" 7 | 8 | # Refactoring 9 | def outputBanner(self): 10 | # Print the banner. 11 | 12 | def outputInfo(self): 13 | # Print the info. 14 | -------------------------------------------------------------------------------- /Consolidate_Conditional_Expression.py: -------------------------------------------------------------------------------- 1 | # After 2 | def disabilityAmount(): 3 | if seniority < 2: 4 | return 0 5 | if monthsDisabled > 12: 6 | return 0 7 | if isPartTime: 8 | return 0 9 | 10 | # Refactoring 11 | def disabilityAmount(): 12 | if isNotEligableForDisability(): 13 | return 0 14 | -------------------------------------------------------------------------------- /Replace_Parameter_with_Method_Call.py: -------------------------------------------------------------------------------- 1 | # After 2 | basePrice = quantity * itemPrice 3 | seasonalDiscount = store.getSeasonalDiscount() 4 | fees = store.getFees() 5 | finalPrice = discountedPrice(basePrice, seasonalDiscount, fees) 6 | 7 | # Refactoring 8 | basePrice = quantity * itemPrice 9 | finalPrice = discountedPrice(basePrice, store) 10 | -------------------------------------------------------------------------------- /Decompose_Conditional.py: -------------------------------------------------------------------------------- 1 | # After 2 | if date.before(SUMMER_START) or date.after(SUMMER_END): 3 | charge = quantity * winterRate + winterServiceCharge 4 | else: 5 | charge = quantity * summerRate 6 | 7 | # Refactoring 8 | if notSummer(date): 9 | charge = winterCharge(quantity) 10 | else: 11 | charge = summerCharge(quantity) 12 | -------------------------------------------------------------------------------- /Remove_ Assignments_Parameters.js: -------------------------------------------------------------------------------- 1 | // Before 2 | function discount(inputVal, quantity) { 3 | if (inputVal > 50) { 4 | inputVal -= 2; 5 | } 6 | } 7 | 8 | // After 9 | function discount(inputVal, quantity) { 10 | var result = inputVal; 11 | if (inputVal > 50) { 12 | result -= 2; 13 | } 14 | } 15 | 16 | -------------------------------------------------------------------------------- /Consolidate_Duplicate_Conditional_Fragments.js: -------------------------------------------------------------------------------- 1 | // After 2 | if (isSpecialDeal()) { 3 | total = price * 0.95; 4 | send(); 5 | } else { 6 | total = price * 0.98; 7 | send(); 8 | } 9 | 10 | // Refactoring 11 | var total; 12 | if (isSpecialDeal()) { 13 | total = price * 0.95; 14 | } else { 15 | total = price * 0.98; 16 | } 17 | send(); 18 | -------------------------------------------------------------------------------- /Replace_Error_Code_with_Exception.py: -------------------------------------------------------------------------------- 1 | # After 2 | def withdraw(self, amount): 3 | if amount > self.balance: 4 | return -1 5 | else: 6 | self.balance -= amount 7 | return 0 8 | 9 | # Refactoring 10 | def withdraw(self, amount): 11 | if amount > self.balance: 12 | raize BalanceException() 13 | self.balance -= amount 14 | -------------------------------------------------------------------------------- /Replace_Exception_with_Test.py: -------------------------------------------------------------------------------- 1 | # After 2 | def getValueForPeriod(periodNumber): 3 | try: 4 | return values[periodNumber] 5 | except IndexError: 6 | return 0 7 | 8 | # Refactoring 9 | def getValueForPeriod(self, periodNumber): 10 | if periodNumber >= len(self.values): 11 | return 0 12 | return self.values[periodNumber] 13 | -------------------------------------------------------------------------------- /Pull_Up_Constructor_Body.py: -------------------------------------------------------------------------------- 1 | # After 2 | class Manager(Employee): 3 | def __init__(self, name, id, grade): 4 | self.name = name 5 | self.id = id 6 | self.grade = grade 7 | 8 | # Refactoring 9 | class Manager(Employee): 10 | def __init__(self, name, id, grade): 11 | Employee.__init__(name, id) 12 | self.grade = grade 13 | -------------------------------------------------------------------------------- /Replace_Parameter_with_Method_Call.js: -------------------------------------------------------------------------------- 1 | // After 2 | var basePrice = quantity * itemPrice; 3 | var seasonalDiscount = store.getSeasonalDiscount(); 4 | var fees = store.getFees(); 5 | var finalPrice = discountedPrice(basePrice, seasonalDiscount, fees); 6 | 7 | // Refactoring 8 | var basePrice = quantity * itemPrice; 9 | var finalPrice = discountedPrice(basePrice, store); 10 | -------------------------------------------------------------------------------- /Inline_Method.py: -------------------------------------------------------------------------------- 1 | # After 2 | class PizzaDelivery: 3 | def getRating(self): 4 | return 2 if self.moreThanFiveLateDeliveries() else 1 5 | 6 | def moreThanFiveLateDeliveries(self): 7 | return self.numberOfLateDeliveries > 5 8 | 9 | # Refactoring 10 | class PizzaDelivery: 11 | def getRating(self): 12 | return 2 if self.numberOfLateDeliveries > 5 else 1 13 | -------------------------------------------------------------------------------- /Replace_Parameter_with_Explicit_Methods.js: -------------------------------------------------------------------------------- 1 | // After 2 | function output(self, type) { 3 | if (name == "banner") { 4 | // Print the banner. 5 | } 6 | 7 | if (name == "info") { 8 | 9 | } 10 | } 11 | 12 | // Refactoring 13 | 14 | // Print the banner. 15 | function outputBanner(self) {} 16 | 17 | // Print the info. 18 | function outputInfo(self) {} 19 | -------------------------------------------------------------------------------- /Decompose_Conditional.js: -------------------------------------------------------------------------------- 1 | // After 2 | if (date.before(SUMMER_START) || date.after(SUMMER_END)) { 3 | charge = quantity * winterRate + winterServiceCharge; 4 | } else { 5 | charge = quantity * summerRate; 6 | } 7 | 8 | // Refactoring 9 | var charge; 10 | if (notSummer(date)) { 11 | charge = winterCharge(quantity); 12 | } else { 13 | charge = summerCharge(quantity); 14 | } 15 | -------------------------------------------------------------------------------- /Pull_Up_Constructor_Body.js: -------------------------------------------------------------------------------- 1 | // After 2 | class Manager(Employee) { 3 | function __init__(self, name, id, grade) { 4 | self.name = name; 5 | self.id = id; 6 | self.grade = grade; 7 | } 8 | } 9 | 10 | // Refactoring 11 | class Manager(Employee) { 12 | function __init__(self, name, id, grade) { 13 | Employee.__init__(name, id); 14 | self.grade = grade; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Replace_Exception_with_Test.js: -------------------------------------------------------------------------------- 1 | //After 2 | function getValueForPeriod(periodNumber) { 3 | try { 4 | return values[periodNumber]; 5 | } catch(IndexError) { 6 | return 0; 7 | } 8 | } 9 | 10 | // Refactoring 11 | function getValueForPeriod(self, periodNumber) { 12 | if (periodNumber >= self.values.length) { 13 | return 0; 14 | } 15 | return self.values[periodNumber]; 16 | } 17 | -------------------------------------------------------------------------------- /Replace_Error_Code_with_Exception.js: -------------------------------------------------------------------------------- 1 | // After 2 | function withdraw(self, amount) { 3 | if (amount > self.balance) { 4 | return -1; 5 | } else { 6 | self.balance -= amount; 7 | } 8 | return 0; 9 | } 10 | 11 | // Refactoring 12 | function withdraw(self, amount) { 13 | if (amount > self.balance) { 14 | throw BalanceException(); 15 | } 16 | self.balance -= amount; 17 | } 18 | -------------------------------------------------------------------------------- /Consolidate_Conditional_Expression.js: -------------------------------------------------------------------------------- 1 | // After 2 | function disabilityAmount() { 3 | if (seniority < 2) { 4 | return 0; 5 | } 6 | if (monthsDisabled > 12) { 7 | return 0; 8 | } 9 | if (isPartTime) { 10 | return 0; 11 | } 12 | } 13 | 14 | // Refactoring 15 | function disabilityAmount() { 16 | if (isNotEligableForDisability()) { 17 | return 0; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Extract_Method.py: -------------------------------------------------------------------------------- 1 | # After 2 | def printOwing(self): 3 | self.printBanner() 4 | 5 | # print details 6 | print("name:", self.name) 7 | print("amount:", self.getOutstanding()) 8 | 9 | # Refactoring 10 | def printOwing(self): 11 | self.printBanner() 12 | self.printDetails(self.getOutstanding()) 13 | 14 | def printDetails(self, outstanding): 15 | print("name:", self.name) 16 | print("amount:", outstanding) 17 | -------------------------------------------------------------------------------- /Inline_Method.js: -------------------------------------------------------------------------------- 1 | // After 2 | class PizzaDelivery { 3 | function getRating(self) { 4 | return self.moreThanFiveLateDeliveries() ? 2 : 1; 5 | } 6 | 7 | function moreThanFiveLateDeliveries(self) { 8 | return self.numberOfLateDeliveries > 5; 9 | } 10 | } 11 | 12 | // Refactoring 13 | class PizzaDelivery { 14 | function getRating(self) { 15 | return self.numberOfLateDeliveries > 5 ? 2 : 1; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Self_Encapsulate_Field.js: -------------------------------------------------------------------------------- 1 | // After 2 | class Range { 3 | var low, high; 4 | function includes(arg) { 5 | return arg >= low && arg <= high; 6 | } 7 | } 8 | 9 | // Refactoring 10 | class Range { 11 | var low, high; 12 | function includes(arg) { 13 | return arg >= getLow() && arg <= getHigh(); 14 | } 15 | function getLow() { 16 | return low; 17 | } 18 | function getHigh() { 19 | return high; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Self_Encapsulate_Field.java: -------------------------------------------------------------------------------- 1 | # After 2 | class Range { 3 | private int low, high; 4 | boolean includes(int arg) { 5 | return arg >= low && arg <= high; 6 | } 7 | } 8 | 9 | # Refactoring 10 | class Range { 11 | private int low, high; 12 | boolean includes(int arg) { 13 | return arg >= getLow() && arg <= getHigh(); 14 | } 15 | int getLow() { 16 | return low; 17 | } 18 | int getHigh() { 19 | return high; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Introduce_Foreign_Method.py: -------------------------------------------------------------------------------- 1 | # After 2 | class Report: 3 | def sendReport(self): 4 | nextDay = Date(self.previousEnd.getYear(), 5 | self.previousEnd.getMonth(), self.previousEnd.getDate() + 1); 6 | 7 | # Refactoring 8 | class Report: 9 | def sendReport(self): 10 | nextDay = self._nextDay(self.previousEnd) 11 | 12 | def _nextDay(self, arg): 13 | return Date(arg.getYear(), arg.getMonth(), arg.getDate() + 1) 14 | -------------------------------------------------------------------------------- /Replace_Temp_with_Query.py: -------------------------------------------------------------------------------- 1 | # After 2 | def calculateTotal(): 3 | basePrice = quantity * itemPrice 4 | if basePrice > 1000: 5 | return basePrice * 0.95 6 | else: 7 | return basePrice * 0.98 8 | 9 | # Refactoring 10 | def calculateTotal(): 11 | if basePrice() > 1000: 12 | return basePrice() * 0.95 13 | else: 14 | return basePrice() * 0.98 15 | 16 | def basePrice(): 17 | return quantity * itemPrice 18 | -------------------------------------------------------------------------------- /Extract_Method.js: -------------------------------------------------------------------------------- 1 | // After 2 | function printOwing(self) { 3 | self.printBanner(); 4 | 5 | // print details 6 | console.log("name: ", self.name); 7 | console.log("amount: ", self.getOutstanding()); 8 | } 9 | 10 | // Refactoring 11 | function printOwing(self) { 12 | self.printBanner(); 13 | self.printDetails(self.getOutstanding()); 14 | } 15 | 16 | function printDetails(self, outstanding) { 17 | console.log("name: ", self.name); 18 | console.log("amount: ", outstanding); 19 | } 20 | -------------------------------------------------------------------------------- /Introduce_Assertion.py: -------------------------------------------------------------------------------- 1 | # After 2 | def getExpenseLimit(self): 3 | # should have either expense limit or a primary project 4 | return self.expenseLimit if self.expenseLimit != NULL_EXPENSE else self.primaryProject.getMemberExpenseLimit() 5 | 6 | # Refactoring 7 | def getExpenseLimit(self): 8 | assert (self.expenseLimit != NULL_EXPENSE) or (self.primaryProject != None) 9 | return self.expenseLimit if (self.expenseLimit != NULL_EXPENSE) else self.primaryProject.getMemberExpenseLimit(); 10 | -------------------------------------------------------------------------------- /Remove_Control_Flag.py: -------------------------------------------------------------------------------- 1 | # Before refactoring 2 | def checkPeople(people): 3 | found = False; 4 | for i in people: 5 | if not found: 6 | if i == "Mark": 7 | found = True; 8 | sendResult(i); 9 | if i == "John": 10 | found = True; 11 | sendResult(i); 12 | 13 | # After refactoring 14 | def checkPeople(people): 15 | for i in people: 16 | if i == "Mark": 17 | sendResult(i); 18 | break; 19 | if i == "John": 20 | sendResult(i); 21 | break 22 | -------------------------------------------------------------------------------- /Introduce_Null_Object.py: -------------------------------------------------------------------------------- 1 | # After 2 | if customer == None: 3 | plan = BillingPlan.basic() 4 | else: 5 | plan = customer.getPlan() 6 | 7 | # Refactoring 8 | class NullCustomer(Customer): 9 | def isNull(self): 10 | return True 11 | def getPlan(self): 12 | return self.NullPlan() 13 | 14 | # Replace null values with Null-object. 15 | customer = order.customer if order.customer != None else NullCustomer() 16 | 17 | # Use Null-object as if it's normal subclass. 18 | plan = customer.getPlan() 19 | -------------------------------------------------------------------------------- /Introduce_Foreign_Method.js: -------------------------------------------------------------------------------- 1 | // After 2 | class Report { 3 | function sendReport(self) { 4 | var nextDay = Date(self.previousEnd.getYear(), self.previousEnd.getMonth(), self.previousEnd.getDate() + 1); 5 | } 6 | } 7 | 8 | // Refactoring 9 | class Report { 10 | function sendReport(self) { 11 | var nextDay = self._nextDay(self.previousEnd); 12 | } 13 | 14 | function _nextDay(self, arg) { 15 | return Date(arg.getYear(), arg.getMonth(), arg.getDate() + 1); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Replace_Temp_with_Query.js: -------------------------------------------------------------------------------- 1 | // After 2 | function calculateTotal() { 3 | var basePrice = quantity * itemPrice; 4 | if (basePrice > 1000) { 5 | return basePrice * 0.95; 6 | } else { 7 | return basePrice * 0.98; 8 | } 9 | } 10 | 11 | // Refactoring 12 | function calculateTotal() { 13 | if (basePrice() > 1000) { 14 | return basePrice() * 0.95; 15 | } else { 16 | return basePrice() * 0.98; 17 | } 18 | } 19 | 20 | function basePrice() { 21 | return quantity * itemPrice; 22 | } 23 | -------------------------------------------------------------------------------- /Substitute_Algorithm.py: -------------------------------------------------------------------------------- 1 | # After 2 | def foundPerson(people): 3 | for i in range(len(people)): 4 | if people[i] == "Don": 5 | return "Don" 6 | if people[i] == "John": 7 | return "John" 8 | if people[i] == "Kent": 9 | return "Kent" 10 | return "" 11 | 12 | # Refactoring 13 | def foundPerson(people): 14 | candidates = ["Don", "John", "Kent"] 15 | for i in range(len(people)): 16 | if people[i] in candidates: 17 | return people[i] 18 | return "" 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Рефакторинг — это контролируемый процесс улучшения вашего кода, без написания новой функциональности. Его следует проводить серией небольших изменений, каждое из которых делает существующий код чуть лучше, оставляя программу в рабочем состоянии. 2 | 3 | Чеклист правильно проведенного рефакторинга: 4 | 5 | 1. В процессе рефакторинга не создается новая функциональность. 6 | 2. Все существующие тесты должны успешно проходить. 7 | 3. Код должен стать чище. 8 | 9 | Список литературы: 10 | 11 | 1. "Рефакторинг" М. Фаулер 12 | 2. https://refactoring.guru/ru?-s 13 | -------------------------------------------------------------------------------- /Introduce_Null_Object.js: -------------------------------------------------------------------------------- 1 | // After 2 | if (customer === Null) { 3 | plan = BillingPlan.basic(); 4 | } else { 5 | plan = customer.getPlan(); 6 | } 7 | 8 | // Refactoring 9 | class NullCustomer(Customer) { 10 | function isNull(self) { 11 | return True; 12 | } 13 | function getPlan(self) { 14 | return self.NullPlan(); 15 | } 16 | } 17 | 18 | // Replace null values with Null-object. 19 | var customer = (order.customer !== Null) ? order.customer : NullCustomer(); 20 | 21 | // Use Null-object as if it's normal subclass. 22 | var plan = customer.getPlan() 23 | -------------------------------------------------------------------------------- /Extract_Variable.py: -------------------------------------------------------------------------------- 1 | # After 2 | def renderBanner(self): 3 | if (self.platform.toUpperCase().indexOf("MAC") > -1) and \ 4 | (self.browser.toUpperCase().indexOf("IE") > -1) and \ 5 | self.wasInitialized() and (self.resize > 0): 6 | # do something 7 | 8 | # Refactoring 9 | def renderBanner(self): 10 | isMacOs = self.platform.toUpperCase().indexOf("MAC") > -1 11 | isIE = self.browser.toUpperCase().indexOf("IE") > -1 12 | wasResized = self.resize > 0 13 | 14 | if isMacOs and isIE and self.wasInitialized() and wasResized: 15 | # do something 16 | -------------------------------------------------------------------------------- /Introduce_Assertion.js: -------------------------------------------------------------------------------- 1 | // After 2 | function getExpenseLimit(self) { 3 | // should have either expense limit or a primary project 4 | return (self.expenseLimit != NULL_EXPENSE) ? self.expenseLimit : self.primaryProject.getMemberExpenseLimit(); 5 | } 6 | 7 | // Refactoring 8 | function getExpenseLimit(self) { 9 | if ((self.expenseLimit != NULL_EXPENSE) || (self.primaryProject != None)) { 10 | throw new Error('Should have either expense limit or a primary project'); 11 | } 12 | return (self.expenseLimit != NULL_EXPENSE) ? self.expenseLimit : self.primaryProject.getMemberExpenseLimit(); 13 | } 14 | -------------------------------------------------------------------------------- /Replace_Method_with_Method_Object.py: -------------------------------------------------------------------------------- 1 | # After 2 | class Order: 3 | def price(self): 4 | primaryBasePrice = 0 5 | secondaryBasePrice = 0 6 | tertiaryBasePrice = 0 7 | # long computation. 8 | 9 | # Refactoring 10 | class Order: 11 | def price(self): 12 | return PriceCalculator(self).compute() 13 | 14 | class PriceCalculator: 15 | self._primaryBasePrice = 0 16 | self._secondaryBasePrice = 0 17 | self._tertiaryBasePrice = 0 18 | 19 | def __init__(self, order): 20 | # copy relevant information from order object. 21 | 22 | def compute(self): 23 | # long computation. 24 | 25 | -------------------------------------------------------------------------------- /Extract_Variable.js: -------------------------------------------------------------------------------- 1 | // After 2 | function renderBanner(self) { 3 | if ((navigator.platform.indexOf("Win") !== -1) && 4 | (navigator.userAgent.indexOf("MSIE") !== -1) && 5 | self.wasInitialized() && (self.resize > 0) ) { 6 | // do something 7 | } 8 | } 9 | 10 | // Refactoring 11 | function renderBanner(self) { 12 | var isWindowOs = navigator.platform.indexOf("Win") !== -1; 13 | var isIE = navigator.userAgent.indexOf("MSIE") !== -1; 14 | var wasResized = self.resize > 0; 15 | 16 | if (isMacOs && isIE && self.wasInitialized() && wasResized) { 17 | // do something 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Replace_Method_with_Method_Object.js: -------------------------------------------------------------------------------- 1 | // After 2 | class Order() { 3 | function price(self) { 4 | var primaryBasePrice = 0; 5 | var secondaryBasePrice = 0; 6 | var tertiaryBasePrice = 0; 7 | } 8 | } 9 | 10 | // Refactoring 11 | class Order() { 12 | function price(self) { 13 | return PriceCalculator(self).compute(); 14 | } 15 | } 16 | 17 | class PriceCalculator() { 18 | self._primaryBasePrice = 0 19 | self._secondaryBasePrice = 0 20 | self._tertiaryBasePrice = 0 21 | 22 | // copy relevant information from order object. 23 | function __init__(self, order) {} 24 | 25 | // long computation. 26 | function compute(self) {} 27 | } 28 | -------------------------------------------------------------------------------- /Replace_Nested_Conditional_with_Guard_Clauses.py: -------------------------------------------------------------------------------- 1 | # After 2 | def getPayAmount(self): 3 | if self.isDead: 4 | result = deadAmount() 5 | else: 6 | if self.isSeparated: 7 | result = separatedAmount() 8 | else: 9 | if self.isRetired: 10 | result = retiredAmount() 11 | else: 12 | result = normalPayAmount() 13 | return result 14 | 15 | # Refactoring 16 | def getPayAmount(self): 17 | if self.isDead: 18 | return deadAmount() 19 | if self.isSeparated: 20 | return separatedAmount() 21 | if self.isRetired: 22 | return retiredAmount() 23 | return normalPayAmount() 24 | -------------------------------------------------------------------------------- /Substitute_Algorithm.js: -------------------------------------------------------------------------------- 1 | // After 2 | function foundPerson(people) { 3 | people.forEach(function(element, i, array) { 4 | if (people[i] == "Don") { 5 | return "Don"; 6 | } 7 | if (people[i] == "John") { 8 | return "John"; 9 | } 10 | if (people[i] == "Kent") { 11 | return "Kent"; 12 | } 13 | }); 14 | return ""; 15 | } 16 | 17 | // Refactoring 18 | function foundPerson(people) { 19 | ["Don", "John", "Kent"].forEach(function(element, i, array) { 20 | people.forEach(function(name, y, people) { 21 | if (name == element) { 22 | return name; 23 | } 24 | }); 25 | }); 26 | return ""; 27 | } 28 | -------------------------------------------------------------------------------- /Remove_Control_Flag.js: -------------------------------------------------------------------------------- 1 | // Before refactoring 2 | function checkPeople(people) { 3 | var found = false; 4 | for (var i = 0; i < people.length; i++) { 5 | if (!found) { 6 | if (i == "Mark") { 7 | found = true; 8 | sendResult(people[i]); 9 | } 10 | if (i == "John") { 11 | found = true; 12 | sendResult(people[i]); 13 | } 14 | } 15 | } 16 | } 17 | 18 | // After refactoring 19 | function checkPeople(people) { 20 | for (var i = 0; i < people.length; i++) { 21 | if (i == "Mark") { 22 | sendResult(people[i]); 23 | break; 24 | } 25 | if (i == "John") { 26 | sendResult(people[i]); 27 | break; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Remove Setting Method (Удаление сеттера).md: -------------------------------------------------------------------------------- 1 | Удаление сеттера 2 | 3 | Проблема: Значение поля должно быть установлено только в момент создания и больше никогда не меняться. 4 | 5 | Решение: Удалите методы, устанавливающие значение этого поля. 6 | 7 | Причины рефакторинга: Вы хотите сделать значение поля неизменяемым. 8 | 9 | Порядок рефакторинга 10 | 11 | 1. Значение поля должно меняться только в конструкторе. Если конструктор не содержит параметра для установки значения, нужно его добавить. 12 | 2. Найдите все вызовы сеттера. 13 | 2.1 Если вызов сеттера стоит сразу после вызова конструктора текущего класса, переместите его аргумент в вызов конструктора и удалите сеттер. 14 | 2.2 Вызовы сеттера в конструкторе замените на прямой доступ к полю. 15 | 3. Удалите сеттер. 16 | -------------------------------------------------------------------------------- /Replace_Nested_Conditional_with_Guard_Clauses.js: -------------------------------------------------------------------------------- 1 | // After 2 | function getPayAmount(self) { 3 | var result; 4 | if (self.isDead) { 5 | result = deadAmount(); 6 | } else { 7 | if (self.isSeparated) { 8 | result = separatedAmount(); 9 | } else { 10 | if (self.isRetired) { 11 | result = retiredAmount(); 12 | } else { 13 | result = normalPayAmount(); 14 | } 15 | } 16 | } 17 | return result; 18 | } 19 | 20 | // Refactoring 21 | function getPayAmount(self) { 22 | if (self.isDead) { 23 | return deadAmount(); 24 | } 25 | if (self.isSeparated) { 26 | return separatedAmount(); 27 | } 28 | if (self.isRetired) { 29 | return retiredAmount(); 30 | } 31 | return normalPayAmount(); 32 | } 33 | -------------------------------------------------------------------------------- /Replace Type Code with Subclasses (Замена кодирования типа подклассами).md: -------------------------------------------------------------------------------- 1 | Замена кодирования типа подклассами 2 | 3 | Что такое кодирование типа? Это когда вместо отдельного типа данных вы имеете набор чисел или строк, который составляет список допустимых значений для какой-то сущности. Зачастую этим конкретным числам и строкам даются понятные имена с помощью констант, что и является причиной их широкого распространения. 4 | 5 | Проблема 6 | 7 | У вас есть закодированный тип, который непосредственно влияет на поведение программы (основываясь на значениях этого поля, в условных операторах выполняется различный код). 8 | 9 | Решение 10 | 11 | Для каждого значения закодированного типа, создайте подклассы. А затем, вынесите соответствующие поведения из исходного класса в эти подклассы. Управляющий код замените полиморфизмом. 12 | -------------------------------------------------------------------------------- /Lazy Class (Ленивый класс).md: -------------------------------------------------------------------------------- 1 | Ленивый класс 2 | 3 | Симптомы и признаки: На понимание и поддержку классов всегда требуются затраты времени и денег. А потому, если класс не делает достаточно много, чтобы уделять ему достаточно внимания, он должен быть уничтожен. 4 | 5 | Причины появления: 6 | 7 | 1. Это может произойти, если класс был задуман как полнофункциональный, но в результате рефакторинга ужался до неприличных размеров. 8 | 2. Либо класс создавался в расчёте на некие будущие разработки, до которых руки так и не дошли. 9 | 10 | Лечение: 11 | 12 | 1. Почти бесполезные компоненты должны быть подвергнуты встраиванию класса. 13 | 2. При наличии подклассов с недостаточными функциями попробуйте свёртывание иерархии. 14 | 15 | Выигрыш: 16 | 17 | 1. Уменьшение размера кода. 18 | 2. Упрощение поддержки. 19 | 20 | Не стоит трогать, когда Ленивый класс был создан для того, чтобы явно очертить какие-то намерения. В этом случае, стоит соблюдать баланс понятности кода и его простоты. 21 | -------------------------------------------------------------------------------- /Replace_Conditional_with_Polymorphism.py: -------------------------------------------------------------------------------- 1 | # After 2 | class Bird: 3 | def getSpeed(self): 4 | if self.type == EUROPEAN: 5 | return self.getBaseSpeed(); 6 | elif self.type == AFRICAN: 7 | return self.getBaseSpeed() - self.getLoadFactor() * self.numberOfCoconuts; 8 | elif self.type == NORWEGIAN_BLUE: 9 | return 0 if isNailed else self.getBaseSpeed(self.voltage) 10 | else: 11 | raise Exception("Should be unreachable") 12 | 13 | # Refactoring 14 | class Bird: 15 | def getSpeed(self): 16 | pass 17 | class European(Bird): 18 | def getSpeed(self): 19 | return self.getBaseSpeed() 20 | class African(Bird): 21 | def getSpeed(self): 22 | return self.getBaseSpeed() - self.getLoadFactor() * self.numberOfCoconuts 23 | class NorvegianBlue(Bird): 24 | def getSpeed(): 25 | return 0 if self.isNailed else self.getBaseSpeed(self.voltage) 26 | # Tests with 27 | speed = bird.getSpeed() 28 | -------------------------------------------------------------------------------- /Pull Up Field (Подъём поля).md: -------------------------------------------------------------------------------- 1 | Подъём поля 2 | 3 | Проблема: Два класса имеют одно и то же поле. 4 | 5 | Решение: Переместите поле в суперкласс, убрав его из подклассов. 6 | 7 | Причины рефакторинга: Подклассы развивались независимо друг от друга. Это привело к созданию одинаковых (или очень похожих) полей и методов. 8 | 9 | Достоинства 10 | 11 | 1. Убивает дублирование полей в подклассах. 12 | 2. Облегчает дальнейший перенос дублирующих методов из подклассов в суперкласс, если они есть. 13 | 14 | Порядок рефакторинга 15 | 16 | 1. Проверьте, что оба поля используются для одинаковых нужд в подклассах. 17 | 2. Если поля имеют разные названия, дайте им общее имя и замените все обращения к полям в существующем коде. 18 | 3. Создайте поле с таким же именем в суперклассе. Обратите внимание на то, что если поля были приватные (private), поле в суперклассе должно быть защищённым (protected). 19 | 4. Удалите поля из подклассов. 20 | 5. Возможно, имеет смысл использовать самоинкапсуляцию поля для нового поля, чтобы скрыть его за методами доступа. 21 | -------------------------------------------------------------------------------- /Push Down Field (Спуск поля).md: -------------------------------------------------------------------------------- 1 | Спуск поля 2 | 3 | Проблема: поле используется только в некоторых подклассах. 4 | 5 | Решение: переместите поле в эти подклассы. 6 | 7 | Причины рефакторинга: 8 | 9 | 1. Поле, которое планировали сделать универсальным для всех классов, по факту, используется только в некоторых подклассах. Такая ситуация может возникнуть, когда планируемые фичи так и не были реализованы. 10 | 2. Кроме того, такая ситуация может возникнуть после извлечения (или удаления) части функциональности из иерархии классов. 11 | 12 | Достоинства: 13 | 14 | 1. Улучшает связность внутри классов. Поле находится там, где оно реально используется. 15 | 2. При перемещении в несколько подклассов одновременно, появляется возможность развивать поля независимо друг от друга. Правда, такое действие создаёт дублирование кода, поэтому стоит спускать поля, только если вы действительно намерены использовать их по-разному. 16 | 17 | Порядок рефакторинга: 18 | 19 | 1. Объявите поле во всех необходимых подклассах. 20 | 2. Удалите поле из суперкласса. 21 | -------------------------------------------------------------------------------- /Middle Man (Посредник).md: -------------------------------------------------------------------------------- 1 | Посредник 2 | 3 | Симптомы и признаки: Если класс выполняет одно действие – делегирует работу другому классу – стоит задуматься, зачем он вообще существует. 4 | 5 | Причины появления: Данный запах может быть результатом фанатичной борьбы с цепочками вызовов. Кроме того, бывает так, что вся полезная нагрузка класса постепенно перемещается в другие классы, в результате кроме делегирующих методов в нем ничего не остается. 6 | 7 | Лечение: Если большую часть методов класс делегирует другому классу, нужно воспользоваться удалением посредника. 8 | 9 | Выигрыш: Уменьшение размера кода. 10 | 11 | Не удаляйте посредников, которые были созданы осознанно: 12 | 13 | 1. Посредник мог быть введён для избавления от нежелательной зависимости между классами. 14 | 2. Некоторые паттерны проектирования намеренно создают посредников (например, Заместитель или Декоратор). 15 | -------------------------------------------------------------------------------- /Parallel Inheritance Hierarchies.md: -------------------------------------------------------------------------------- 1 | Параллельные иерархии наследования 2 | 3 | Симптомы и признаки: Всякий раз при создании подкласса какого-то класса приходится создавать ещё один подкласс для другого класса. 4 | 5 | Причины появления: Пока иерархия была небольшая, всё было хорошо. Но с появлением новых классов вносить изменения становилось всё сложнее и сложнее. 6 | 7 | Лечение: Вы можете попытаться устранить дублирования паралельных классов в два этапа. Во-первых, нужно заставить экземпляры одной иерархии ссылаться на экземпляры другой иерархии. Затем следует убрать иерархию в ссылающемся классе c помощью перемещения метода и перемещения поля. 8 | 9 | Выигрыш 10 | 11 | 1. Уменьшает дублирования кода. 12 | 2. Может улучшить организацию кода. 13 | 14 | Не стоит трогать, если наличие паралельной иерархии — это необходимое зло, без которого устройство программы было бы еще хуже. Если вы обнаружите, что ваши попытки устранить дублирование приводят к еще большему ухудшению организации кода, то... остановите рефакторинг, откатите все внесенные изменения, выпейте чаю и начните привыкать к этому коду. 15 | -------------------------------------------------------------------------------- /Replace_Conditional_with_Polymorphism.js: -------------------------------------------------------------------------------- 1 | // After 2 | class Bird() { 3 | function getSpeed(self) { 4 | if (self.type == EUROPEAN) { 5 | return self.getBaseSpeed(); 6 | } else if (self.type == AFRICAN) { 7 | return (self.getBaseSpeed() - self.getLoadFactor() * self.numberOfCoconuts); 8 | } else if (self.type == NORWEGIAN_BLUE) { 9 | return isNailed ? 0 : self.getBaseSpeed(self.voltage); 10 | } else { 11 | throw "Should be unreachable"; 12 | } 13 | } 14 | } 15 | 16 | // Refactoring 17 | class Bird() { 18 | function getSpeed(self) { } 19 | } 20 | 21 | class European(Bird) { 22 | function getSpeed(self) { 23 | return self.getBaseSpeed(); 24 | } 25 | } 26 | 27 | class African(Bird) { 28 | function getSpeed(self) { 29 | return (self.getBaseSpeed() - self.getLoadFactor() * self.numberOfCoconuts); 30 | } 31 | } 32 | 33 | class NorvegianBlue(Bird) { 34 | function getSpeed() { 35 | return self.isNailed ? 0 : self.getBaseSpeed(self.voltage); 36 | } 37 | } 38 | 39 | # Tests with 40 | var speed = bird.getSpeed() 41 | -------------------------------------------------------------------------------- /Inline_Method (Встраивание метода).md: -------------------------------------------------------------------------------- 1 | Встраивание метода 2 | 3 | Проблема: стоит использовать в том случае, когда тело метода очевиднее самого метода. 4 | 5 | Решение: замените вызовы метода его содержимым и удалите сам метод. 6 | 7 | Примеры: Inline_Method.js, Inline_Method.py 8 | 9 | Причины рефакторинга: 10 | 11 | 1. Основаная причина – тело метода состоит из простого делегирования к другому методу. Само по себе такое делегирование — не проблема. Но если таких методов довольно много, становится очень легко в них запутаться. 12 | 2. Зачастую методы не бывают слишком короткими изначально, а становятся такими в результате изменений в программе. Поэтому не стоит бояться избавляться от ставших ненужными методов. 13 | 14 | Достоинства: минимизируя количество бесполезных методов, мы уменьшаем общую сложность кода. 15 | 16 | Порядок рефакторинга: 17 | 18 | 1. Убедитесь, что метод не переопределяется в подклассах. Если он переопределяется, воздержитесь от рефакторинга. 19 | 2. Найдите все вызовы метода. Замените эти вызовы содержимым метода. 20 | 3. Удалите метод. 21 | -------------------------------------------------------------------------------- /Incomplete Library Class (Неполнота библиотечного класса).md: -------------------------------------------------------------------------------- 1 | Неполнота библиотечного класса 2 | 3 | Симптомы и признаки: библиотеки через некоторое время перестают удовлетворять требованиям пользователей. Естественное решение — внести изменения в библиотеку – очень часто оказывается недоступным, т.к. библиотека закрыта для записи. 4 | 5 | Причины появления: автор библиотеки не предусмотрел возможности, которые вам нужны, либо отказался их внедрять. 6 | 7 | Лечение 8 | 9 | 1. Если надо добавить пару методов в библиотечный класс, используется введение внешнего метода. 10 | 2. Если надо серьёзно поменять поведение класса, используется введение локального расширения. 11 | 12 | Выигрыш: уменьшает дублирование кода (вместо создания своей библиотеки с нуля, вы используете готовую библиотеку). 13 | 14 | Не стоит трогать, если расширение библиотеки может стать причиной появления дополнительного объема работы. Это происходит в том случае, когда изменения в библиотеке затрагивают изменения в коде. 15 | -------------------------------------------------------------------------------- /Push Down Method (Спуск метода).md: -------------------------------------------------------------------------------- 1 | Спуск метода 2 | 3 | Проблема: поведение, реализованное в суперклассе, используется только одним или несколькими подклассами. 4 | 5 | Решение: переместите это поведение в подклассы. 6 | 7 | Причины рефакторинга: 8 | 9 | 1. Метод, который планировали сделать универсальным для всех классов, по факту используется только в одном подклассе. Такая ситуация может возникнуть, когда планируемые фичи так и не были реализованы. 10 | 2. Кроме того, такая ситуация может возникнуть после извлечения (или удаления) части функциональности из иерархии классов, после которого метод остался используемым только в одном подклассе. 11 | 3. Если вы видите, что метод необходим более чем одному подклассу (но не всем), возможно, стоит создать промежуточный подкласс и переместить метод в него. Это позволит избежать дублирования кода, которое возникло бы при спуске метода во все подклассы. 12 | 13 | Достоинства: улучшает связность внутри классов. Метод находится там, где вы ожидаете его увидеть. 14 | 15 | Порядок рефакторинга: 16 | 17 | 1. Объявите метод в подклассе и скопируйте его код из суперкласса. 18 | 2. Удалите метод из суперкласса. 19 | 3. Найдите все места, где используется метод, и убедитесь, что он вызывается из нужного подкласса. 20 | -------------------------------------------------------------------------------- /Dead Code (Мёртвый код).md: -------------------------------------------------------------------------------- 1 | Мёртвый код 2 | 3 | Симптомы и признаки: переменная, параметр, поле, метод или класс больше не используются (чаще всего потому, что устарели). 4 | 5 | Причины появления 6 | 7 | 1. Когда требования к программному продукту изменились, либо были внесены какие-то корректировки, но чистка старого кода так и не была проведена. 8 | 2. Мёртвый код можно обнаружить и в сложном условном коде, где одна из веток никогда не может быть исполнена (в виду ошибки или другого стечения обстоятельств). 9 | 10 | Лечение: лучше всего мёртвый код обнаруживается при помощи хорошей среды разработки (IDE). 11 | 12 | 1. Удалите неиспользуемый код и лишние файлы. 13 | 2. В случае выявления ненужного класса может быть использовано встраивание класса. Если у такого класса есть подклассы, то поможет схлопывание иерархии. 14 | 3. Для удаления ненужных параметров используйте удаление параметра. 15 | 16 | Выигрыш 17 | 18 | 1. Уменьшает размер кода. 19 | 2. Упрощает его поддержку. 20 | -------------------------------------------------------------------------------- /Rename Method (Переименование метода).md: -------------------------------------------------------------------------------- 1 | Переименование метода 2 | 3 | Проблема: Название метода не раскрывает суть того, что он делает. 4 | 5 | Решение: Измените название метода. 6 | 7 | Причины рефакторинга: 8 | 9 | 1. Метод мог получить неудачное название с самого начала. Например, кто-то создал метод впопыхах, не придал должного значения хорошему названию. 10 | 2. С другой стороны, метод мог быть назван удачно изначально, но ввиду расширения его функциональности, имя метода перестало быть актуальным. 11 | 12 | Достоинства: Улучшает читабельность кода. Постарайтесь дать новому методу такое название, которое бы отражало суть того, что он делает. Например, createOrder(), renderCustomerInfo() и т.д. 13 | 14 | Порядок рефакторинга: 15 | 16 | 1. Проверьте, не определён ли метод в суперклассе или подклассе. Если так, нужно будет повторить все шаги и в этих классах. 17 | 2. Следующий шаг важен, чтобы сохранить работоспособность программы во время рефакторинга. Итак, создайте новый метод с новыми именем. Скопируйте туда код старого метода. Удалите весь код в старом методе и вставьте вместо него вызов нового метода. 18 | 3. Найдите все обращения к старому методу и замените их обращениями к новому. 19 | 4. Удалите старый метод. Этот шаг неосуществим, если старый метод является частью публичного интерфейса. В этом случае, старый метод нужно пометить как устаревший (deprecated). 20 | -------------------------------------------------------------------------------- /Refused Bequest (Отказ от наследства).md: -------------------------------------------------------------------------------- 1 | Отказ от наследства 2 | 3 | Симптомы и признаки: Если подклас использует лишь малую часть унаследованных методов и свойств суперкласа, это является признаком неправильной иерархии. При этом ненужные методы могут просто не использоваться либо быть переопределёнными и выбрасывать исключения. 4 | 5 | Причины появления: Кто-то создал наследование между классами только из побуждений повторного использования кода, находящегося в суперклассе. При этом суперкласс и подкласс могут являться совершенно различными сущностями. 6 | 7 | Лечение: 8 | 9 | 1. Если наследование не имеет смысла, и подкласс в действительности не является представителем суперкласса, следует избавиться от отношения наследования между этими классами, применив замену наследования делегацией. 10 | 2. Если наследование имеет смысл, нужно избавиться от лишних полей и методов в подклассе. Для этого необходимо извлечь из родительского класса все поля и методы, которые нужны подклассу, в новый суперкласс, и сделать оба класса его наследниками («извлечение суперкласса»). 11 | 12 | Выигрыш: Улучшает понимание и организацию кода. Теперь вы не будете тратить время на догадки о том, почему класс Стул унаследован от класса Животное (несмотря на то, что оба имеют четыре ноги). 13 | -------------------------------------------------------------------------------- /Inline Temp (Встраивание переменной).md: -------------------------------------------------------------------------------- 1 | Встраивание переменной 2 | 3 | Проблема: у вас есть временная переменная, которой присваивается результат простого выражения (и больше ничего). 4 | 5 | Решение: замените обращения к переменной этим выражением. 6 | 7 | Причины рефакторинга: встраивание локальной переменной почти всегда используется как часть замены переменной вызовом метода или для облегчения извлечения метода. 8 | 9 | Достоинства: сам по себе данный рефакторинг не несёт почти никакой пользы. Тем не менее, если переменной присваивается результат выполнения какого-то метода, у вас есть возможность немного улучшить читабельность программы, избавившись от лишней переменной. 10 | 11 | Недостатки: иногда с виду бесполезные временные переменные служат для кеширования, то есть сохранения результата какой-то дорогостоящей операции, который будет в ходе работы использован несколько раз повторно. Перед тем, как осуществлять рефакторинг, убедитесь, что в вашем случае это не так. 12 | 13 | Порядок рефакторинга 14 | 15 | 1. Найдите все места, где используется переменная, и замените их выражением, которое ей присваивалось. 16 | 2. Удалите объявление переменной и строку присваивания ей значения. 17 | -------------------------------------------------------------------------------- /Hide Method (Сокрытие метода).md: -------------------------------------------------------------------------------- 1 | Сокрытие метода 2 | 3 | Проблема: метод не используется другими классами либо используется только внутри своей иерархии классов. 4 | 5 | Решение: сделайте метод приватным или защищённым. 6 | 7 | Причины рефакторинга: очень часто потребность в сокрытии методов получения и установки значений возникает в связи с разработкой более богатого интерфейса, предоставляющего дополнительное поведение. Особенно это проявляется в случае, когда вы начинали с класса в котором не было никаких методов, кроме геттеров и сеттеров. 8 | По мере встраивания в класс нового поведения может обнаружиться, что в открытых геттерах и сеттерах более нет надобности, и тогда можно их скрыть. А после этого, если использовать только прямой доступ к полю, его методы доступа можно вообще удалить. 9 | 10 | Достоинства 11 | 12 | 1. Сокрытие методов упрощает эволюционные изменения в вашем коде. Изменяя приватный метод, вам нужно будет заботиться только о том, чтобы не сломать текущий класс, т.к. этот метод не может быть использован где-то ещё. 13 | 2. Делая методы приватными, вы подчёркиваете важность публичного интерфейса класса, другими словами, тех методов, которые остались публичными. 14 | 15 | Порядок рефакторинга 16 | 17 | 1. Регулярно делайте попытки найти методы, которые можно сделать приватными. Статический анализ кода и хорошее покрытие unit-тестами может очень помочь в этом. 18 | 2. Делайте каждый метод настолько приватным, насколько это возможно. 19 | -------------------------------------------------------------------------------- /Temporary Field (Временное поле).md: -------------------------------------------------------------------------------- 1 | Временное поле 2 | 3 | Симптомы и признаки: временные поля – это поля, которые нужны объекту только при определённых обстоятельствах. Только тогда они заполняются какими-то значениями, оставаясь пустыми в остальное время. 4 | 5 | Причины появления 6 | 7 | 1. Зачастую временные поля создаются для использования в алгоритме, который требует большого числа входных данных. Так, вместо создания большого числа параметров в таком методе, программист решает создать для этих данных поля в классе. Эти поля используются только в данном алгоритме, а в остальное время простаивают. 8 | 2. Такой код очень трудно понять. Вы ожидаете увидеть данные в полях объекта, а они почему-то пустуют почти все время. 9 | 10 | Лечение 11 | 12 | 1. Временные поля и весь код, работающий с ними, можно поместить в свой собственный класс с помощью извлечения класса. По сути, вы создадите объект-метод. 13 | 2. Введите Null-объект и встройте его вместо кода проверок на наличие значений во временных полях. 14 | 15 | Выигрыш: улучшает понятность и организацию кода. 16 | 17 | 18 | -------------------------------------------------------------------------------- /Inline Class (Встраивание класса).md: -------------------------------------------------------------------------------- 1 | Встраивание класса 2 | 3 | Проблема: Класс почти ничего не делает, ни за что не отвечает, и никакой ответственности для этого класса не планируется. 4 | 5 | Решение: Переместите все фичи из описанного класса в другой. 6 | 7 | Причины рефакторинга: часто этот рефакторинг оказывается следствием недавнего «переселения» части фич класса в другие, после чего от исходного класса мало что осталось. 8 | 9 | Достоинства: меньше бесполезных классов — больше свободной оперативной памяти, в том числе, и у вас в голове. 10 | 11 | Порядок рефакторинга 12 | 13 | 1. Создайте в классе-приёмнике публичные поля и методы, которые есть в классе-доноре. Методы должны обращаться к аналогичным методам класса-донора. 14 | 2. Замените все обращения к классу-донору обращениями к полям и методам класса-приёмника. 15 | 3. Самое время протестировать программу и убедиться, что во время работы не было допущено никаких ошибок. Если тесты показали, что все работает так, как должно, начинаем использоватьперемещение метода и перемещение поля для того, чтобы полностью переместить все функциональности в класс-приёмник из исходного класса. Продолжаем делать это, пока исходный класс не окажется совсем пустым. 16 | 4. Удалите исходный класс. 17 | -------------------------------------------------------------------------------- /Move Field (Перемещение поля).md: -------------------------------------------------------------------------------- 1 | Перемещение поля 2 | 3 | Проблема: поле используется в другом классе больше, чем в собственном. 4 | 5 | Решение: создайте поле в новом классе и перенаправьте к нему всех пользователей старого поля. 6 | 7 | Причины рефакторинга: 8 | 9 | Зачастую поля переносятся как часть «извлечение одного класса из другого» (Extract class). Решить, в каком из классов должно остаться поле, бывает непросто. Тем не менее, у нас есть неплохой рецепт — поле должно быть там, где находятся методы, которые его используют (либо там, где этих методов больше). Это правило поможет вам и в других случаях, когда поле попросту находится не там, где нужно. 10 | 11 | Порядок рефакторинга: 12 | 13 | 1. Если поле публичное, вам будет намного проще совершить рефакторинг, если вы сделаете его приватным и предоставите публичные методы доступа (для этого можно использовать рефакторинг «инкапсуляция поля» (Encapsulate field)). 14 | 2. Создайте такое же поле с методами доступа в классе-приёмнике. 15 | 3. Определите, как вы будете обращаться к классу-получателю. Вполне возможно, у вас уже есть поле или метод, которые возвращают подходящий объект. Если нет — нужно будет написать новый метод или поле, в котором бы хранился объект класса-получателя. 16 | 4. Замените все обращения к старому полю на соответствующие вызовы методов в классе-получателе. Если поле не приватное, проделайте это и в суперклассе, и в подклассах. 17 | 5. Удалите поле в исходном классе. 18 | -------------------------------------------------------------------------------- /Decompose Conditional (Разбиение условного оператора).md: -------------------------------------------------------------------------------- 1 | Разбиение условного оператора 2 | 3 | Проблема: у вас есть сложный условный оператор (if-then/else или switch). 4 | 5 | Решение: выделите в отдельные методы все сложные части оператора: условие, then и else. 6 | 7 | Примеры: Decompose_Conditional.py, Decompose_Conditional.js 8 | 9 | Причины рефакторинга: чем длиннее кусок кода, тем сложнее понять, что он делает. Все усложняется ещё больше, когда код щедро приправлен условными операторами: 10 | 11 | 1. Пока вы разберётесь в том, что делает код в then, вы забываете, какое условие стояло в операторе; 12 | 2. Пока вы разбираетесь с else, вы забываете, что делал код в then. 13 | 14 | Достоинства: 15 | 16 | 1. Извлекая код условного оператора в методы с понятным названием, вы упрощаете жизнь тому, кто впоследствии будет этот код поддерживать (зачастую вам самим через месяц или два). 17 | 2. Кстати, этот рефакторинг применим и для коротких выражений в условиях оператора. Строка isSallaryDay() куда наглядней опишет то, что она делает, чем код сравнения дат. 18 | 19 | Порядок рефакторинга: 20 | 21 | 1. Выделите условие в отдельный метод с помощью извлечения метода. 22 | 2. Повторите выделение для then и else части оператора. 23 | -------------------------------------------------------------------------------- /Shotgun Surgery (Стрельба дробью).md: -------------------------------------------------------------------------------- 1 | Стрельба дробью 2 | 3 | Стрельба дробью похожа на Расходящиеся модификации, но является противоположностью этого запаха. Расходящиеся модификации имеют место, когда есть один класс, в котором производится много различных изменений, а Стрельба дробью — это одно изменение, затрагивающее много классов. 4 | 5 | Симптомы и признаки: при выполнении любых модификаций приходится вносить множество мелких изменений в большое число классов. 6 | 7 | Причины появления: одна обязанность была разделена среди множества классов. Это может случиться после фанатичного исправления расходящихся модификаций. 8 | 9 | Лечение 10 | 11 | 1. Вынести все изменения в один класс позволят перемещение метода и перемещение поля. Если для выполнения этого действия нет подходящего класса, то следует предварительно создать новый. 12 | 2. Если после вынесения кода в один класс в оригинальных классах мало что осталось, следует попытаться от них избавиться, воспользовавшись встраиванием класса. 13 | 14 | Выигрыш 15 | 16 | 1. Улучшает организацию кода. 17 | 2. Уменьшает дублирование кода. 18 | 3. Упрощает поддержку. 19 | -------------------------------------------------------------------------------- /Message Chains (Цепочка вызовов).md: -------------------------------------------------------------------------------- 1 | Цепочка вызовов 2 | 3 | Симптомы и признаки: Вы видите в коде цепочки вызовов вроде такой $a->b()->c()->d() 4 | 5 | Причины появления: Цепочка вызовов появляется тогда, когда клиент запрашивает у одного объекта другой, в свою очередь этот объект запрашивает ещё один и т. д. Такие последовательности вызовов означают, что клиент связан с навигацией по структуре классов. Любые изменения промежуточных связей означают необходимость модификации клиента. 6 | 7 | Лечение: 8 | 9 | 1. Для удаления цепочки вызовов применяется приём сокрытие делегирования. 10 | 2. Иногда лучше рассмотреть, для чего используется конечный объект. Может быть, имеет смысл использовать извлечение метода, чтобы извлечь эту функциональность, и передвинуть её в самое начало цепи с помощью перемещения метода. 11 | 12 | Выигрыш: 13 | 14 | 1. Может уменьшить связность между классами цепочки. 15 | 2. Может уменьшить размер кода. 16 | 17 | Не стоит трогать, если вы перестараетесь в процессе сокрытия делегации, в коде будет довольно сложно понять, где именно осуществляется конкретная работа. Другими словами, появится запах Посредник. 18 | -------------------------------------------------------------------------------- /Extract_Variable (Извлечение переменной).md: -------------------------------------------------------------------------------- 1 | Извлечение переменной 2 | 3 | Проблема: у вас есть сложное для понимания выражение. 4 | 5 | Решение: поместите результат выражения или его части в отдельные переменные, поясняющие суть выражения. 6 | 7 | Причины рефакторинга: главная мотивация этого рефакторинга — сделать более понятным сложное выражение, разбив его на промежуточные части. 8 | 9 | Это может быть: 10 | 11 | 1. Условие оператора if () или части оператора ?: в C-подобных языках. 12 | 2. Длинное арифметическое выражение без промежуточных результатов. 13 | 3. Длинное склеивание строк. 14 | 15 | Выделение переменной может стать первым шагом к последующему извлечению метода, если вы увидите, что выделенное выражение используется и в других местах кода. 16 | 17 | Достоинства: улучшает читабельность кода. Постарайтесь дать выделенным переменным хорошие названия, которые будут отражать точно суть выражения. Так вы сделаете код читабельным и сумеете избавиться от лишних комментариев. Например, customerTaxValue, cityUnemploymentRate, clientSalutationString и т.д. 18 | 19 | Недостатки: появляются дополнительные переменные. Но этот минус компенсируется простотой чтения кода. 20 | 21 | Порядок рефакторинга: 22 | 23 | 1. Вставьте новую строку перед интересующим вас выражением и объявите там новую переменную. Присвойте этой переменной часть сложного выражения. 24 | 2. Замените часть вынесенного выражения новой переменной. 25 | 3. Повторите это для всех сложных частей выражения. 26 | -------------------------------------------------------------------------------- /Consolidate_Duplicate_Conditional_Fragments (Объединение дублирующихся фрагментов в условных операторах).md: -------------------------------------------------------------------------------- 1 | Объединение дублирующихся фрагментов в условных операторах 2 | 3 | Проблема: одинаковый фрагмент кода находится во всех ветках условного оператора. Борется с запахом "Дублирование кода". 4 | 5 | Решение: вынесите его за рамки оператора. 6 | 7 | Примеры: Consolidate_Duplicate_Conditional_Fragments.py, Consolidate_Duplicate_Conditional_Fragments.js 8 | 9 | Причины рефакторинга: дублирующий код находится внутри всех веток условного оператора. Зачастую это является результатом эволюции кода внутри веток оператора, тем более, если над кодом работало несколько человек. 10 | 11 | Достоинства: убивает дублирование кода. 12 | 13 | Порядок рефакторинга: 14 | 15 | 1. Если дублирующие участки находятся вначале веток оператора, вынесите их перед условным оператором. 16 | 2. Если такой код выполняется в конце веток, поместите его после условного оператора. 17 | 3. Если дублирующий код расположен случайным образом внутри веток, вам нужно для начала попытаться передвинуть его в начало или в конец ветки, в зависимости от того, меняет ли он результат последующего кода. 18 | 4. Дублирующий фрагмент кода более одной строки можно попытаться извлечь в новый метод, если в этом есть смысл. 19 | 20 | -------------------------------------------------------------------------------- /Replace_Parameter_with_Explicit_Methods (Замена параметра набором специализированных методов).md: -------------------------------------------------------------------------------- 1 | Замена параметра набором специализированных методов 2 | 3 | Проблема: Метод разбит на части, каждая из которых выполняется в зависимости от значения какого-то параметра. 4 | 5 | Решение: Извлеките отдельные части метода в собственные методы и вызывайте их вместо оригинального метода. 6 | 7 | Примеры: Replace_Parameter_with_Explicit_Methods.py, Replace_Parameter_with_Explicit_Methods.js 8 | 9 | Причины рефакторинга: Метод, содержащий варианты выполнения, разросся до грандиозных размеров. В каждой из таких цепочек выполняется нетривиальный код. При этом новые варианты добавляются очень редко. 10 | 11 | Достоинства: Улучшает читабельность кода. Куда очевидней, что делает метод startEngine() чем setValue("engineEnabled", true). 12 | 13 | Когда нельзя применить: Не стоит применять замену параметра явными методами, если метод редко меняется, а новые вариации внутри него не добавляются. 14 | 15 | Порядок рефакторинга: 16 | 17 | 1. Для каждого варианта исполнения метода создайте свой метод. Запускайте эти методы в зависимости от значения параметра в основном методе. 18 | 2. Найдите все места, где вызывается оригинальный метод. Подставьте туда вызов одного из новых методов в зависимости от передающегося параметра. 19 | 3. Когда не останется ни одного вызова оригинального метода, его можно будет удалить. 20 | -------------------------------------------------------------------------------- /Alternative Classes with different Interfaces.md: -------------------------------------------------------------------------------- 1 | Альтернативные классы с разными интерфейсами 2 | 3 | Симптомы и признаки: два класса выполняют одинаковые функции, но имеют разные названия методов. 4 | 5 | Причины появления: программист, который создал один из классов, скорей всего, не знал о том, что в программе уже существует аналогичный по функциям класс. 6 | 7 | Лечение 8 | 9 | 1. Постарайтесь привести интерфейс классов к общему знаменателю: 10 | 2. Переименуйте методы так, чтобы они стали одинаковыми во всех альтернативных классах. 11 | 3. Используйте перемещение метода, добавление параметра и параметризацию метода для того, чтобы сигнатура и реализация методов стали одинаковыми. 12 | 4. Если только часть функциональности классов идентична, попробуйте извлечь эту часть в общий суперкласс. Существующие классы в этом случае станут подклассами. 13 | 5. После того, как вы определились с вариантом «лечения» и осуществили его, подумайте, возможно, один из классов теперь можно удалить. 14 | 15 | Выигрыш 16 | 17 | 1. Вы избавляетесь от ненужного дублирование кода, и, таким образом, уменьшаете его размер. 18 | 2. Повышается читабельность кода, улучшается его понимание. Вам больше не придется гадать, зачем создавался второй класс, выполняющий точно такие же функции, как и первый. 19 | 20 | Не стоит трогать, если объединить классы оказывается невозможно либо настолько сложно, что смысла заниматься этой работой нет. Один из примеров — альтернативные классы находятся в двух разных библиотеках, каждая из которых имеет свою версию класса. 21 | -------------------------------------------------------------------------------- /Parameterize Method (Параметризация метода).md: -------------------------------------------------------------------------------- 1 | Параметризация метода 2 | 3 | Проблема: Несколько методов выполняют похожие действия, которые отличаются только какими-то внутренними значениями, числами или операциями. 4 | 5 | Решение: Объедините все эти методы в один с параметром, в который будет передаваться отличающееся значение. 6 | 7 | Причины рефакторинга 8 | 9 | 1. Если у вас есть схожие методы, скорей всего, в них присутствует дублирующий код со всеми вытекающими недостатками. 10 | 2. Кроме того, если вам нужно будет добавить ещё одну вариацию функциональности, вам придётся создавать ещё один метод. Вместо этого можно бы было запустить существующий метод с другим параметром. 11 | 12 | Недостатки 13 | 14 | 1. Иногда при проведении рефакторинга можно переусердствовать, в результате чего у вас появится длинный и сложный общий метод вместо нескольких простых. 15 | 2. Кроме того, будьте осторожны, выделяя в параметр переключатель какой-то функциональности. В дальнейшем это может привести к созданию большого условного оператора, который надо будет лечить с помощью замены параметра набором специализированных методов. 16 | 17 | Порядок рефакторинга 18 | 19 | 1. Создайте новый метод с параметром и поместите в него общий для всех методов код, применяя извлечение метода. Обратите внимание, иногда общей оказывается только определённая часть методов. В этом случае рефакторинг сведётся к извлечению только этой общей части в новый метод. 20 | 2. Отличающееся значение замените параметром в коде нового метода. 21 | 3. Для каждого старого метода найдите места, где они вызываются, и поменяйте их вызовы на вызовы нового метода с параметром. После чего старый метод можно удалить. 22 | -------------------------------------------------------------------------------- /Replace_Exception_with_Test (Замена исключения проверкой условия).md: -------------------------------------------------------------------------------- 1 | Замена исключения проверкой условия 2 | 3 | Проблема: Вы выбрасываете исключение там, где можно было бы обойтись простой проверкой условия. 4 | 5 | Решение: Замените выбрасывание исключения проверкой этого условия. 6 | 7 | Примеры: Replace_Exception_with_Test.py, Replace_Exception_with_Test.js 8 | 9 | Причины рефакторинга: 10 | 11 | 1. Исключения должны использоваться для обработки внештатного поведения, связанного с неожиданной ошибкой. Они не должны служить заменой проверкам выполнения условий. Если исключения можно избежать, просто проверив какое-то условие перед выполнением действия, то стоит так и сделать. Исключения следует приберечь для настоящих ошибок. 12 | 2. Например, вы зашли на минное поле и там подорвались, вызвав исключение; исключение успешно обработалось и вас вынесло за пределы минного поля. Вместо этого можно бы было просто прочитать указатель перед минным полем и обойти его другой дорогой. 13 | 14 | Достоинства: Простой условный оператор иногда может быть очевиднее блока обработки исключения. 15 | 16 | Порядок рефакторинга: 17 | 18 | 1. Создайте условный оператор для граничного случая и поместите его перед try/catch блоком. 19 | 2. Переместите код из catch-секции внутрь этого условного оператора. 20 | 3. В catch-секции поставьте код выбрасывания обычного безымянного исключения и запустите все тесты. 21 | 4. Если никаких исключений не было выброшено во время тестов, избавьтесь от оператора try/catch. 22 | -------------------------------------------------------------------------------- /Divergent Change (Расходящиеся модификации).md: -------------------------------------------------------------------------------- 1 | Расходящиеся модификации 2 | 3 | Расходящиеся модификации похожи на Стрельбу дробью, но на самом деле являются ее противоположностью. Расходящиеся модификации имеют место, когда есть один класс, в котором производится много разных изменений, а Стрельба дробью — это одно изменение, затрагивающее одновременно много классов. 4 | 5 | Симптомы и признаки: При внесении изменений в класс приходится изменять большое число различных методов. Например, для добавления нового вида товара вам нужно изменить методы поиска, отображения и заказа товаров. 6 | 7 | Причины появления: Часто появление расходящихся модификаций является следствием плохой структурированности программы или программирования методом копирования-вставки. 8 | 9 | Лечение 10 | 11 | 1. Разделите поведения класса, используя извлечение класса. 12 | 2. Если различные классы имеют одно и то же поведение, возможно, следует объединить эти классы, используя наследование (извлечение суперклассаи извлечение подкласса). 13 | 14 | Выигрыш 15 | 16 | 1. Улучшает организацию кода. 17 | 2. Уменьшает дублирование кода. 18 | 3. Упрощает поддержку. 19 | 20 | -------------------------------------------------------------------------------- /Extract Superclass (Извлечение суперкласса).md: -------------------------------------------------------------------------------- 1 | Извлечение суперкласса 2 | 3 | Проблема: у вас есть два класса с общими полями и методами. 4 | 5 | Решение: создайте для них общий суперкласс и перенесите туда одинаковые поля и методы. 6 | 7 | Причины рефакторинга: одним из видов дублирования кода является наличие двух классов, выполняющих сходные задачи одинаковым способом или сходные задачи разными способами. Объекты предоставляют встроенный механизм для упрощения такой ситуации с помощью наследования. Однако часто общность оказывается незамеченной до тех пор, пока не будут созданы какие-то классы, и тогда появляется необходимость создавать структуру наследования позднее. 8 | 9 | Достоинства: 10 | 11 | 1. Убирает дублирование кода. Общие поля и методы теперь «живут» только в одном месте. 12 | 2. Когда нельзя применить 13 | 3. Вы не можете применить этот рефакторинг к классам, которые уже имеют суперкласс. 14 | 15 | Порядок рефакторинга: 16 | 17 | 1. Создайте абстрактный суперкласс. 18 | 2. Используйте подъём поля, подъём метода и подъём тела конструктора для перемещения общей функциональности в суперкласс. Лучше начинать с полей, т.к. помимо общих полей, вам нужно будет перенести те из них, которые используются в общих методах. 19 | 3. Стоит поискать места в клиентском коде, в которых можно заменить использование подклассов вашим общим классом (например, в объявлениях типов). 20 | -------------------------------------------------------------------------------- /Self_Encapsulate_Field (Самоинкапсуляция поля).md: -------------------------------------------------------------------------------- 1 | Самоинкапсуляция поля 2 | 3 | Самоинкапсуляция отличается от обычной «инкапсуляции поля» (Encapsulate field) тем, что рефакторинг производится над приватным полем. 4 | 5 | Проблема: вы используете прямой доступ к приватным полями внутри класса. 6 | 7 | Решение: создайте геттер и сеттер для поля, и пользуйтесь для доступа к полю только ими. 8 | 9 | Причины рефакторинга: бывает так, что вам перестаёт хватать гибкости с прямым доступом к приватному полю внутри класса. Вы хотите иметь возможность инициализировать значение поля при первом запросе или производить какие-то операции над новыми значениями поля в момент присваивания, либо делать все это разными способами в подклассах. 10 | 11 | Достоинства: непрямой доступ к полям — это когда работа с полем происходит через методы доступа (геттеры и сеттеры). Этот подход отличается гораздо большей гибкостью, чем прямой доступ к полям. 12 | 13 | 1. Вы можете осуществлять сложные операции при получении или установке данных в поле. Ленивая инициализация, валидация значений в поле — все это легко реализуемо внутри геттеров и сеттеров поля. 14 | 2. Что ещё важнее, вы можете переопределять геттеры и сеттеры в подклассах. 15 | Вы можете вообще не реализовывать сеттер для поля. Значение поля будет задаваться только в конструкторе, делая это поле неизменяемым для всего периода жизни объекта. 16 | 17 | Недостатки: когда используется прямой доступ к полям, код выглядит проще и нагляднее, хотя и теряет в гибкости. 18 | 19 | Порядок рефакторинга 20 | 21 | 1. Создайте геттер (и опциональный сеттер) для поля. Они должны быть защищёнными (protected) либо публичными (public). 22 | 2. Найдите все прямые обращения к полю и замените их вызовами геттера и сеттера. 23 | -------------------------------------------------------------------------------- /Remove_Control_Flag (Удаление управляющего флага).md: -------------------------------------------------------------------------------- 1 | Удаление управляющего флага 2 | 3 | Проблема: У вас есть булевская переменная, которая играет роль управляющего флага для нескольких булевских выражений. 4 | 5 | Решение: Используйте break, continue и return вместо этой переменной. 6 | 7 | Примеры: Remove_Control_Flag.js 8 | 9 | Причины рефакторинга: Управляющие флаги пришли к нам из тех «бородатых» дней, когда хорошим стилем программирования считалось иметь в функции одну входную точку (строку объявления функции) и одну выходную точку (в самом конце функции). 10 | В современных языках программирования этот подход устарел, т.к. у нас появились специальные операторы для управления ходом программы в циклах и других сложных конструкциях: 11 | 12 | 1. break: останавливает выполнение цикла; 13 | 2. continue: останавливает выполнение текущего витка цикла и переходит к проверке условия цикла и следующей итерации; 14 | 3. return: останавливает выполнение всей функции и возвращает её результат, если он подан в этом операторе. 15 | 16 | Достоинства: Код с управляющим флагом зачастую получается значительно более запутанным, чем при использовании операторов управления выполнением. 17 | 18 | Порядок рефакторинга: 19 | 20 | 1. Найдите присваивание значения управляющему флагу, которое приводит к выходу из цикла или текущей итерации. 21 | 2. Замените его на break, если это выход из цикла, или continue, если это выход из итерации, или return, если нужно вернуть это значение из функции. 22 | 3. Уберите весь остальной код и проверки, связанные с управляющим флагом. 23 | -------------------------------------------------------------------------------- /Pull Up Method (Подъём метода).md: -------------------------------------------------------------------------------- 1 | Подъём метода 2 | 3 | Проблема: подклассы имеют методы, которые делают схожую работу. 4 | 5 | Решение: в этом случае нужно сделать методы идентичными, а затем переместить их в суперкласс. 6 | 7 | Причины рефакторинга: подклассы развивались независимо друг от друга. Это привело к созданию одинаковых (или очень похожих) полей и методов. 8 | 9 | Достоинства: 10 | 11 | 1. Убирает дублирование кода. Если вам нужно внести изменения в метод, лучше сделать это в одном месте, чем искать все дубликаты этого метода в подклассах. 12 | 2. Также этот рефакторинг можно использовать и в случае, если подкласс зачем-то переопределяет метод суперкласса, но, по сути, делает ту же работу. 13 | 14 | Порядок рефакторинга: 15 | 16 | 1. Обследовать похожие методы в суперклассах. Если они не одинаковы, привести их к одному и тому же виду. 17 | 2. Если методы используют разный набор параметров, привести эти параметры к тому виду, который вы хотите видеть в суперклассе. 18 | 3. Скопируйте метод в суперкласс. Здесь вы можете столкнуться с тем, что код метода использует поля и методы, которые есть только в подклассах, а посему недоступны в суперклассе. Чтобы решить эту проблему, вам нужно: 19 | - Для полей: либо поднимите нужные поля в суперкласс, либо используйте самоинкапсуляцию поля для создания геттеров и сеттеров в подклассах, а затем объявите эти геттеры абстрактным методом в суперклассе. 20 | - Для методов: либо поднимите нужные методы в суперкласс, либо объявите для них абстрактные методы в суперклассе (обратите внимание, ваш класс станет абстрактным, если не был таким до этого). 21 | 4. Удалите методы в подклассах. 22 | 5. Проверьте места, в которых вызывается метод. Возможно, в некоторых из них использование подкласса можно заменить суперклассом. 23 | -------------------------------------------------------------------------------- /Replace Delegation with Inheritance (Замена делегирования наследованием).md: -------------------------------------------------------------------------------- 1 | Замена делегирования наследованием 2 | 3 | Проблема: Класс содержит множество простых делегирующих методов ко всем методам другого класса. 4 | 5 | Решение: Сделайте класс наследником делегата, после чего делегирующие методы потеряют смысл. 6 | 7 | Причины рефакторинга: 8 | 9 | 1. Делегация является более гибким подходом, чем наследование, т.к. позволяет изменять реализацию делегирования, а также подставлять туда другие классы. Тем не менее, применение делегации перестаёт быть выгодным, если вы делегируете действия только одному классу, причём всем его публичным методам. 10 | 2. Если в этом случае заменить делегацию наследованием, вы избавите класс от множества делегирующих методов, а себя от необходимости создавать их для каждого нового метода класса-делегата. 11 | 12 | Достоинства: Уменьшает количество кода. Вам больше не нужны все эти делегирующие методы. 13 | 14 | Когда нельзя применить: 15 | 16 | 1. Не применяйте рефакторинг, если класс содержит делегирование только к части публичных методов класса-делегата. Этим вы нарушите принцип замещения Барбары Лисков. 17 | 2. Этот рефакторинг может быть применён только если класс ещё не имеет родителей. 18 | 19 | Порядок рефакторинга: 20 | 21 | 1. Сделайте класс подклассом класса-делегата. 22 | 2. В поле, содержащее ссылку на объект-делегат, поставьте текущий объект. 23 | 3. Один за другим удаляйте методы с простой делегацией. Если у них отличались названия, используйте переименование метода, чтобы привести все методы к одному названию. 24 | 4. Замените все обращения к полю-делегату обращениями к текущему объекту. 25 | 5. Удалите поле-делегат. 26 | -------------------------------------------------------------------------------- /Remove Parameter (Удаление параметра).md: -------------------------------------------------------------------------------- 1 | Удаление параметра 2 | 3 | Проблема: Параметр не используется в теле метода. 4 | 5 | Решение: Удалите неиспользуемый параметр. 6 | 7 | Причины рефакторинга 8 | 9 | Каждый параметр в вызове метода заставляет человека, пишущего код метода, размышлять о том, какая информация может оказаться в этом параметре. И если параметр потом вовсе не используется в теле метода, значит, весь этот мыслительный процесс уходит впустую. 10 | Кроме того, дополнительные параметры – это ещё и лишний код к выполнению. 11 | Порой мы добавляем параметры на будущее, предчувствуя скорые изменения в методе, для которых потребуется этот параметр. Тем не менее, опыт показывает, что лучше добавить параметр тогда, когда он действительно понадобится, ведь ожидаемые изменения могут так и не наступить. 12 | 13 | Достоинства: Метод должен содержать только действительно необходимые параметры. 14 | 15 | Когда нельзя применить: Не стоит начинать этот рефакторинг, если метод имеет альтернативные реализации в подклассах или в суперклассе, и ваш параметр используется в этих реализациях. 16 | 17 | Порядок рефакторинга 18 | 1. Проверьте, не определён ли метод в суперклассе или подклассе. Если так, используется ли там параметр? Если в какой-то из реализаций параметр используется, воздержитесь от рефакторинга. 19 | 2. Следующий шаг важен, чтобы сохранить работоспособность программы во время рефакторинга. Итак, создайте новый метод, скопировав старый, и удалите из него требуемый параметр. Замените код старого метода вызовом нового метода. 20 | 3. Найдите все обращения к старому методу и замените их обращениями к новому методу. 21 | 4. Удалите старый метод. Этот шаг неосуществим, если старый метод является частью публичного интерфейса. В этом случае, старый метод нужно пометить как устаревший (deprecated). 22 | -------------------------------------------------------------------------------- /Hide Delegate (Сокрытие делегирования).md: -------------------------------------------------------------------------------- 1 | Сокрытие делегирования 2 | 3 | Проблема: клиент получает объект B из поля или метода объекта А. Затем клиент вызывает какой-то метод объекта B. 4 | 5 | Решение: создайте новый метод в классе А, который бы делегировал вызов объекту B. Таким образом, клиент перестанет знать о классе В и зависеть от него. 6 | 7 | Причины рефакторинга: 8 | 9 | 1. Для начала следует определиться с названиями: - делегат— это конечный объект, который содержит функциональность, нужную клиенту; - сервер — это объект, к которому клиент имеет непосредственный доступ. 10 | 2. Цепочка вызовов появляется тогда, когда клиент запрашивает у одного объекта другой, потом второй объект запрашивает еще один и т.д. Такие последовательности вызовов означают, что клиент связан с навигацией по структуре классов. Любые изменения промежуточных связей означают необходимость модификации клиента. 11 | 12 | Достоинства: скрывает делегирование от клиента. Чем меньше клиентский код знает подробностей о связях между объектами, тем проще будет впоследствии вносить изменения в программу. 13 | 14 | Недостатки: если требуется создать слишком много делегирующих методов, класс-сервер рискует превратиться в лишнее промежуточное звено и привести к запашку посредник. 15 | 16 | Порядок рефакторинга: 17 | 18 | 1. Для каждого метода класса-делегата, вызываемого клиентом, нужно создать метод в классе-сервере, который бы делегировал вызов классу-делегату. 19 | 2. Измените код клиента так, чтобы он вызывал методы класса-сервера. 20 | 3. Если после всех изменений клиент больше не нуждается в классе-делегате, можно убрать метод доступа к классу-делегату из класса-сервера (тот метод, который использовался изначально для получения класса-делегата). 21 | -------------------------------------------------------------------------------- /Introduce_Null_Object (Введение Null-объекта).md: -------------------------------------------------------------------------------- 1 | Введение Null-объекта 2 | 3 | Проблема: Из-за того, что некоторые методы возвращают null вместо реальных объектов, у вас в коде присутствует множество проверок на null. 4 | 5 | Решение: Вместо null возвращайте Null-объект, который предоставляет поведение по умолчанию. 6 | 7 | Примеры: Introduce_Null_Object.py, Introduce_Null_Object.py 8 | 9 | Причины рефакторинга: Десятки проверок на null усложняют и засоряют код. 10 | 11 | Недостатки: За отказ от условных операторов вы платите ещё одним новым классом. 12 | 13 | Порядок рефакторинга 14 | 15 | 1. Из интересующего вас класса создайте подкласс, который будет выполнять роль Null-объекта. 16 | 2. В обоих классах создайте метод isNull(), который будет возвращать true для Null-объекта и false для реального класса. 17 | 3. Найдите все места, где код может вернуть null вместо реального объекта. Измените этот код так, чтобы он возвращал Null-объект. 18 | 4. Найдите все места, где переменные реального класса сравниваются с null. Замените такие проверки вызовом метода isNull(). 19 | 5.1 Если в этих условных операторах при значении не равном null выполняются методы исходного класса, переопределите эти методы в Null-классе и вставьте туда код из else части условия. После этого условный оператор можно будет вообще удалить, а разное поведение будет осуществляться за счёт полиморфизма. 20 | 5.2 Если не все так просто, и методы переопределить не получается, посмотрите, можно ли просто выделите операции, которые должны были выполняться при значении равном null в новые методы Null-объекта. Вызывайте эти методы вместо старого кода в else как операции по умолчанию. 21 | -------------------------------------------------------------------------------- /Preserve_Whole_Object (Передача всего объекта).md: -------------------------------------------------------------------------------- 1 | Передача всего объекта 2 | 3 | Проблема: Вы получаете несколько значений из объекта, а затем передаёте их в метод как параметры. 4 | 5 | Решение: Вместо этого передавайте весь объект. 6 | 7 | Примеры: Preserve_Whole_Object.py, Preserve_Whole_Object.js 8 | 9 | Причины рефакторинга: 10 | 11 | 1. Проблема заключается в том, что перед вызовом вашего метода нужно каждый раз вызывать методы будущего объекта-параметра. Если способ вызова этих методов либо количество данных, получаемых для метода, поменяется, то изменения придётся искать и вносить в дюжину мест по всей программе. 12 | 2. Вместо этого код получения всех нужных данных может храниться одном месте — в самом методе. 13 | 14 | Достоинства: 15 | 16 | 1. Вместо пачки разнообразных параметров вы видите один объект с понятным названием. 17 | 2. Если методу понадобятся ещё какие-то данные из объекта, не нужно будет переписывать все места, где вызывается этот метод, а только лишь внутренности самого метода. 18 | 19 | Недостатки: В некоторых случаях после такого преобразования метод теряет в универсальности, т.к. он мог получать данные из множества разных источников, а в результате рефакторинга мы ограничиваем круг его применения только для объектов с определённым интерфейсом. 20 | 21 | Порядок рефакторинга: 22 | 23 | 1. Создайте параметр в методе для объекта, из которого можно получить нужные значения. 24 | 2. Теперь начинайте по одному удалять старые параметры из метода, заменяя их в коде вызовами соответствующих методов объекта-параметра. Тестируйте программу после каждой замены параметра. 25 | 3. Удалите код получения значений из объекта-параметра, который стоял перед вызовом метода. 26 | -------------------------------------------------------------------------------- /Change Unidirectional Association to Bidirectional (Замена однонаправленной связи двунаправленной).md: -------------------------------------------------------------------------------- 1 | Замена однонаправленной связи двунаправленной 2 | 3 | Проблема: у вас есть два класса, которым нужно использовать фичи друг друга, но между ними существует только односторонняя связь. 4 | 5 | Решение: добавьте недостающую связь в класс, в котором она отсутствует. 6 | 7 | Причины рефакторинга: между классами изначально была одностороння связь. Однако с течением времени клиентскому коду потребовался доступ в обе стороны этой связи. 8 | 9 | Достоинства: если в классе возникает необходимость в обратной связи, её можно попросту вычислить. Однако, если такие вычисления оказываются довольно сложными, лучше хранить обратную связь. 10 | 11 | Недостатки: 12 | 13 | 1. Двусторонние связи гораздо сложнее в реализации и поддержке, чем односторонние. 14 | 2. Двусторонние связи делают классы зависимыми друг от друга. При односторонней связи один из них можно было использовать отдельно от другого. 15 | 16 | Порядок рефакторинга: 17 | 18 | 1. Добавьте поле, которое будет содержать обратную связь. 19 | 2. Решите, какой класс будет «управляющим». Этот класс должен содержать методы, которые создают или обновляют связь при добавлении или изменении элементов, устанавливая связь в своём классе, а также вызывая служебные методы установки связи в связываемом объекте. 20 | 3. Создайте служебный метод установки связи в «не управляющем классе». Этот метод должен заполнять поле со связью тем, что ему подают в параметрах. Назовите этот метод так, чтобы было очевидно, что его не стоит использовать в других целях. 21 | 4. Если старые методы управления однонаправленной связью находились в «управляющем классе», дополните их вызовами служебных методов из связываемого объекта. 22 | 5. Если старые методы управления связью находились в «не управляющем классе», создайте управляющие методы в «управляющем классе», вызывайте их и делегируйте им выполнение. 23 | -------------------------------------------------------------------------------- /Inappropriate Intimacy (Неуместная близость).md: -------------------------------------------------------------------------------- 1 | Неуместная близость 2 | 3 | Симптомы и признаки: один класс использует служебные поля и методы другого класса. 4 | 5 | Причины появления: смотрите внимательно за классами, которые проводят слишком много времени вместе. Хорошие классы должны знать друг о друге как можно меньше. Такие классы легче поддерживать и повторно использовать. 6 | 7 | Лечение 8 | 9 | 1. Самый простой выход — при помощи перемещения метода и перемещения поля перенести части одного класса в другой (в тот, где они используются). Однако это может сработать только в том случае, если оригинальный класс не использует перемещаемые поля и методы. 10 | 2. Другим решением является извлечение зависимых частей в отдельный класс и сокрытие делегирования к этому классу. 11 | 3. Если между классами существует обоюдная зависимость, стоит прибегнуть к замене двунаправленной связи на однонаправленную. 12 | 4. Если близость возникает между подклассом и родительским классом, рассмотрите возможность замены делегирования наследованием. 13 | 14 | Выигрыш 15 | 16 | 1. Улучшает организацию кода. 17 | 2. Упрощает техническую поддержку и повторное использование кода. 18 | -------------------------------------------------------------------------------- /Replace_Array_with_Object (Замена поля-массива объектом).md: -------------------------------------------------------------------------------- 1 | Замена поля-массива объектом 2 | 3 | Этот рефакторинг является особым случаем замены простого поля объектом. 4 | 5 | Проблема: У вас есть массив, в котором хранятся разнотипные данные. 6 | 7 | Решение: Замените массив объектом, который будет иметь отдельные поля для каждого элемента. 8 | 9 | Причины рефакторинга: 10 | 11 | Массивы – отличный инструмент для хранения однотипных данных и коллекций. Но ждите беды, если вы используете массив в качестве банковских ячеек, например, храните в ячейке №1 — имя пользователя, а в ячейке №14 – его адрес. Такой подход не только может привести к фатальным последствиям, когда кто-то положит что-то не в ту ячейку, но также требует затраты огромного количества времени на запоминание того, где какие данные хранятся. 12 | 13 | Достоинства: 14 | 15 | 1. В образовавшийся класс можно переместить все связанные поведения, которые раньше хранились в основном классе или в других местах. 16 | 2. Поля класса гораздо проще документировать, чем ячейки массива. 17 | 18 | Порядок рефакторинга 19 | 20 | 1. Создайте новый класс, который будет содержать данные из массива. Поместите в него сам массив как публичное поле. 21 | 2. Создайте поле для хранения объекта этого класса в исходном классе. Не забудьте создать также сам объект в том месте, где вы инициировали массив данных. 22 | 3. В новом классе один за другим создавайте методы доступа для всех элементов массива. Давайте им понятные названия, которые отражают суть того, что они хранят. В тоже время изменяйте каждый случай использования ячейки массива в основном коде соответствующими методами доступа к этой ячейке. 23 | 4. Когда методы доступа будут созданы для всех ячеек, сделайте массив приватным. 24 | 5. Для каждого элемента массива создайте приватное поле в классе, после чего измените методы доступа так, чтобы они использовали это поле вместе массива. 25 | 6. Когда все данные будут перемещены, удалите массив. 26 | -------------------------------------------------------------------------------- /Substitute_Algorithm (Замена алгоритма).md: -------------------------------------------------------------------------------- 1 | Замена алгоритма 2 | 3 | Проблема: вы хотите заменить существующий алгоритм другим? 4 | 5 | Решение: замените тело метода, реализующего старый алгоритм, новым алгоритмом. 6 | 7 | Примеры: Substitute_Algorithm.py, Substitute_Algorithm.js 8 | 9 | Причины рефакторинга 10 | 11 | 1. Поэтапный рефакторинг – не единственный способ улучшить программу. Иногда вы сталкиваетесь с таким нагромождением проблем в методе, что его гораздо проще переписать заново. С другой стороны, вы могли найти алгоритм, который куда проще и эффективнее текущего. В этом случае надо просто заменить старый алгоритм новым. 12 | 2. С течением времени ваш алгоритм может оказаться включен в набор известной библиотеки или фреймворка, и вы можете пожелать избавиться от собственной реализации, чтобы облегчить себе поддержку программы. 13 | 3. Требования к работе программы могут измениться насколько сильно, что старый алгоритм невозможно просто «допилить» до соответствия им. 14 | 15 | Порядок рефакторинга 16 | 17 | 1. Убедитесь, что вы по максимуму упростили текущий алгоритм. Перенесите несущественный код в другие методы с помощью извлечения метода. Чем меньше «движущихся частей» останется в исходном алгоритме, тем проще будет его заменить. 18 | 2. Создайте ваш новый алгоритм в новом методе. Замените старый алгоритм новым и запустите тесты программы. 19 | 3. Если результаты не сходятся, верните старую реализацию и сравните результаты. Выясните, почему результаты не совпадают. Нередко, причиной могут быть ошибки в старом алгоритме, но с большей вероятностью не работает что-то в новом. 20 | 4. Когда все тесты начнут проходить, окончательно удалите старый алгоритм. 21 | -------------------------------------------------------------------------------- /Data Clumps (Группы данных).md: -------------------------------------------------------------------------------- 1 | Группы данных 2 | 3 | Симптомы и признаки: иногда в разных частях кода встречаются одинаковые группы переменных (например, параметры подключения к базе данных). Такие группы следует превращать в самостоятельные классы. 4 | 5 | Причины появления 6 | 7 | 1. Появление групп данных является следствием плохой структурированности программы или программирования методом копирования-вставки. 8 | 2. Чтобы определить группу данных, достаточно удалить одно из значений данных и проверить, сохранят ли смысл остальные. Если нет, это верный признак того, что группа переменных напрашивается на объединение их в объект. 9 | 10 | Лечение 11 | 12 | 1. Если повторяющиеся данные являются полями какого-то класса, используйте извлечение класса для перемещения полей в собственный класс. 13 | 2. Если те же группы данных передаются в параметрах методов, используйте замену параметров объектом чтобы выделить их в общий класс. 14 | 3. Если некоторые из этих данных передаются в другие методы, подумайте о возможности передачи в метод всего объекта данных вместо отдельных полей (в этом поможет передача всего объекта). 15 | 4. Посмотрите на код, который использует эти поля. Возможно, имеет смысл перенести этот код в класс данных. 16 | 17 | Выигрыш 18 | 19 | 1. Улучшает понимание и организацию кода. Операции над определёнными данными теперь собраны в одном месте, и их не надо искать по всему коду. 20 | 2. Уменьшает размер кода. 21 | 22 | Не стоит трогать, если передача всего объекта в параметрах метода вместо передачи его значений (элементарных типов) может создать нежелательную зависимость между двумя классами. 23 | -------------------------------------------------------------------------------- /Split_Temporary_Variable (Расщепление переменной).md: -------------------------------------------------------------------------------- 1 | Расщепление переменной 2 | 3 | Проблема: у вас есть локальная переменная, которая используется для хранения разных промежуточных значений внутри метода (за исключением переменных циклов). 4 | 5 | Решение: используйте разные переменные для разных значений. Каждая переменная должна отвечать только за одну определённую вещь. 6 | 7 | Причины рефакторинга: если вы «экономите» переменные внутри функции, повторно используете их для различных несвязанных целей, у вас обязательно начнутся проблемы в тот момент, когда потребуется внести какие-то изменения в код, содержащий эти переменные. Вам придётся перепроверять все случаи использования переменной, чтобы удостовериться в отсутствии ошибки в коде. 8 | 9 | Достоинства 10 | 11 | 1. Каждый элемент программы должен отвечать только за одну вещь. Это сильно упрощает поддержку кода в будущем, т.к. вы можете спокойно заменить этот элемент, не опасаясь побочных эффектов. 12 | 2. Улучшается читабельность кода. Если переменная создавалась очень давно, да еще и в спешке, она могла получить элементарное название, которое не объясняет сути хранимого значения, например, k, a2, value и т.д. У вас есть шанс исправить ситуацию, назначив новым переменным хорошие названия, отражающие суть хранимых значений. Например, customerTaxValue, cityUnemploymentRate, clientSalutationString и т.д. 13 | 3. Этот рефакторинг помогает в дальнейшем выделить повторяющиеся участки кода в отдельные методы. 14 | 15 | Порядок рефакторинга 16 | 17 | 1. Найдите место в коде, где переменная в первый раз заполняется каким-то значением. В этом месте переименуйте ее, причем новое название должно соответствовать присваиваемому значению. 18 | 2. Подставьте её новое название вместо старого в тех местах, где использовалось это значение переменной. 19 | 3. Повторите операцию для случаев, где переменной присваивается новое значение. 20 | -------------------------------------------------------------------------------- /Pull_Up_Constructor_Body (Подъём тела конструктора).md: -------------------------------------------------------------------------------- 1 | Подъём тела конструктора 2 | 3 | Проблема: Подклассы имеют конструкторы с преимущественно одинаковым кодом. 4 | 5 | Решение: Создайте конструктор в суперклассе и вынесите в него общий для подклассов код. Вызывайте конструктор суперкласса в конструкторах подкласса. 6 | 7 | Примеры: Pull_Up_Constructor_Body.py, Pull_Up_Constructor_Body.js 8 | 9 | Причины рефакторинга: Чем этот рефакторинг отличается от подъёма метода? 10 | 11 | 1. В Java подклассы не могут наследовать конструктор, поэтому вы не можете просто применить подъём метода к конструктору подкласса и удалить его после перемещения всего кода конструктора в суперкласс. Вдобавок к созданию конструктора в суперклассе нужно будет иметь конструкторы в подклассах с простым делегированием к конструктору суперкласса. 12 | 2. В C++ и Java (в случае, если вы явно не вызвали конструктор суперкласса) конструктор суперкласса автоматически вызывается перед конструктором подкласса, что делает обязательным перемещение общего кода только из начала конструкторов подклассов (т.к. вы не сможете вызвать конструктор суперкласса в произвольном месте конструктора подкласса). 13 | 3. В большинстве языков программирования конструктор подкласса может иметь свой собственный список параметров, отличный от параметров суперкласса, поэтому вы должны создать конструктор суперкласса только с теми параметрами, которые ему действительно нужны. 14 | 15 | Порядок рефакторинга: 16 | 17 | 1. Создайте конструктор в суперклассе. 18 | 2. Извлеките общий код из начала конструктора каждого из подклассов в конструктор суперкласса. Перед этим действием стоит попробовать поместить как можно больше общего кода в начало конструктора. 19 | 3. Поместите вызов конструктора суперкласса первой строкой в конструкторах подклассов. 20 | -------------------------------------------------------------------------------- /Speculative Generality (Теоретическая общность).md: -------------------------------------------------------------------------------- 1 | Теоретическая общность 2 | 3 | Симптомы и признаки: класс, метод, поле или параметр не используются. 4 | 5 | Причины появления: иногда код создаётся «про запас», чтобы поддерживать какой-то возможный будущий функционал, который в итоге так и не реализуется. В результате этот код становится труднее понимать и сопровождать. 6 | 7 | Лечение 8 | 9 | 1. Для удаления незадействованных абстрактных классов используйте сворачивание иерархии. 10 | 2. Ненужная делегация функциональности другому классу может быть удалена с помощью встраивания класса. 11 | 3. От неиспользуемых методов можно избавиться с помощью встраивания метода. 12 | 4. Методы с неиспользуемыми параметрами должны быть подвергнуты удалению параметров. 13 | 5. Неиспользуемые поля можно просто удалить. 14 | 15 | Выигрыш 16 | 17 | 1. Уменьшение размера кода. 18 | 2. Упрощение поддержки. 19 | 20 | Не стоит трогать, если... 21 | 22 | 1. В случаях, когда вы работаете над фреймворком, создание функциональности, не используемой самим фреймворком, вполне оправдано. Главное, чтобы она была полезна пользователям фреймворка. 23 | 2. Перед удалением элементов, стоит удостовериться, не используются ли они в юнит-тестах. Такое бывает, если в тестах необходим способ получения какой-то служебной информации класса или осуществления каких-то специальных тестовых действий. 24 | -------------------------------------------------------------------------------- /Change Reference to Value (Замена ссылки значением).md: -------------------------------------------------------------------------------- 1 | Замена ссылки значением 2 | 3 | Проблема: у вас есть объект-ссылка, который слишком маленький и неизменяемый, чтобы оправдать сложности по управлению его жизненным циклом. 4 | 5 | Решение: превратите его в объект-значение. 6 | 7 | Причины рефакторинга 8 | 9 | 1. Побудить к переходу от ссылки к значению могут возникшие при работе с объектом-ссылкой неудобства. 10 | 2. Объектами-ссылками необходимо некоторым образом управлять: 11 | 2.1 Всегда приходится запрашивать у объекта-хранилища нужный объект; 12 | 2.2. Ссылки в памяти тоже могут оказаться неудобными в работе; 13 | работать с объектами-ссылками, в отличие от объектов-значений, особенно сложно в распределённых и параллельных системах. 14 | 3. Кроме того, объекты-значения будут особенно полезны, если вам больше подходит неизменяемость объектов, чем возможность изменения их состояния во время жизни объекта. 15 | 16 | Достоинства 17 | 18 | 1. Важное свойство объектов-значений заключается в том, что они должны быть неизменяемыми. При каждом запросе, возвращающем значение одного из них, должен получаться одинаковый результат. Если это так, то не возникает проблем при наличии многих объектов, представляющих одну и ту же вещь. 19 | 2. Объекты-значения гораздо проще в реализации. 20 | 21 | Недостатки: если значение изменяемое, вам необходимо обеспечить, чтобы при изменении любого из объектов обновлялись значения во всех остальных, которые представляют ту же самую сущность. Это настолько обременительно, что проще для этого создать объект-ссылку. 22 | 23 | Порядок рефакторинга 24 | 25 | 1. Обеспечьте неизменяемость объекта. Объект не должен иметь сеттеров или других методов, меняющих его состояние и данные (в этом может помочь удаление сеттера). Единственное место, где полям объекта-значения присваиваются какие-то данные, должен быть конструктор. 26 | 2. Создайте метод сравнения для того, чтобы иметь возможность сравнить два объекта-значения. 27 | 3. Проверьте, возможно ли удалить фабричный метод и сделать конструктор объекта публичным. 28 | -------------------------------------------------------------------------------- /Replace_Parameter_with_Method_Call (Замена параметра вызовом метода).md: -------------------------------------------------------------------------------- 1 | Замена параметра вызовом метода 2 | 3 | Проблема: Перед вызовом метода, выполнятся второй метод, результат которого, передаётся обратно в первый метод как аргумент. При этом значение параметра могло бы быть получено и внутри вызываемого метода. 4 | 5 | Решение: Вместо передачи значения через параметр переместите код получения значения внутрь метода. 6 | 7 | Примеры: Replace_ Parameter_with_Method_Call.py, Replace_ Parameter_with_Method_Call.js 8 | 9 | Причины рефакторинга: В длинном списке параметров зачастую крайне сложно ориентироваться. Кроме того, вызовы таких методов часто превращаются в целую вереницу вычислений значений, которые будут передаваться в метод. Вот почему если значения параметра может быть вычислено при помощи вызова какого-то метода, это следует сделать внутри самого метода, а от параметра избавиться. 10 | 11 | Достоинства: Избавляемся от лишних параметров, упрощая вызовы методов. Эти параметры зачастую создаются как задел на будущее (которое может так и не наступить). 12 | 13 | Недостатки: Параметр может понадобиться завтра для каких-то других целей и метод придётся переписать. 14 | 15 | Порядок рефакторинга: 16 | 17 | 1. Убедитесь, что код получения значения не использует параметров из текущего метода. Это важно, т.к. параметры текущего метода будут недоступны внутри другого метода, из-за чего перенос станет невозможен. 18 | 2. Если код получения значения сложнее, чем один вызов какого-то метода или функции, примените извлечение метода, чтобы выделить этот код в новый метод и сделать вызов простым. 19 | 3. В коде главного метода замените все обращения к заменяемому параметру вызовами метода получения значения. 20 | 4. Используйте «удаление параметра» (Remove parameter), чтобы удалить неиспользуемый теперь параметр. 21 | -------------------------------------------------------------------------------- /Extract Interface (Извлечение интерфейса).md: -------------------------------------------------------------------------------- 1 | Извлечение интерфейса 2 | 3 | Проблема: 4 | 5 | 1. Несколько клиентов пользуются одной и той же частью интерфейса класса. 6 | 2. В двух классах часть интерфейса оказалась общей. 7 | 8 | Решение: выделите эту общую часть в свой собственный интерфейс. 9 | 10 | Причины рефакторинга 11 | 12 | 1. Интерфейсы бывают кстати, когда один и тот же класс может отыгрывать различные роли в различных ситуациях. Используйте извлечение интерфейса, чтобы явно обозначить каждую из ролей. 13 | 2. Ещё один удобный случай возникает, когда требуется описать операции, которые класс выполняет на своём сервере. Если в будущем предполагается разрешить использование серверов нескольких видов, все они должны реализовывать этот интерфейс. 14 | 15 | Полезные факты 16 | 17 | 1. Есть некоторое сходство между извлечением суперкласса и извлечением интерфейса. 18 | 2. Извлечение интерфейса позволяет выделять только общие интерфейсы, но не общий код. Другими словами, если в классах находится дублирующий код, то, применив извлечение интерфейса, вы никак не избавитесь от этого дублирования. 19 | 3. Тем не менее, эту проблему можно уменьшить, применив извлечение класса для помещения поведения, содержащего дублирование в отдельный компонент и делегирования ему всей работы. В случае если объем общего поведения окажется довольно большим, всегда можно применить извлечение суперкласса. Конечно, это даже проще, но помните, что при этом вы получаете только один родительский класс. 20 | 21 | Порядок рефакторинга 22 | 23 | 1. Создайте пустой интерфейс. 24 | 2. Объявите общие операции в интерфейсе. 25 | 3. Объявите нужные классы как реализующие этот интерфейс. 26 | 4. Измените объявление типов в клиентском коде так, чтобы они использовали новый интерфейс. 27 | -------------------------------------------------------------------------------- /Remove_ Assignments to Parameters Удаление присваиваний.md: -------------------------------------------------------------------------------- 1 | Удаление присваиваний параметрам 2 | 3 | Проблема: параметру метода присваивается какое-то значение. 4 | 5 | Решение: вместо параметра воспользуйтесь новой локальной переменной. 6 | 7 | Пример: Remove_Assignments_Parameters.py 8 | 9 | Причины рефакторинга: причины проведения этого рефакторинга такие же, как и при расщеплении переменной, но в данном случае речь идёт о параметре, а не о локальной переменной. 10 | 11 | 1. Если параметр передаётся по ссылке, то после изменения его значения внутри метода, оно передается аргументу, который подавался на вызов этого метода. Очень часто это происходит случайно и приводит к печальным последствиям. Даже если в вашем языке программирования параметры обычно передаются по значению, а не по ссылке, сам код может вызвать замешательство у тех, кто привык считать иначе. 12 | 2. Множественные присваивания разных значений параметру приводят к тому, что вам становится сложно понять, какие именно данные должны находиться в параметре в определенный момент времени. Проблема усугубляется, если ваш параметр и то, что он должен хранить, описаны в документации, но фактически, его значение может не совпадать с ожидаемым внутри метода. 13 | 14 | Достоинства 15 | 16 | 1. Каждый элемент программы должен отвечать только за одну вещь. Это сильно упрощает поддержку кода в будущем, т.к. вы можете спокойно заменить этот элемент, не опасаясь побочных эффектов. 17 | 2. Этот рефакторинг помогает в дальнейшем выделить повторяющиеся участки кода в отдельные методы. 18 | 19 | Порядок рефакторинга 20 | 21 | 1. Создайте локальную переменную и присвойте ей начальное значение вашего параметра. 22 | 2. Во всем коде метода после этой строки замените использование параметра вашей локальной переменной. 23 | -------------------------------------------------------------------------------- /Comments.md: -------------------------------------------------------------------------------- 1 | Комментарии 2 | 3 | Симптомы и признаки: метод содержит множество поясняющих комментариев. 4 | 5 | Причины появления 6 | 7 | 1. Зачастую комментарии создаются с хорошими намерениями, когда автор и сам понимает, что его код недостаточно очевиден и понятен. В таких случаях комментарии играют роль «дезодоранта», т.е. пытаются заглушить «дурной запах» недостаточно проработанного кода. 8 | 2. Самый лучший комментарий — это хорошее название метода или класса. 9 | 3. Если вы чувствуете, что фрагмент кода будет непонятным без комментария, попробуйте изменить структуру кода так, чтобы любые комментарии стали излишними. 10 | 11 | Лечение 12 | 13 | 1. Если комментарий предназначен для того, чтобы объяснить сложное выражение, возможно, это выражение лучше разбить на понятные подвыражения с помощью извлечения переменной. 14 | 2. Если комментарий поясняет целый блок кода, возможно, этот блок можно извлечь в отдельный метод с помощью извлечения метода. Название нового метода вам, скорей всего, подскажет сам комментарий. 15 | 3. Если метод уже выделен, но для объяснения его действия по-прежнему нужен комментарий, дайте методу новое не требующее комментария название. Используйте для этого переименование метода. 16 | 4. Если требуется описать какие-то правила, касающиеся правильной работы метода, попробуйте рефакторинг введение утверждения. 17 | 18 | Выигрыш: код становится более очевидным и понятным. 19 | 20 | Иногда комментарии бывают полезными: 21 | 22 | 1. Те, которые объясняют почему что-то выполняется именно таким образом. 23 | 2. Те, которые объясняют сложные алгоритмы (когда все иные средства упростить алгоритм уже были испробованы). 24 | -------------------------------------------------------------------------------- /Feature Envy (Завистливые функции).md: -------------------------------------------------------------------------------- 1 | Завистливые функции 2 | 3 | Симптомы и признаки: метод обращается к данным другого объекта чаще, чем к собственным данным. Чаще всего предметом зависти являяются данные другого объекта\класса. 4 | 5 | Причины появления: этот запах может появиться после перемещения каких-то полей в класс данных. В этом случае операции с данными, возможно, также следует переместить в этот класс. 6 | 7 | Лечение: следует придерживаться такого правила: то, что изменяется одновременно, нужно хранить в одном месте. Обычно данные и функции, использующие эти данные, также изменяются вместе (хотя бывают исключения). 8 | 9 | 1. Если метод явно следует перенести в другое место, примените перемещение метода. 10 | 2. Если только часть метода обращается к данным другого объекта, примените извлечение метода к этой части. 11 | 3. Если метод использует функции нескольких других классов, нужно сначала определить, в каком классе находится больше всего используемых данных. Затем следует переместить метод в этот класс вместе с остальными данными. Как альтернатива, с помощью извлечения метода метод разбивается на несколько частей, и они помещаются в разные места в других классах. 12 | 13 | Выигрыш: 14 | 15 | 1. Уменьшение дублирования кода (если код работы с данными переехал в одно общее место). 16 | 2. Улучшение организации кода (т.к. методы работы с данными находятся возле этих данных). 17 | 18 | Исключение: паттерны Стратегия и Посетитель, которые позволяют легко изменить поведение, потому что они изолируют небольшой объем функций, которые должны быть заменены, ценой увеличения косвенности. 19 | -------------------------------------------------------------------------------- /Consolidate_Conditional_Expression (Объединение условных операторов).md: -------------------------------------------------------------------------------- 1 | Объединение условных операторов. 2 | 3 | Проблема: у вас есть несколько условных операторов, ведущих к одинаковому результату или действию. Борется с запахом: дублирование кода. 4 | 5 | Решение: объедините все условия в одном условном операторе. 6 | 7 | Примеры: Consolidate_Conditional_Expression.py Consolidate_Conditional_Expression.js 8 | 9 | Причины рефакторинга: код содержит множество чередующихся операторов, которые выполняют одинаковые действия. Причина разделения операторов неочевидна. 10 | 11 | Главная цель объединения операторов — извлечь условие оператора в отдельный метод, упростив его понимание. 12 | 13 | Достоинства: 14 | 15 | 1. Убирает дублирование управляющего кода. Объединение множества условных операторов, ведущих к одной цели, помогает показать, что на самом деле вы делаете только одну сложную проверку, ведущую к одному общему действию. 16 | 2. Объединив все операторы в одном, вы позволяете выделить это сложное условие в новый метод с названием, отражающим суть этого выражения. 17 | 18 | Порядок рефакторинга: Перед тем как осуществлять рефакторинг, убедитесь, что в условиях операторов нет «побочных эффектов», или, другими словами, они не модифицируют что-то, а только возвращают значения. Побочные эффекты могут быть и в коде, который выполняется внутри самого оператора. Например, по результатам условия, что-то добавляется к переменной. 19 | 20 | 1. Объедините множество условий в одном с помощью операторов и и или. Объединение операторов обычно следует такому правилу: 21 | - Вложенные условия соединяются с помощью оператора и. 22 | - Условия, следующие друг за другом, соединяются с помощью оператора или. 23 | 2. Извлеките метод из условия оператора и назовите его так, чтобы он отражал суть проверяемого выражения. 24 | -------------------------------------------------------------------------------- /Introduce Parameter Object (Замена параметров объектом).md: -------------------------------------------------------------------------------- 1 | Замена параметров объектом 2 | 3 | Проблема: в ваших методах встречается повторяющаяся группа параметров. 4 | 5 | Решение: замените эти параметры объектом. 6 | 7 | Причины рефакторинга: одинаковые группы параметров зачастую встречаются не в единственном методе. Это приводит к дублированию кода, как самих параметров, так и частых операций над ними. Как только вы сведёте параметры в одном классе, вы сможете переместить туда и методы обработки этих данных, очистив от этого кода другие методы. 8 | 9 | Достоинства: 10 | 11 | 1. Улучшает читабельность кода. Вместо пачки разнообразных параметров вы видите один объект с понятным названием. 12 | 2. Одинаковые группы параметров тут и там создают особый род дублирования кода, при котором вроде бы не происходит вызова идентичного кода, но все время встречаются одинаковые группы параметров и аргументов. 13 | 14 | Недостатки: если вы переместили в новый класс только данные и не планируете перемещать в него никакие поведения и операции над этими данными, это может попахивать «классами данных» (Data class). 15 | 16 | Порядок рефакторинга 17 | 18 | 1. Создайте новый класс, который будет представлять вашу группу параметров. Сделайте так, чтобы данные объектов этого класса нельзя было изменить после создания. 19 | 2. В методе, к которому применяем рефакторинг, «добавьте новый параметр» (Add parameter), в котором будет передаваться ваш объект-параметр. Во всех вызовах метода передавайте в этот параметр объект, создаваемый из старых параметров метода. 20 | 3. Теперь начинайте по одному удалять старые параметры из метода, заменяя их в коде полями объекта-параметра. Тестируйте программу после каждой замены параметра. 21 | 4. По окончанию оцените, есть ли возможность и смысл перенести какую-то часть метода (а иногда и весь метод) в класс объекта-параметра. Если так, используйте перемещение метода или извлечение метода, чтобы осуществить перенос. 22 | -------------------------------------------------------------------------------- /Replace Inheritance with Delegation (Замена наследования делегированием).md: -------------------------------------------------------------------------------- 1 | Замена наследования 2 | 3 | Проблема: У вас есть подкласс, который использует только часть методов суперкласса или не хочет наследовать его данные. 4 | 5 | Решение: Создайте поле и поместите в него объект суперкласса, делегируйте выполнение методов объекту-суперклассу, уберите наследование. 6 | 7 | Причины рефакторинга: 8 | 9 | 1. Замена наследования композицией может значительно улучшить дизайн классов, если: 10 | - Ваш подкласс нарушает принцип замещения Барбары Лисков. Другими словами, наследование возникло только ради объединения общего кода, но не потому, что подкласс «является» (is-a) расширением суперкласса. 11 | - Подкласс использует только часть методов суперкласса. В этом случае, это только вопрос времени, пока кто-то не вызовет метод суперкласса, который он не должен был вызывать. 12 | 2. Суть рефакторинга сводится к тому, чтобы разделить оба класса, и сделать суперкласс помощником подкласса, а не его родителем. Вместо того чтобы наследовать все методы суперкласса, подкласс будет иметь только необходимые методы, которые будут делегировать выполнение методам объекта-суперкласса. 13 | 14 | Достоинства: 15 | 16 | 1. Класс не содержит лишних методов, которые достались ему в наследство от суперкласса. 17 | 2. В поле-делегат можно подставлять разные объекты, имеющие различные реализации функциональности. По сути, вы получаете реализацию паттерна проектирования Стратегия. 18 | 19 | Недостатки: Приходится писать очень много простых делегирующих методов. 20 | 21 | Порядок рефакторинга 22 | 23 | 1. Создайте поле в подклассе для содержания суперкласса. На первом этапе поместите в него текущий объект. 24 | 2. Измените методы подкласса так, чтобы они использовали объект суперкласса, вместо this. 25 | 3. Для методов, которые были унаследованы из суперкласса и которые вызывается в клиентском коде, в подклассе нужно создать простые делегирующие методы. 26 | 4. Уберите объявление наследования из подкласса. 27 | 5. Измените код инициализации поля, в котором хранится бывший суперкласс, созданием нового объекта. 28 | -------------------------------------------------------------------------------- /Large Class (Большой класс).md: -------------------------------------------------------------------------------- 1 | Большой класс 2 | 3 | Симптомы и признаки: класс содержит множество полей/методов/строк кода. 4 | 5 | Причины появления: 6 | 7 | 1. Классы редко бывают большими изначально. Но со временем постепенно многие из них «раздуваются» в связи с развитием программы. 8 | 2. Как и в случае с длинными методами, чаще всего программисту ментально проще добавить фичу в существующий класс, чем создать новый класс для этой фичи. 9 | 10 | Лечение: когда класс реализует слишком обширный функционал, стоит подумать о его разделении: 11 | 12 | 1. Извлечение класса поможет, если часть поведения большого класса может быть выделена в свой собственный компонент. 13 | 2. Извлечение подкласса поможет, если часть поведения большого класса может иметь альтернативные реализации либо используется в редких случаях. 14 | 3. Извлечение интерфейса поможет, если нужно иметь список операций и поведений, которые клиент сможет использовать. 15 | 4. классах графическго интерфейса часто можно найти данные и поведения, которые не относятся к непосредственной отрисовке интерфейса, а скорее отвечают за общую логику работы. Такие данные и поведения следует выделить в отдельный класс предметной области, который бы управлял работой графического интерфейса. При этом может оказаться необходимым хранить копии некоторых данных в двух местах и обеспечить их согласованность. Дублирование видимых данных предлагает путь, которым можно это осуществить. 16 | 17 | Выигрыш: 18 | 19 | 1. Рефакторинг таких классов избавит разработчиков от необходимости запоминать чрезмерное количество имеющихся у класса атрибутов. 20 | 2. Во многих случаях разделение больших классов на части позволяет избежать дублирования кода и функциональности. 21 | -------------------------------------------------------------------------------- /Replace_Nested_Conditional_with_Guard_Clauses (Замена вложенных условных операторов граничным оператором).md: -------------------------------------------------------------------------------- 1 | Замена вложенных условных операторов граничным оператором 2 | 3 | Проблема: у вас есть группа вложенных условных операторов, среди которых сложно выделить нормальный ход выполнения кода. 4 | 5 | Решение: выделите все проверки специальных или граничных случаев выполнения в отдельные условия и поместите их перед основными проверками. В идеале, вы должны получить «плоский» список условных операторов, идущих один за другим. 6 | 7 | Пример: Replace_ Nested_Conditional_with_Guard_Clauses.py, Replace_ Nested_Conditional_with_Guard_Clauses.js 8 | 9 | Причины рефакторинга: «Условный оператор из ада» довольно просто отличить. Отступы каждого из уровней вложенности формируют в нем отчётливую стрелку, указывающую вправо. 10 | Разобраться в том, что и как делает такой оператор довольно сложно, так как «нормальный» ход выполнения в нем не очевиден. Такие операторы появляются эволюционным путём, когда каждое из условий добавляется в разные промежутки времени без мыслей об оптимизации остальных условий. 11 | Чтобы упростить такой оператор, нужно выделить все особые случаи в отдельные условные операторы, которые бы при наступлении граничных условий, сразу заканчивали выполнение и возвращали нужное значение. По сути, ваша цель — сделать такой оператор плоским. 12 | 13 | Порядок рефакторинга: Постарайтесь избавиться от «побочных эффектов» в условиях операторов. Разделение запроса и модификатора может в этом помочь. Такое решение понадобится для дальнейших перестановок условий. 14 | 15 | 1. Выделите граничные условия, которые приводят к вызову исключения или немедленному возвращению значения из метода. Переместите эти условия в начало метода. 16 | 2. После того, как с переносами покончено, и все тесты стали проходить, проверьте, можно ли использовать объединение условных операторов для граничных условных операторов, ведущих к одинаковым исключениям или возвращаемым значениям. 17 | 18 | -------------------------------------------------------------------------------- /Change Value to Reference (Замена значения ссылкой).md: -------------------------------------------------------------------------------- 1 | Замена значения ссылкой 2 | 3 | Проблема: есть много одинаковых экземпляров одного класса, которые можно заменить одним объектом. 4 | 5 | Решение: превратите одинаковые объекты в один объект-ссылку. 6 | 7 | Причины рефакторинга: во многих системах объекты можно разделить на две категории — объекты-значения и объекты-ссылки. 8 | 9 | 1. Объекты-ссылки — это когда одному объекту реального мира соответствует только один объект программы. Объектами-ссылками обычно становятся объекты пользователей, заказов, товаров и пр. 10 | 2. Объекты-значения — одному объекту реального мира соответствует множество объектов в программе. Такими объектами могут быть даты, телефонные номера, адреса, цвет и другие. 11 | 12 | Выбор между ссылкой и значением не всегда очевиден. Иногда вначале есть простое значение с небольшим объёмом неизменяемых данных. Затем возникает необходимость добавить изменяемые данные и обеспечить передачу этих изменений при всех обращениях к объекту. Тогда появляется необходимость превратить его в объект-ссылку. 13 | 14 | Достоинства: один объект хранит в себе всю последнюю информацию об определенной сущности. Если в этот объект вносятся изменения в одной части программы, они тут же становятся доступными из других частей программы, использующих этот объект. 15 | 16 | Недостатки: объекты-ссылки гораздо сложнее в реализации. 17 | 18 | Порядок рефакторинга 19 | 20 | 1. Используйте замену конструктора фабричным методом над классом, который должен порождать объекты-ссылки. 21 | 2. Определите, какой объект будет ответственным за предоставление доступа к объектам-ссылкам. Вместо создания нового объекта, когда он нужен, вам теперь нужно получать его из какого-то объекта-хранилища или статического поля-словаря. 22 | 3. Определите, будут ли объекты-ссылки создаваться заранее или динамически по мере надобности. Если объекты создаются предварительно, необходимо обеспечить их загрузку перед использованием. 23 | 4. Измените фабричный метод так, чтобы он возвращал объект-ссылку. Если объекты создаются заранее, необходимо решить, как обрабатывать ошибки при запросе несуществующего объекта. Также вам может понадобиться применить к фабрике переименование метода для информации о том, что метод возвращает только существующие объекты. 24 | -------------------------------------------------------------------------------- /Replace_Error_Code_with_Exception (Замена кода ошибки исключением).md: -------------------------------------------------------------------------------- 1 | Замена кода ошибки исключением 2 | 3 | Проблема: Метод возвращает определенное значение, которое будет сигнализировать об ошибке. 4 | 5 | Решение: Вместе этого следует выбрасывать исключение. 6 | 7 | Примеры: Replace_Error_Code_with_Exception.py, Replace_Error_Code_with_Exception.js 8 | 9 | Причины рефакторинга: Возвращение кодов ошибок — давно устаревшая практика процедурного программирования. В современном программировании для обработки ошибок используются специальные классы, называемые исключениями. При возникновении проблемы вы «выбрасываете» такое исключение и оно впоследствии «ловится» одним из обработчиков исключений. При этом запускается специальный код обработки внештатной ситуации, который игнорируется в обычных условиях. 10 | 11 | Достоинства: 12 | 13 | 1. Избавляет код от множества условных операторов проверки кодов ошибок. Обработчики исключений намного чётче разграничивают нормальный и нештатный путь исполнения программы. 14 | 2. Классы исключений могут реализовывать собственные методы, а значит содержать часть функциональности по обработке ошибок (например, для перевода сообщений об ошибках). 15 | 3. В отличие от исключений, коды ошибок не могут быть использованы в конструкторе, т.к. он должен возвращать только новый объект. 16 | 17 | Недостатки: Обработку исключений можно превратить в goto-подобный костыль. Не делайте так! Не используйте исключения для управления исполнением кода. Исключения следует выбрасывать только в целях сообщения об ошибке или критической ситуации. 18 | 19 | Порядок рефакторинга: Старайтесь выполнять шаги этого рефакторинга только для одного кода ошибки за один раз. Так будет легче удержать в голове все важные сведения и избежать ошибок. 20 | 21 | 1. Найдите все вызовы метода, возвращающего код ошибки, и оберните его в try/catch блоки вместо проверки кода ошибки. 22 | 2. Внутри метода вместо возвращения кода ошибки выбрасывайте исключение. 23 | 3. Измените сигнатуру метода так, чтобы она содержала информацию о выбрасываемом исключении (секция @throws). 24 | -------------------------------------------------------------------------------- /Collapse Hierarchy (Свёртывание иерархии).md: -------------------------------------------------------------------------------- 1 | Свёртывание иерархии 2 | 3 | Проблема: у вас есть некая иерархия классов, в которой подкласс мало чем отличается от суперкласса. 4 | 5 | Решение: слейте подкласс и суперкласс воедино. 6 | 7 | Причины рефакторинга: развитие программы привело к тому, что подкласс и суперкласс стали очень мало отличаться друг от друга. Какая-то фича была убрана из подкласса, какой-то метод «переехал» в суперкласс, и вот вы уже имеете два практически одинаковых класса. 8 | 9 | Достоинства 10 | 11 | 1. Уменьшается сложность программы. Меньше классов, меньше вещей, которые нужно держать в голове, меньше «движущихся частей», меньше вероятность сломать что-то при последующих изменениях в коде. 12 | 2. Навигация по коду становится проще, когда методы определены только в одном классе. Нужный метод не приходится искать по всей иерархии. 13 | 3. Когда нельзя применить 14 | 4. Если в иерархии классов находится больше одного подкласса, то после проведения рефакторинга, оставшиеся подклассы должны стать наследниками класса, в котором была объединена иерархия. 15 | 5. Однако имейте в виду, что это может привести к нарушению принципа подстановки Барбары Лисков. Например, если в программе эмуляторе городского транспорта неверно свернуть суперкласс Транспорт в подкласс Автомобиль, класс Самолёт может оказаться наследником Автомобиля, а это уже неправильно. 16 | 17 | Порядок рефакторинга 18 | 19 | 1. Выберите, какой класс убрать удобнее: суперкласс или подкласс. 20 | 2. Используйте подъём поля и подъём метода, если вы решили избавиться от подкласса. Используйте спуск поля и спуск метода, если убран будет суперкласс. 21 | 3. Замените все использования класса, который будет удалён, классом, в который переезжают поля и методы. Зачастую это будет код создания классов, указания типов параметров и переменных, а также документации в комментариях. 22 | 4. Удалите пустой класс. 23 | -------------------------------------------------------------------------------- /Add Parameter (Добавление параметра).md: -------------------------------------------------------------------------------- 1 | Добавление параметра 2 | 3 | Проблема: методу не хватает данных для осуществления каких-то действий. 4 | 5 | Решение: создайте новый параметр, чтобы передать эти данные. 6 | 7 | Причины рефакторинга: вам необходимо внести какие-то изменения в метод. Эти изменения требуют дополнительной информации или данных, которые ранее в метод не подавались. 8 | 9 | Достоинства: Введение нового параметра всегда соперничает с введением нового приватного поля, которое бы содержало необходимые методу данные. Исходя из этого, можно сказать, что поле лучше добавить тогда, когда вам понадобятся какие-то эпизодические или часто изменяющиеся данные, которые нет смысла держать в объекте все время. В этом случае новый параметр послужит лучше приватного поля и рефакторинг будет оправданным. В других случаях лучше ввести приватное поле и заполнять его нужными данными перед вызовом метода. 10 | 11 | Недостатки 12 | 13 | 1. Добавить новый параметр всегда легче, чем его убрать, поэтому списки параметров часто разрастаются до неприличных размеров. Это приводит к появлению запаха длинный список параметров. 14 | 2. То, что вам потребовалось добавить новый параметр, иногда означает, что ваш класс не содержит необходимых данных либо существующие параметры не несут необходимых связанных данных. В обоих случаях, лучшим решением было бы подумать о перемещении данных в основной класс либо в другие классы, объекты которых уже доступны внутри метода. 15 | 16 | Порядок рефакторинга 17 | 18 | 1. Проверьте, не определён ли метод в суперклассе или подклассе. Если метод в них присутствует, нужно будет повторить все шаги также в этих классах. 19 | 2. Следующий шаг важен, чтобы сохранить работоспособность программы во время рефакторинга. Итак, создайте новый метод, скопировав старый, и добавьте в него требуемый параметр. Замените код старого метода вызовом нового метода. Вы можете подставить любое значение в новый параметр (например null для объектов или ноль для чисел). 20 | 3. Найдите все обращения к старому методу и замените их обращениями к новому методу. 21 | 4. Удалите старый метод. Этот шаг неосуществим, если старый метод является частью публичного интерфейса. В этом случае старый метод нужно пометить как устаревший (deprecated). 22 | -------------------------------------------------------------------------------- /Replace Subclass with Fields (Замена подкласса полями).md: -------------------------------------------------------------------------------- 1 | Замена подкласса полями 2 | 3 | Проблема: у вас есть подклассы, которые отличаются только методами, возвращающими данные-константы. 4 | 5 | Решение: замените методы полями в родительском классе и удалите подклассы. 6 | 7 | Причины рефакторинга: 8 | 9 | 1. Бывает так, что вам нужно развернуть действие рефакторинга избавления от кодирования типа. 10 | 2. В одном из подобных случаев иерархия подклассов может отличаться только значениями, возвращаемыми определёнными методами. Причём эти значения не являются результатом вычисления, а жёстко прописаны либо в самих методах, либо в полях, возвращаемых методами. Чтобы упростить архитектуру классов, такая иерархия может быть свёрнута в один класс, содержащий одно или несколько полей с нужными значениями в зависимости от ситуации. 11 | 3. Нужда в этих изменениях могла возникнуть после перемещения большого числа функциональностей из иерархии классов куда-то в другое место. После этого текущая иерархия потеряла свою ценность, и подклассы стали создавать только избыточную сложность. 12 | 13 | Достоинства: упрощает архитектуру системы. Создание подклассов – слишком избыточное решение, если все, что нужно сделать, это возвращать разные значения в нескольких методах. 14 | 15 | Порядок рефакторинга: 16 | 17 | 1. Примените к подклассам замену конструктора фабричным методом. 18 | 2. Замените вызовы конструкторов подклассов вызовами фабричного метода суперкласса. 19 | 3. Объявите в суперклассе поля для хранения значений каждого из методов подклассов, возвращающих константные значения. 20 | 4. Создайте защищённый конструктор суперкласса для инициализации новых полей. 21 | 5. Создайте или модифицируйте имеющиеся конструкторы подклассов, чтобы они вызывали новый конструктор родительского класса и передавали в него соответствующие значения. 22 | 6. Реализуйте каждый константный метод в родительском классе так, чтобы он возвращал значение соответствующего поля, а затем удалите метод из подкласса. 23 | 7. Если конструктор подкласса имеет какую-то дополнительную функциональность, примените встраивание метода для встраивания его конструктора в фабричный метод суперкласса. 24 | 8. Удалите подкласс. 25 | -------------------------------------------------------------------------------- /Data Class (Класс данных).md: -------------------------------------------------------------------------------- 1 | Класс данных 2 | 3 | Симптомы и признаки: классы данных – это классы, которые содержат только поля и простейшие методы для доступа к ним (геттеры и сеттеры). Это просто контейнеры для данных, используемые другими классами. Эти классы не содержат никакой дополнительной функциональности и не могут самостоятельно работать с данными, которыми владеют. 4 | 5 | Причины появления: это нормально, когда класс в начале своей жизни содержит всего лишь несколько публичных полей (а может даже и парочку геттеров/сеттеров). Тем не менее, настоящая сила объектов заключается в том, что они могут хранить типы поведения или операции над собственными данными. 6 | 7 | Лечение 8 | 9 | 1. Если класс содержит публичные поля, примените инкапсуляцию поля, чтобы скрыть их из прямого доступа, разрешив доступ только через геттеры и сеттеры. 10 | 2. Примените инкапсуляцию коллекции для данных, которые хранятся в коллекциях (вроде массивов). 11 | 3. Осмотрите клиентский код, который использует этот класс. Возможно, там вы найдёте функциональность, которая смотрелась бы уместнее в самом классе данных. В этом случае используйте перемещение метода и извлечение метода для переноса функциональности в класс данных. 12 | 4. После того, как класс наполнился осмысленными методами, возможно, стоит подумать об уничтожении старых методов доступа к данным, которые дают слишком открытый доступ к данным класса. В этом вам поможет удаление сеттера и сокрытие метода. 13 | 14 | Выигрыш 15 | 16 | 1. Улучшает понимание и организацию кода. Операции над определёнными данными теперь собраны в одном месте, их не надо искать по всему коду. 17 | 2. Может вскрыть факты дублирования клиентского кода. 18 | -------------------------------------------------------------------------------- /Form Template Method (Создание шаблонного метода).md: -------------------------------------------------------------------------------- 1 | Создание шаблонного метода 2 | 3 | Проблема: в подклассах реализованы алгоритмы, содержащие похожие шаги и одинаковый порядок выполнения этих шагов. 4 | 5 | Решение: вынесите структуру алгоритма и одинаковые шаги в суперкласс, а в подклассах оставьте реализацию отличающихся шагов. 6 | 7 | Причины рефакторинга: подклассы развиваются параллельно. Иногда разными людьми, что приводит к дублированию кода и ошибок, а также к усложнению поддержки, т.к. каждое изменение приходится проводить во всех подклассах. 8 | 9 | Достоинства: 10 | 11 | 1. Когда мы говорим о дублировании кода, не всегда имеется в виду программирование методом копирования-вставки. Нередко дублирование возникает на более абстрактном уровне. Например, у вас есть метод сортировки чисел и метод сортировки коллекции объектов, при этом, единственное, чем они отличаются это сравнение элементов. Создание шаблонного метода позволяет справиться с таким дублированием, объединив общие шаги алгоритма в суперклассе и оставив различия для подклассов. 12 | 2. Создание шаблонного метода реализует принцип открытости/закрытости. При появлении новой версии алгоритма, вам нужно будет всего лишь создать новый подкласс, не меняя существующий код. 13 | 14 | Порядок рефакторинга: 15 | 16 | 1. Разбейте алгоритмы в подклассах на составные части, описанные в отдельных методах. В этом может помочь извлечение метода. 17 | 2. Получившиеся методы, одинаковые для всех подклассов, можете смело перемещать в суперкласс, используя подъём метода. 18 | 3. Отличающиеся методы приведите к единым названиям с помощью переименования метода. 19 | 4. Поместите сигнатуры отличающихся методов в суперкласс как абстрактные с помощью подъёма метода. Их реализации оставьте в подклассах. 20 | 5. И наконец, поднимите основной метод алгоритма в суперкласс. Он теперь должен работать с методами-шагами, описанными в суперклассе — реальными или абстрактными. 21 | -------------------------------------------------------------------------------- /Move Method (Перемещение метода).md: -------------------------------------------------------------------------------- 1 | Перемещение метода 2 | 3 | Проблема: метод используются в другом классе больше, чем в собственном. 4 | 5 | Решение: создайте новый метод в классе, который использует его больше других, и перенесите туда код из старого метода. Код оригинального метода превратите в обращение к новому методу в другом классе либо уберите его вообще. 6 | 7 | Причины рефакторинга: 8 | 9 | 1. Вы хотите переместить метод в класс, который содержит данные, с которыми, в основном, работает этот метод. Этим вы увеличиваете связность внутри классов. 10 | 2. Вы хотите переместить метод, чтобы убрать или уменьшить зависимость класса, вызывающего этот метод, от класса, в котором он находился. Это может быть полезно, если вызывающий класс уже имеет зависимость от класса, куда вы планируете перенести метод. Таким образом, вы уменьшаете связанность между классами. 11 | 12 | Порядок рефакторинга 13 | 14 | 1. Проверьте все фичи, используемые старым методом в его же классе. Возможно, их тоже следует переместить. Руководствуйтесь таким правилом — если фича используется только интересующим вас методом, её точно следует переносить. Если фича используется и другими методами, возможно, следует перенести и эти методы. Иногда гораздо проще перенести пачку методов, чем настраивать взаимодействие между ними в разных классах. 15 | 16 | - Проверьте, не объявлен ли метод в суперклассах и подклассах приемника. Если это так, вам придётся либо отказаться от идеи переноса, либо реализовать в классе-получателе подобие полиморфизма, чтобы обеспечить различную функциональность метода, которая была разнесена по классам-донорам. 17 | 18 | 2. Объявите новый метод в классе-приёмнике. Возможно, следует придумать для метода новое имя, которое в новом классе будет подходить ему больше. 19 | 3. Определите, как вы будете обращаться к классу-получателю. Вполне возможно, у вас уже есть поле или метод, которые возвращают подходящий объект, но если нет, нужно будет написать новый метод или поле, в котором бы хранился объект класса-получателя. 20 | 21 | - Теперь у вас есть способ обратиться к объекту-получателю и новый метод в его классе. С этим всем вы уже можете превратить старый метод в обращение к новому методу. 22 | 23 | 4. Оцените, есть ли возможность удалить старый метод вообще? При этом нужно будет во всех местах, где используется этот метод, поставить обращение к новому. 24 | -------------------------------------------------------------------------------- /Separate Query from Modifier (Разделение запроса и модификатора).md: -------------------------------------------------------------------------------- 1 | Разделение запроса и модификатора 2 | 3 | Проблема: у вас есть метод, который возвращает какое-то значение, но при этом в процессе работы он изменяет что-то внутри объекта. 4 | 5 | Решение: разделите метод на два разных метода. Один из них пускай возвращает значение, а второй модифицирует объект. 6 | 7 | Причины рефакторинга 8 | 9 | 1. Этот рефакторинг является реализацией принципа разделения команд от запросов данных. Суть принципа сводится к тому, чтобы отделять код получения каких-то данных от кода, который изменяет что-то внутри объекта. 10 | 2. Код получения данных называют запросами, а код изменения видимого состояния объекта — модификаторами. Когда запрос и модификатор совмещены, у вас нет способа получить данные из объекта без того, чтобы не внести изменения в его состояние. Другими словами, вы задаёте вопрос и можете изменить ответ прямо в процессе его получения. Усугубляется эта проблема тем, что человек, вызывающий запрос, может не знать о «побочных действиях» такого метода, что нередко приводит к ошибкам выполнения программы. 11 | 3. Однако стоит подчеркнуть, что «побочными действиями» опасны только те модификаторы, которые меняют видимое состояние объекта. Это, например, поля, которые доступны из публичного интерфейса объекта, записи в базе данных, в файлах и т.д. Если какой-то модификатор всего лишь кеширует какую-то сложную операцию и сохраняет её внутри приватного поля класса, он вряд ли приведёт к «побочным действиям». 12 | 13 | Достоинства: Если у вас есть запрос, который не меняет состояние программы, вы можете вызывать его сколько угодно раз, не опасаясь того, что результат измениться от самого факта вызова метода. 14 | 15 | Недостатки: В некоторых случаях, удобно возвращать какие-то данные после осуществления команды. Например, удаляя что-то из базы данных, вы хотите узнать, сколько при этом строк было удалено. 16 | 17 | Порядок рефакторинга 18 | 19 | 1. Создайте новый метод-запрос, который бы возвращал то, что возвращал оригинальный метод. 20 | 2. Сделайте так, чтобы оригинальный метод возвращал только результат вызова нового метода-запроса. 21 | 3. Замените все обращения к оригинальному методу вызовом метода-запроса, но сразу перед этой строкой вставьте вызов оригинального метода. 22 | 4. Избавьтесь от кода возврата значения в оригинальном методе. После этого он станет правильным методом-модификатором. 23 | -------------------------------------------------------------------------------- /Replace_Constructor_with_Factory_Method (Замена конструктора фабричным методом).md: -------------------------------------------------------------------------------- 1 | Замена конструктора фабричным методом 2 | 3 | Проблема: У вас есть сложный конструктор, делающий нечто большее, чем простая установка значений полей объекта. 4 | 5 | Решение: Создайте фабричный метод и замените им вызовы конструктора. 6 | 7 | Примеры: Replace_Constructor_with_Factory_Method.java, Replace_Constructor_with_Factory_Method.js 8 | 9 | Причины рефакторинга 10 | 11 | 1. Самая очевидная причина применения этого рефакторинга связана с заменой кодирования типа подклассами. 12 | 2. У вас есть код, в котором раньше создавался объект, куда передавалось значение кодированного типа. После применения рефакторинга появилось уже несколько подклассов, из которых нужно создавать объекты в зависимости от значения кодированного типа. Изменить оригинальный конструктор так, чтобы он возвращал объекты подклассов, невозможно, поэтому мы создаём статический фабричный метод, который будет возвращать объекты нужных классов, после чего он заменяет собой все вызовы оригинального конструктора. 13 | 3. Фабричные методы можно использовать и в других ситуациях, когда возможностей конструкторов оказывается недостаточно. Они важны при замене значения ссылкой. Их можно также применять для задания различных режимов создания, выходящих за рамки числа и типов параметров. 14 | 15 | Достоинства 16 | 17 | 1. Фабричный метод не обязательно возвращает объект того класса, в котором он был вызван. Зачастую это могут быть его подклассы, выбираемые в зависимости от подаваемых в метод аргументов. 18 | 2. Фабричный метод может иметь более удачное имя, описывающее, что и каким образом он возвращает, например, Troops::GetCrew(myTank). 19 | 3. Фабричный метод может вернуть уже созданный объект в отличие от конструктора, который всегда создает новый экземпляр. 20 | 21 | Порядок рефакторинга 22 | 23 | 1. Создайте фабричный метод. Поместите в его тело вызова текущего конструктора. 24 | 2. Замените все вызовы конструктора вызовами фабричного метода. 25 | 3. Объявите конструктор приватным. 26 | 4. Обследуйте код конструктора и попытайтесь вынести в фабричный метод тот код, который не относится к непосредственному конструированию объекта текущего класса. 27 | -------------------------------------------------------------------------------- /Encapsulate_Field (Инкапсуляция поля).md: -------------------------------------------------------------------------------- 1 | Инкапсуляция поля 2 | 3 | Проблема: у вас есть публичное поле. 4 | 5 | Решение: сделайте поле приватным и создайте для него методы доступа. 6 | 7 | Причины рефакторинга: одним из столпов объектного программирования является Инкапсуляция или возможность сокрытия данных объекта. Иначе все данные объектов были бы публичными, и другие объекты могли бы получать и модифицировать данные вашего объекта без его ведома. При этом отделяются данные от поведений, связанных с этими данными, ухудшается модульность частей программы и усложняется её поддержка. 8 | 9 | Пример: Encapsulate_Field.java, Encapsulate_Field.js 10 | 11 | Достоинства 12 | 13 | 1. Если данные и поведения какого-то компонента тесно связанны между собой и находятся в одном месте в коде, вам гораздо проще поддерживать и развивать этот компонент. 14 | 2. Кроме того, вы можете производить какие-то сложные операции, связанные с доступом к полям объекта. 15 | 16 | Когда нельзя применить 17 | 18 | 1. Встречаются случаи, когда инкапсуляция полей нежелательна из соображений, связанных с повышением производительности. Эти случаи очень редки, но иногда этот момент бывает очень важным. 19 | 2. Например, у вас есть графический редактор, в котором есть объекты, имеющие координаты x и y. Эти поля вряд ли будут меняться в будущем. К тому же, в программе участвует очень много различных объектов, в которых присутствуют эти поля. Поэтому обращение напрямую к полям координат экономит значительную часть процессорного времени, которое иначе затрачивалось бы на вызовы методов доступа. Как иллюстрация этого исключения, существует класс Point в Java, все поля которого являются публичными. 20 | 3. 21 | Порядок рефакторинга 22 | 23 | 1. Создайте геттер и сеттер для поля. 24 | 2. Найдите все обращения к полю. Замените получение значения из поля геттером, а установку новых значений в поле — сеттером. 25 | 3. После того, как все обращения к полям заменены, сделайте поле приватным. 26 | 27 | Инкапсуляция поля является всего лишь первым шагом к сближению данных и поведений над этими данными. После того, как вы создали простые методы доступа к полям, стоит ещё раз проверить места, где эти методы вызываются. Вполне возможно, код из этих участков уместнее смотрелся бы в самих методах доступа. 28 | -------------------------------------------------------------------------------- /Replace_Method_with_Method Object (Замена метода объектом методов).md: -------------------------------------------------------------------------------- 1 | Замена метода объектом методов 2 | 3 | Проблема: у вас есть длинный метод, в котором локальные переменные так сильно переплетены, что это делает невозможным применение извлечения метода. 4 | 5 | Решение: преобразуйте метод в отдельный класс так, чтобы локальные переменные стали полями этого класса. После этого можно без труда разделить метод на части. 6 | 7 | Примеры: Replace_Method_with_Method_Object.js, Replace_Method_with_Method_Object.py 8 | 9 | Причины рефакторинга: 10 | 11 | 1. Метод слишком длинный, и вы не можете его разделить из-за хитросплетения локальных переменных, которые сложно изолировать друг от друга. 12 | 2. Первым шагом к решению проблемы будет выделение всего этого метода в отдельный класс и превращение его локальных переменных в поля. Во-первых, это позволит вам изолировать проблему в пределах этого класса, а во-вторых, расчистит дорогу для разделения большого метода на методы поменьше, которые, к тому же, не подходили бы к смыслу оригинального класса. 13 | 14 | Достоинства: изоляция длинного метода в собственном классе позволяет остановить бесконтрольный рост метода. Кроме того, даёт возможность разделить его на подметоды в рамках своего класса, не засоряя служебными методами оригинальный класс. 15 | 16 | Недостатки: создаётся ещё один класс, повышая общую сложность программы. 17 | 18 | Порядок рефакторинга: 19 | 20 | 1. Создайте новый класс. Дайте ему название, основываясь на предназначении метода, который рефакторите. 21 | 2. В новом классе создайте приватное поле для хранения ссылки на экземпляр класса, в котором раньше находился метод. 22 | 3. Создайте отдельное приватное поле для каждой локальной переменной метода. 23 | 4. Создайте конструктор, который принимает в параметрах значения всех локальных переменных метода, а также инициализирует соответствующие приватные поля. 24 | 5. Объявите основной метод и скопируйте в него код оригинального метода, заменив локальные переменные приватным полями. 25 | 6. Замените тело оригинального метода в исходном классе созданием объекта-метода и вызовом его основного метода. 26 | -------------------------------------------------------------------------------- /Introduce Local Extension (Введение локального расширения).md: -------------------------------------------------------------------------------- 1 | Введение локального расширения 2 | 3 | Проблема: в служебном классе отсутствуют некоторые методы, которые вам нужны. При этом добавить их в этот класс вы не можете. 4 | 5 | Решение: создайте новый класс, который бы содержал эти методы, и сделайте его наследником служебного класса, либо его обёрткой. 6 | 7 | Причины рефакторинга: в классе, который вы используете, нет нужных вам методов. Или ещё хуже — вы не можете их туда добавить (например, потому что классы находятся в сторонней библиотеке). 8 | 9 | У вас есть два пути: 10 | 11 | 1. Создать подкласс из интересующего класса, который будет содержать новые методы, и наследовать все остальное из родительского класса. Этот путь проще, но иногда бывает заблокирован в самом служебном классе с помощью директивы final. 12 | 2. Создать класс-обёртку, который будет содержать все новые методы, а остальное делегировать связанному объекту служебного класса. Этот путь более трудоёмкий, так как вам нужно будет иметь в довесок не только код поддержания связи между обёрткой и служебным объектом, но и большое количество простых делегирующих методов, которые будут эмулировать публичный интерфейс служебного класса. 13 | 14 | Достоинства: помещая дополнительные методы в отдельный класс-расширение (обёртку или подкласс), вы не засоряете клиентские классы кодом, который им не принадлежит по смыслу. Этим повышается связность компонентов программы и возможность их повторного использования. 15 | 16 | Порядок рефакторинга 17 | 18 | 1. Создайте новый класс-расширение: либо сделайте его наследником служебного класса, либо, если вы решили делать обёртку, создайте в нем поле для хранения объекта служебного класса, к которому будет происходить делегирование. В этом случае нужно будет создать ещё и методы, которые повторяют публичные методы служебного класса и содержат простую делегацию к методам служебного объекта. 19 | 2. Создайте конструктор, использующий параметры конструктора служебного класса. 20 | 3. Кроме того, создайте альтернативный «конвертирующий» конструктор, который принимает в параметрах только объект оригинального класса. Это поможет в подстановке расширения вместо объектов оригинального класса. 21 | 4. Создайте в классе новые расширенные методы. Переместите в него внешние методы из других классов, либо удалите их, если расширение уже имеет такой функционал. 22 | 5. Замените использование служебного класса новым классом-расширением в тех местах, где нужна расширенная функциональность. 23 | -------------------------------------------------------------------------------- /Extract_Method (Извлечение метода).md: -------------------------------------------------------------------------------- 1 | Извлечение метода 2 | 3 | Проблема: у вас есть фрагмент кода, который можно сгруппировать. 4 | 5 | Решение: выделите участок кода в новый метод (или функцию) и вызовите этот метод вместо старого кода. 6 | 7 | Примеры: Extract_Method.py, Extract_Method.js 8 | 9 | Причины рефакторинга: чем больше строк кода в методе, тем сложнее разобраться в том, что он делает. Это основная проблема, которую решает этот рефакторинг. 10 | 11 | Извлечение метода не только убивает множество запашков в коде, но и является одним из этапов множества других рефакторингов. 12 | 13 | Достоинства: 14 | 15 | 1. Улучшает читабельность кода. Постарайтесь дать новому методу название, которое бы отражало суть того, что он делает. Например, createOrder(), renderCustomerInfo() и т.д. 16 | 2. Убирает дублирование кода. Иногда код, вынесенный в метод, можно найти и в других местах программы. В таком случае, имеет смысл заменить найденные участки кода вызовом вашего нового метода. 17 | 3. Изолирует независимые части кода, уменьшая вероятность ошибок (например, по вине переопределения не той переменной). 18 | 19 | Порядок рефакторинга: 20 | 21 | 1. Создайте новый метод и назовите его так, чтобы название отражало суть того, что будет делать этот метод. 22 | 2. Скопируйте беспокоящий вас фрагмент кода в новый метод. Удалите этот фрагмент из старого места и замените вызовом вашего нового метода. 23 | Найдите все переменные, которые использовались в этом фрагменте кода. Если они были объявлены внутри этого фрагмента и не используются вне его, просто оставьте их без изменений — они станут локальными переменными нового метода. 24 | 3. Если переменные объявлены перед интересующим вас участком кода, значит, их следует передать в параметры вашего нового метода, чтобы использовать значения, которые в них находились ранее. Иногда от таких переменных проще избавиться с помощью замены переменных вызовом метода. 25 | 4. Если вы видите, что локальная переменная как-то изменяется в вашем участке кода, это может означать, что её изменённое значение понадобится дальше в основном методе. Проверьте это. Если подозрение подтвердилось, значение этой переменной следует возвратить в основной метод, чтобы ничего не сломать. 26 | -------------------------------------------------------------------------------- /Long Parameter List.md: -------------------------------------------------------------------------------- 1 | Длинный список параметров 2 | 3 | Симптомы и признаки: количество параметров метода больше трёх-четырёх. 4 | 5 | Причины появления: 6 | 7 | 1. Длинный список параметров может появиться после объединения нескольких вариантов алгоритмов в одном методе. В этом случае может быть создан длинный список параметров, контролирующих то, какая из вариаций будет выполнена и как. 8 | 2. Появление длинного списка параметров также может быть связано с попыткой программиста уменьшить связанность между классами. Например, код создания конкретных объектов, нужных в методе, переместили из самого метода в код вызова этого метода, причём созданные объекты передаются в метод как параметры. Таким образом, оригинальный класс перестал знать о связях между объектами, и связность уменьшилась. 9 | 3. Но если таких объектов нужно создать несколько, под каждый из них потребуется свой параметр, что приводит к разрастанию списка параметров. 10 | 4. В длинных списках параметров трудно разбираться, они становятся противоречивыми и сложными в использовании. Вместо длинного списка параметров метод может использовать данные своего собственного объекта. Если всех необходимых данных в текущем объекте нет, в качестве параметра метода можно передать другой объект, который получит недостающие данные. 11 | 12 | Лечение: 13 | 14 | 1. Если данные, передаваемые в метод, можно получить путём вызова метода другого объекта, применяем замену параметра вызовом метода. Этот объект может быть помещён в поле собственного класса либо передан как параметр метода. 15 | 2. Вместо того чтобы передавать группу данных, полученных из другого объекта в качестве параметров, в метод можно передать сам объект, используя передачу всего объекта. 16 | 3. Если есть несколько несвязанных элементов данных, иногда их можно объединить в один объект-параметр, применив замену параметров объектом. 17 | 18 | Выигрыш: 19 | 20 | 1. Повышает читабельность кода, уменьшает его размер. 21 | 2. В процессе рефакторинга вы можете обнаружить дублирование кода, которое ранее было незаметно. 22 | 23 | Не стоит избавляться от параметров, если при этом появляется нежелательная связанность между классами. 24 | -------------------------------------------------------------------------------- /Change Bidirectional Association to Unidirectional (Замена двунаправленной связи однонаправленной).md: -------------------------------------------------------------------------------- 1 | Замена двунаправленной связи однонаправленной 2 | 3 | Проблема: у вас есть двухсторонняя связь между классами, но один из классов больше не использует фичи другого. 4 | 5 | Решение: уберите неиспользуемую связь. 6 | 7 | Причины рефакторинга 8 | 9 | 1. Двустороннюю связь, как правило, сложнее поддерживать, чем одностороннюю. Такая связь требует наличия дополнительного кода, который бы отслеживал корректное создание и удаление связанных объектов, что приводит к усложнению программы. 10 | 2. Кроме того, неправильно реализованная двусторонняя связь может приводить к проблемам с «очисткой мусора», т.е. освобождения памяти, занятой неиспользуемыми объектами. Приведём пример: «сборщик мусора» удаляет из памяти объекты, на которые уже не осталось ссылок из других объектов программы. Допустим, была создана, использована, после чего заброшена пара объектов Пользователь-Заказ. Однако эти объекты не будут очищены из памяти, так как они все ещё содержат ссылки друг на друга. Стоит, однако, заметить, что эта проблема теряет актуальность с течением времени. С ней уже научились бороться многие современные версии языков программирования, которые автоматически обнаруживают неиспользуемые связки объектов и успешно очищают их из памяти. 11 | 3. Кроме того, существует проблема тесной зависимости между классами. Двусторонняя связь предполагает, что два класса должны знать друг о друге, а значит, такие классы невозможно использовать отдельно друг от друга. Наличие множества таких двусторонних связей приводит к тому, что части программы становятся слишком тесно связанными, и любое изменение в одном из компонентов приводит к необходимости менять другие компоненты программы. 12 | 13 | Достоинства: 14 | 15 | 1. Упрощает код класса, которому не нужна связь. Меньше кода — проще поддержка. 16 | 2. Уменьшает зависимость между классами. Независимые классы проще поддерживать, т.к. изменения в них затрагивают только эти классы. 17 | 18 | Порядок рефакторинга 19 | 20 | 1. Убедитесь, что один из следующих пунктов справедлив в отношении ваших классов: 21 | 22 | - Связь не используется вообще; 23 | - Есть другой способ получить связанный объект, например, используя запрос к базе данных; 24 | - Связанный объект может быть передан как аргумент в методы, которые используют его. 25 | 26 | 2. Избавьтесь от использование поля, содержащего связь с другим объектом. Проще всего заменить такую связь передачей нужного объекта в параметрах метода. 27 | 3. Удалите код присваивания связанного объекта полю. 28 | 4. Удалите неиспользуемое теперь поле. 29 | -------------------------------------------------------------------------------- /Introduce_Assertion (Введение проверки утверждения).md: -------------------------------------------------------------------------------- 1 | Введение проверки утверждения. Здесь под проверками подразумевается использование вызовов assert(). 2 | 3 | Проблема: Корректная работа участка кода предполагает наличие каких-то определённых условий или значений. 4 | 5 | Решение: Замените эти предположения конкретными проверками. 6 | 7 | Примеры: Introduce_Assertion.py, Introduce_Assertion.js 8 | 9 | Причины рефакторинга 10 | 11 | 1. Участок кода создает предположения о чем-то (например, о текущем состоянии объекта, значении параметра или локальной переменной). Обычно это предположение никогда не будет нарушено разве что при наличии какой-то ошибки. 12 | 2. Сделайте эти предположения явными, добавив соответствующие проверки. Как и подсказки типов в параметрах методов, эти проверки могут играть роль живой документации кода. 13 | 3. Хорошим признаком того, что нужна проверка каких-то условий, являются комментарии в коде. Если вы видите комментарии, описывающие условия, при которых метод корректно работать, значит здесь неплохо было бы вставить проверку-утверждение этих условий. 14 | 15 | Достоинства: Если какое-то предположение неверно и в результате код производит также неверный результат, лучше сразу остановить выполнение, пока это не привело к фатальным последствиям и порче данных. Кроме того, это означает, что вы упустили написание какого-то теста в наборе тестов вашей программы. 16 | 17 | Недостатки 18 | 19 | 1. Иногда вместо простой проверки лучше вставить исключение. При этом вы можете выбрать необходимый класс исключения и дать остальному коду возможность корректно его обработать. 20 | 2. В каких случаях исключение лучше простой проверки? Если к нему может привести деятельность пользователя или системы, и вы можете обработать это исключение. С другой стороны, обычные безымянные необрабатываемые исключения равносильны простым проверкам — вы их не обрабатываете, они могут быть вызваны только как результат бага в программе, который никогда не должен был возникнуть. 21 | 22 | Порядок рефакторинга 23 | 24 | 1. Когда вы видите, что предполагается какое-то условие, добавьте проверку этого условия, чтобы проверить его наверняка. 25 | 2. Добавление проверки не должно изменять поведение программы. 26 | 3. Не перебарщивайте с использованием проверок для всего, что только можно, на выбранном участке кода. Нужно проверять лишь те условия, без которых код перестанет корректно работать. Если ваш код нормально работает, в том числе, в случае, когда проверка не проходит успешно, такую проверку можно убрать. 27 | -------------------------------------------------------------------------------- /Switch Statements.md: -------------------------------------------------------------------------------- 1 | Операторы switch 2 | 3 | Симптомы и признаки: у вас есть сложный оператор switch или последовательность if-ов. 4 | 5 | Причины появления 6 | 7 | 1. Одним из очевидных признаков объектно-ориентированного кода служит сравнительно редкое использование операторов типа switch или case. Часто один и тот же блок switch оказывается разбросанным по разным местам программы. При добавлении в него нового варианта приходится искать все эти блоки switch и модифицировать их. 8 | 2. Как правило, заметив блок switch, следует подумать о полиморфизме. 9 | 10 | Лечение 11 | 12 | 1. Чтобы изолировать switch и поместить его в нужный класс может понадобиться извлечение метода и перемещение метода. 13 | 2. Если switch переключается по коду типа, например, переключается режим выполнения программы, то следует использовать замену кодирования типа подклассами или замену кодирования типа состоянием/стратегией. 14 | 3. После настройки структуры наследования следует использовать замену условного оператора полиморфизмом. 15 | 4. Если вариантов в операторе не очень много и все они приводят к вызову одиного и того же метода с разными параметрами, введение полиморфизма будет избыточным. В этом случае стоит задуматься о разбиении этого метода на несколько разных, которые будут выполнять каждый свои функции, для чего нужно применить замену параметра набором специализированных методов. 16 | 5. Если одним из вариантов условного оператора является null, используйте введение Null-объекта. 17 | 18 | Выигрыш: улучшает организацию кода. 19 | 20 | Не стоит трогать, если... 21 | 22 | 1. Когда оператор switch выполняет простые действия, нет никакого смысла что-то менять в коде. 23 | 2. Зачастую оператор switch используется фабричных паттернах проектирования (Фабричный метод, Абстрактная фабрика), для выбора создаваемого класса. 24 | -------------------------------------------------------------------------------- /Introduce_Foreign_Method (Введение внешнего метода).md: -------------------------------------------------------------------------------- 1 | Введение внешнего метода 2 | 3 | Проблема: служебный класс не содержит метода, который вам нужен, при этом у вас нет возможности добавить метод в этот класс. 4 | 5 | Решение: добавьте метод в клиентский класс и передавайте в него объект служебного класса в качестве аргумента. 6 | 7 | Примеры: Introduce_Foreign_Method.py, Introduce_Foreign_Method.js 8 | 9 | Причины рефакторинга: 10 | 11 | 1. У вас есть код, который использует данные и методы определённого класса. Вы приходите к выводу, что этот код намного лучше будет смотреться и работать внутри нового метода в этом классе. Однако возможность добавить такой метод в класс у вас отсутствует (например, потому что класс находится в сторонней библиотеке). 12 | 2. Данный рефакторинг особенно выгоден в случаях, когда участок кода, который вы хотите перенести в метод, повторяется несколько раз в различных местах программы. 13 | 3. Так как вы передаёте объект служебного класса в параметры нового метода, у вас есть доступ ко всем его полям. Вы можете делать внутри этого метода практически все, что вам может потребоваться, как если бы метод был частью служебного класса. 14 | 15 | Достоинства: убирает дублирование кода. Если ваш участок кода повторяется в нескольких местах, вы можете заменить их вызовом метода. Это удобнее дублирования даже с учетом того, что внешний метод находится не там, где хотелось бы. 16 | 17 | Недостатки: причины того, почему метод служебного класса находится в клиентском классе, не всегда очевидны для того специалиста, который будет поддерживать код после вас. Если данный метод может быть использован и в других классах, имеет смысл создать обёртку над служебным классом, и поместить метод туда. То же самое имеет смысл сделать, если таких служебных методов несколько. В этом поможет рефакторинг введение локального расширения. 18 | 19 | Порядок рефакторинга: 20 | 21 | 1. Создайте новый метод в клиентском классе. 22 | 2. В этом методе создайте параметр, в который будет передаваться объект служебного класса. Если этот объект может быть получен из клиентского класса, параметр можно не создавать. 23 | 3. Извлеките волнующие вас участки кода в этот метод и замените их вызовами метода. 24 | 4. Обязательно оставьте в комментарии к этому методу метку Foreign method и призыв поместить этот метод в служебный класс, если такая возможность появится в дальнейшем. Это облегчит понимание того, почему этот метод находится в данном классе для тех, кто будет поддерживать программный продукт в будущем. 25 | -------------------------------------------------------------------------------- /Long Method.md: -------------------------------------------------------------------------------- 1 | Длинный метод 2 | 3 | Симптомы и признаки: метод содержит слишком большое число строк кода. Длина метода более десяти строк должна начинать вас беспокоить. 4 | 5 | Причины появления: 6 | 7 | 1. В метод всё время что-то добавляется, но ничего не выносится. Так как писать код намного проще, чем читать, этот запах долго остаётся незамеченным — до тех пор пока метод не превратится в настоящего монстра. 8 | 2. Стоит помнить, что человеку зачастую ментально сложнее создать новый метод, чем дописать что-то в уже существующий: «Как же, мне нужно добавить всего две строки, не буду же я создавать для этого целый метод». 9 | 3. Таким образом, добавляется одна строка за другой, а в результате метод превращается в большую тарелку спагетти. 10 | 11 | Лечение: следует придерживаться такого правила: если ощущается необходимость что-то прокомментировать внутри метода, этот код лучше выделить в новый метод. Даже одну строку имеет смысл выделить в метод, если она нуждается в разъяснениях. К тому же, если у метода хорошее название, то не нужно будет смотреть в его код, чтобы понять, что он делает. 12 | 13 | 1. Для сокращения тела метода достаточно применить извлечение метода. 14 | 2. Если локальные переменные и параметры препятствуют выделению метода, можно применить замену временной переменной вызовом метода, замену параметров объектом и передачу всего объекта. 15 | 3. Если предыдущие способы не помогли, можно попробовать выделить весь метод в отдельный объект с помощью замены метода объектом методов. 16 | 4. Условные операторы и циклы свидетельствуют о возможности выделения кода в отдельный метод. Для работы с условными выражениями подходит декомпозиция условных операторов. Для работы с циклом — извлечение метода. 17 | 18 | Выигрыш: 19 | 20 | 1. Из всех видов объектного кода дольше всего выживают классы с короткими методами. Чем длиннее ваш метод или функция, тем труднее будет её понять и поддерживать. 21 | 2. Кроме того, в длинных методах зачастую можно обнаружить «залежи» дублирования кода. 22 | -------------------------------------------------------------------------------- /Extract Class (Извлечение класса).md: -------------------------------------------------------------------------------- 1 | Извлечение класса 2 | 3 | Проблема: один класс работает за двоих. 4 | 5 | Решение: создайте новый класс, переместите в него поля и методы, отвечающие за определённую функциональность. 6 | 7 | Причины рефакторинга: классы всегда изначально выглядят чёткими и понятными. Они выполняют свою работу и не лезут в обязанности других классов. Однако, с течением жизни программы добавляется один метод – тут, одно поле – там. В результате некоторые классы получают массу дополнительных обязанностей. 8 | 9 | Достоинства 10 | 11 | 1. Этот рефакторинг призван помочь в соблюдении принципа единственной обязанности класса. Это делает код ваших классов очевиднее и понятнее. 12 | 2. Классы с единственной обязанностью более надёжны и устойчивы к изменениям. Например, у вас есть класс, отвечающий за десять разных вещей. И когда вам придётся вносить в него изменения, вы рискуете при корректировках одной вещи сломать другие. 13 | 14 | Недостатки: если при проведении этого рефакторинга вы перестараетесь, придётся прибегнуть к встраиванию класса. 15 | 16 | Порядок рефакторинга: перед началом рефакторинга обязательно определите, как именно следует разделить обязанности класса. 17 | 18 | 1. Создайте новый класс, который будет содержать выделенную функциональность. 19 | 2. Создайте связь между старым и новым классом. Лучше всего, если эта связь будет односторонней; при этом второй класс можно будет без проблем использовать повторно. С другой стороны, если вы считаете, что это необходимо, всегда можно создать двустороннюю связь. 20 | 3. Используйте перемещение поля и перемещение метода для каждого поля и метода, которые вы решили переместить в новый класс. 21 | 4. Для методов имеет смысл начинать с приватных, таким образом вы снижаете вероятность допустить массу ошибок. Старайтесь двигаться понемногу и тестировать результат после каждого перемещения, это избавит вас от необходимости исправлять большое число ошибок в самом конце. После того как с перемещением покончено, пересмотрите ещё раз на получившиеся классы. Возможно, старый класс теперь имеет смысл назвать по-другому ввиду его изменившихся обязанностей. Проверьте ещё раз, можно ли избавиться от двусторонней связи между классами, если она возникла. 22 | 5. Ещё одним нюансом является доступность нового класса извне. Вы можете полностью спрятать его от клиента, сделав приватным, управляя при этом его полями из старого класса. Либо сделать его публичным, предоставив клиенту возможность напрямую менять значения. Решение зависит от того, насколько безопасны для поведения старого класса будут неожиданные прямые изменения значений в новом классе. 23 | -------------------------------------------------------------------------------- /Replace_Temp_with_Query (Замена переменной вызовом метода).md: -------------------------------------------------------------------------------- 1 | Замена переменной вызовом метода 2 | 3 | Проблема: вы помещаете результат какого-то выражения в локальную переменную, чтобы использовать её далее в коде. 4 | 5 | Решение: выделите все выражение в отдельный метод и возвращайте результат из него. Замените использование вашей переменной вызовом метода. Новый метод может быть использован и в других методах. 6 | 7 | Примеры: Replace_Temp_with_Query.js, Replace_Temp_with_Query.py 8 | 9 | Причины рефакторинга: 10 | 11 | 1. Применение данного рефакторинга может быть подготовительным этапом для применения выделения метода для какой-то части очень длинного метода. 12 | 2. Кроме того, иногда можно найти это же выражение и в других методах, что заставляет задуматься о создании общего метода для его получения. 13 | 14 | Достоинства: 15 | 16 | 1. Улучшает читабельность кода. Намного проще понять, что делает метод getTax() чем строка orderPrice() * -2. 17 | 2. Помогает убрать дублирование кода, если заменяемая строка используется более чем в одном методе. 18 | 19 | Вопрос производительности: 20 | 21 | 1. При использовании этого рефакторинга может возникнуть вопрос, не скажется ли результат рефакторинга не лучшим образом на производительности программы. Честный ответ — да, результирующий код может получить дополнительную нагрузку за счёт вызова нового метода. Однако в наше время быстрых процессоров и хороших компиляторов такая нагрузка вряд ли будет заметна. Зато взамен мы получаем лучшую читабельность кода и возможность использовать новый метод в других местах программы. 22 | 2. Тем не менее, если ваша временная переменная служит для кеширования результата действительно трудоёмкого выражения, имеет смысл остановить этот рефакторинг после выделения выражения в новый метод. 23 | 24 | Порядок рефакторинга: 25 | 26 | 1. Убедитесь, что переменной в пределах метода присваивается значение только один раз. Если это не так, используйте расщепление переменной для того, чтобы гарантировать, что переменная будет использована только для хранения результата вашего выражения. 27 | 2. Используйте извлечение метода для того, чтобы переместить интересующее вас выражение в новый метод. Убедитесь, что этот метод только возвращает значение и не меняет состояние объекта. Если он как-то влияет на видимое состояние объекта, используйте разделение запроса и модификатора. 28 | 3. Замените использование переменной вызовом вашего нового метода. 29 | -------------------------------------------------------------------------------- /Replace Data Value with Object (Замена простого поля объектом).md: -------------------------------------------------------------------------------- 1 | Замена простого поля объектом 2 | 3 | Проблема: В классе (или группе классов) есть поле простого типа. У этого поля есть своё поведение и связанные данные. 4 | 5 | Решение: Создайте новый класс, поместите в него старое поле и его поведения, храните объект этого класса в исходном классе. 6 | 7 | Причины рефакторинга 8 | 9 | 1. В целом этот рефакторинг является частным случаем извлечения класса. Отличия заключаются в причинах, повлёкших рефакторинг. 10 | 2. В извлечении класса мы имеем один класс, который отвечает за разные вещи, и хотим разделить его ответственность. 11 | 3. В замене простого поля объектом мы имеем поле примитивного типа (число, строка и пр.), которое с развитием программы перестало быть таким простым и обзавелось связанными данными и поведениями. С одной стороны, в наличии таких полей нет ничего страшного, с другой – такое семейство-полей-и-поведений может содержаться в нескольких классах одновременно, создавая много дублирующего кода. Поэтому мы создаём для всего этого новый класс и перемещаем в него не только само поле, но и связанные с ним данные и поведения. 12 | 13 | Достоинства: Улучшает связность внутри классов. Данные и поведения этих данных находятся в одном классе. 14 | 15 | Порядок рефакторинга: 16 | 17 | 1. Перед началом рефакторинга, посмотрите есть ли прямые обращения к полю внутри класса. Если да, примените самоинкапсуляцию поля, чтобы скрыть его в исходном классе. 18 | 2. Создайте новый класс и скопируйте в него ваше поле и его геттер. Кроме того, создайте конструктор, принимающий простое значение этого поля. В этом классе не будет сеттера, т.к. каждое новое значение поля, подаваемое в оригинальный класс, будет создавать новый объект-значение. 19 | 3. В исходном классе поменяйте тип поля на новый класс. 20 | 4. В его геттере в исходном классе обращайтесь к геттеру связанного объекта. 21 | 5. В сеттере создайте новый объект-значение. Возможно, вам потребуется также создать новый объект в конструкторе, если ранее там задавались какие-то начальные значения для поля. 22 | 23 | Последующие шаги: 24 | 25 | 1. После этого рефакторинга иногда имеет смысл применить замену значения ссылкой над полем, содержащим объект. Это позволяет вместо хранения десятков объектов для одного и того же значения поля хранить ссылку на один объект, соответствующий этому значению. 26 | 2. Чаще всего такое решение потребуется в случаях, когда вы хотите иметь один объект, отвечающий за один реальный объект из жизни (например, пользователи, заказы, документы и так далее). В тоже время такой подход будет лишним для объектов дат, денег, диапазонов и т.п. 27 | -------------------------------------------------------------------------------- /Encapsulate Collection (Инкапсуляция коллекции).md: -------------------------------------------------------------------------------- 1 | Инкапсуляция коллекции 2 | 3 | Проблема: класс содержит поле-коллекцию и простой геттер и сеттер для работы с этой коллекцией. 4 | 5 | Решение: сделайте возвращаемое геттером значение доступным только для чтения и создайте методы добавления/удаления элементов этой коллекции. 6 | 7 | Причины рефакторинга 8 | 9 | 1. В классе есть поле, содержащее коллекцию объектов. Эта коллекция может быть массивом, списком, множеством или вектором. Для работы с этой коллекцией создан обычный геттер и сеттер. 10 | 2. Однако коллекции должны использовать протокол, несколько отличный от того, который используется другими типами данных. Метод получения не должен возвращать сам объект коллекции, потому что это позволило бы клиентам изменять содержимое коллекции без ведома владеющего ею класса. Кроме того, это чрезмерно раскрывало бы клиентам строение внутренних структур данных объекта. Метод получения элементов коллекции должен возвращать такое значение, которое не позволяло бы изменять коллекцию и не раскрывало бы лишних данных о её структуре. 11 | 3. Кроме того, не должно быть метода, присваивающего коллекции значение; вместо этого должны быть операции для добавления и удаления элементов. Благодаря этому объект-владелец получает контроль над добавлением и удалением элементов коллекции. 12 | 4. Такой протокол осуществляет корректную инкапсуляцию коллекции, что, в итоге, уменьшает связанность между владеющим ею классом и клиентским кодом. 13 | 14 | Достоинства 15 | 16 | 1. Поле коллекции инкапсулировано внутри класса. При вызове геттера возвращается копия коллекции, что исключает возможность случайного изменения или перетирания элементов коллекции, без ведома того объекта, в котором она хранится. 17 | 2. В случае если элементы коллекции содержаться внутри примитивного типа, вроде массива, вы создаёте более удобные методы работы с коллекцией. 18 | 3. Если элементы коллекции содержаться внутри непримитивного контейнера (стандартного класса коллекций), инкапсулировав коллекцию, вы можете ограничить доступ к нежелательным стандартным методам коллекции (например,2. ограничив добавление новых элементов). 19 | 20 | Порядок рефакторинга 21 | 22 | 1. Создайте методы для добавления и удаления элементов коллекции. Они должны принимать элементы коллекции в параметрах. 23 | 2. Присвойте полю пустую коллекцию в качестве начального значения, если это ещё не делается в конструкторе класса. 24 | Найдите вызовы сеттера поля коллекции. Измените сеттер так, чтобы он использовал операции добавления и удаления элементов, или сделайте так, чтобы эти операции вызывал клиентский код. 25 | 3. Обратите внимание, что сеттеры могут быть использованы только для того, чтобы заменить все элементы коллекции на другие. Исходя из этого, возможно, имеет смысл «изменить название сеттера» (Rename method) на replace. 26 | 4. Найдите все вызовы геттера коллекции, после которых происходит изменение коллекции. Поменяйте этот код так, чтобы там использовались ваши новые методы добавления и удаления элементов коллекции. 27 | 5. Измените геттер, чтобы он возвращал представление коллекции, доступное только для чтения. 28 | 6. Обследуйте клиентский код, использующий коллекцию, в поисках кода, который бы лучше смотрелся внутри самого класса коллекции. 29 | -------------------------------------------------------------------------------- /Extract Subclass (Извлечение подкласса).md: -------------------------------------------------------------------------------- 1 | Извлечение подкласса 2 | 3 | Проблема: класс имеет фичи, которые используются только в определённых случаях. 4 | 5 | Решение: создайте подкласс и используйте его в этих случаях. 6 | 7 | Причины рефакторинга: В основном классе находятся методы и поля для реализации какого-то редкого случая использования класса. Этот случай, хоть и очень редкий, является частью обязанностей класса, поэтому все связанные с ним поля и методы неправильно было бы вынести в абсолютно другой класс. Но, с другой стороны, их можно вынести в подкласс с помощью этого рефакторинга. 8 | 9 | Достоинства: 10 | 11 | 1. Создать подкласс довольно легко и быстро. 12 | 2. Можно выделить несколько разных подклассов, если основной класс реализует несколько подобных особых случаев. 13 | 14 | Недостатки: 15 | 16 | 1. Несмотря на всю очевидную простоту, Наследование может завести вас в тупик, если придётся выделить несколько различных иерархий классов. Например, если у вас был класс Собаки, поведение которого отличается в зависимости от размера и длины шерсти собак. Вы выделили из него две иерархии: 17 | 1.1 по размеру: Большие, Средние и Маленькие 18 | 1.2 по длине шерсти: Гладкошёрстные и Длинношёрстные 19 | 2. И все бы хорошо, но у вас начнутся проблемы, когда нужно будет создать одновременно Большую и Гладкошёрстную собаку, т.к. объект можно создать лишь из одного класса. С другой стороны, эту проблему можно обойти, используя Композицию вместо Наследования (см. паттерн Стратегия). Другими словами, класс Собака будет иметь два поля-компонента — размер и длина шерсти. В эти поля вы будете подставлять объекты-компоненты необходимых классов. Например, вы сможете создать Собаку, имеющую БольшойРазмер и ДлиннуюШерсть. 20 | 21 | Порядок рефакторинга 22 | 23 | 1. Создайте новый подкласс из интересующего вас класса. 24 | 2. Если для создания объектов из подкласса будут нужны какие-то дополнительные данные, создайте конструктор и дополните его нужными параметрами. Не забудьте вызвать родительскую реализацию конструктора. 25 | 3. Найдите все вызовы конструктора родительского класса. В тех случаях, когда требуется функциональность подкласса, замените родительский конструктор конструктором подкласса. 26 | 4. Переместите нужные методы и поля из родительского класса в подкласс. Используйте для этого спуск метода и спуск поля. Проще всего начинать перенос с методов. Так поля будут доступны для них все время: из родительского класса до переноса, и из самого подкласса после окончания переноса. 27 | 5. После того как подкласс готов, найдите все старые поля, которые управляли тем, какой набор функций должен выполняться. Эти поля можно удалить, заменив полиморфизмом все условные операторы, в которых они использовались. Простой пример — у вас в классе Автомобиль было поле isElectricCar, и в зависимости от него, в методе refuel() в машину либо заливается бензин, либо заряжается электричество. В результате рефакторинга, поле isElectricCar будет удалено, а классы Автомобиль и ЭлектроАвтомобиль будут иметь свои реализации метода refuel(). 28 | -------------------------------------------------------------------------------- /Replace_Conditional_with_Polymorphism (Замена условного оператора полиморфизмом).md: -------------------------------------------------------------------------------- 1 | Замена условного оператора полиморфизмом 2 | 3 | Проблема 4 | 5 | У вас есть условный оператор, который, в зависимости от типа или свойств объекта, выполняет различные действия. 6 | 7 | Решение 8 | 9 | Создайте подклассы, которым соответствуют ветки условного оператора. В них создайте общий метод и переместите в него код из соответствующей ветки условного оператора. Впоследствии замените условный оператор на вызов этого метода. Таким образом, нужная реализация будет выбираться через полиморфизм в зависимости от класса объекта. 10 | 11 | Пример: Replace_Conditional_with_Polymorphism.py 12 | 13 | Причины рефакторинга: Этот рефакторинг может помочь, если у вас в коде есть условные операторы, которые выполняют различную работу, в зависимости от: 14 | 15 | 1. класса объекта или интерфейса, который он реализует; 16 | 2. значения какого-то из полей объекта; 17 | 3. результата вызова одного из методов объекта. 18 | 19 | При этом если у вас появится новый тип или свойство объекта, нужно будет искать и добавлять код во все схожие условные операторы. Таким образом, польза от данного рефакторинга увеличивается, если условных операторов больше одного, и они разбросаны по всем методам объекта. 20 | 21 | Достоинства: 22 | 1. Этот рефакторинг реализует принцип говори, а не спрашивай: вместо того, чтобы спрашивать объект о его состоянии, а потом выполнять на основании этого какие-то действия, гораздо проще просто сказать ему, что нужно делать, а как это делать он решит сам. 23 | 2. Убивает дублирование кода. Вы избавляетесь от множества почти одинаковых условных операторов. 24 | 3. Если вам потребуется добавить новый вариант выполнения, все, что придётся сделать, это добавить новый подкласс, не трогая существующий код (принцип открытости/закрытости). 25 | 26 | Порядок рефакторинга 27 | 28 | Подготовка к рефакторингу: Чтобы выполнить этот рефакторинг, вам следует иметь готовую иерархию классов, в которых будут содержаться альтернативные поведения. Если такой иерархии ещё нет, нужно создать её. В этом могут помочь другие рефакторинги: 29 | 30 | 1. Замена кодирования типа подклассами. При этом для всех значений какого-то свойства объекта будут созданы свои подклассы. Это хоть и простой, но менее гибкий способ, т.к. нельзя будет создать подклассы для других свойств объекта. 31 | 2. Замена кодирования типа состоянием/стратегией. При этом для определенного свойства объекта будет выделен свой класс и из него созданы подклассы для каждого значения этого свойства. Текущий класс будет содержать ссылки на объекты такого типа и делегировать им выполнение. 32 | 3. Последующие шаги этого рефакторинга подразумевают, что вы уже создали иерархию. 33 | 34 | Шаги рефакторинга 35 | 36 | 1. Если условный оператор находится в методе, который выполняет ещё какие-то действия, «извлеките его в новый метод» (Extract method). 37 | 2. Для каждого подкласса иерархии, переопределите метод, содержащий условный оператор, и скопируйте туда код соответствующей ветки оператора. 38 | 3. Удалите эту ветку из условного оператора. 39 | 4. Повторяйте замену, пока условный оператор не опустеет. Затем удалите условный оператор и объявите метод абстрактным. 40 | -------------------------------------------------------------------------------- /Duplicate Observed Data (Дублирование видимых данных).md: -------------------------------------------------------------------------------- 1 | Дублирование видимых данных 2 | 3 | Проблема: данные предметной области программы хранятся в классах, отвечающих за пользовательский интерфейс (GUI). 4 | 5 | Решение: имеет смысл выделить данные предметной области в отдельные классы и, таким образом, обеспечить связь и синхронизацию между классом предметной области и GUI. 6 | 7 | Причины рефакторинга: вы хотите иметь несколько видов интерфейса для одних и тех же данных (например, у вас есть приложение не только для десктопа, но также для телефонов и планшетов). В этом случае вам будет очень сложно избежать большого количества ошибок и дублирования кода, если вы не разделите GUI и предметную область. 8 | 9 | Достоинства 10 | 11 | 1. Вы разделяете ответственность между классами бизнес-логики и представления (принцип единственной обязанности), что упрощает читабельность и понимание программы в целом. 12 | 2. Если потребуется добавить новый вид интерфейса, вам нужно будет создать новые классы представления, при этом код бизнес-логики трогать нет никакой нужды (принцип открытости/закрытости). 13 | 3. Над бизнес-логикой и пользовательскими интерфейсами теперь могут работать разные люди. 14 | 15 | Когда нельзя применить 16 | 17 | 1. Этот рефакторинг, который в классическом исполнении производится с введением шаблона Наблюдатель, малоприменим для веб-приложений, где все классы пересоздаются между запросами к веб-серверу. 18 | 2. Тем не менее, общий принцип извлечения бизнес-логики в отдельные классы имеет смысл, в том числе, и для веб-приложений. Но реализуется он при помощи других рефакторингов, исходя из дизайна вашей системы. 19 | 20 | Порядок рефакторинга 21 | 22 | 1. Необходимо скрыть прямой доступ к данным предметной области в классе GUI, для чего лучше всего использовать «самоинкапсуляцию поля» (Self encapsulate field). Таким образом, вы создадите геттеры и сеттеры к этим данным. 23 | 2. В обработчиках событий класса GUI используйте сеттеры для установки новых значений полей. Это даст возможность передавать новые значения в связанный объект предметной области. 24 | 3. Создайте класс предметной области и скопируйте в него необходимые поля из класса GUI. Для всех этих полей создайте геттеры и сеттеры. 25 | 4. Примените паттерн Наблюдатель к этим двум классам: 26 | 4.1 В классе предметной области создайте массив для хранения объектов наблюдателей (объектов GUI), а также методы их регистрации, удаления и оповещения. 27 | 4.2 В классе GUI создайте поле для хранения ссылки на объект предметной области, а также метод update(), который будет реагировать на изменения в этом объекте и обновлять значения полей в классе GUI. Обратите внимание, в методе обновления значения должны устанавливаться напрямую, чтобы избежать рекурсии. 28 | 5. В конструкторе класса GUI создайте экземпляр класса предметной области и сохраните его в созданном поле. 29 | 6. Зарегистрируйте объект GUI как наблюдатель в объекте предметной области. 30 | 7. В сеттерах полей класса предметной области вызывайте метод оповещения наблюдателя (т.е. метод обновления в классе GUI), чтобы передать новые значения в пользовательский интерфейс. 31 | 8. Измените сеттеры полей класса GUI так, чтобы они теперь устанавливали новые значения в объекте предметной области, причём напрямую. Будьте внимательны, если значения будут устанавливаться через сеттер класса предметной области, это приведёт к бесконечной рекурсии. 32 | -------------------------------------------------------------------------------- /Replace_Magic_Number_with_Symbolic_Constant (Замена магического числа символьной константой).md: -------------------------------------------------------------------------------- 1 | Замена магического числа символьной константой 2 | 3 | Проблема: В коде используется число, которое несёт какой-то определённый смысл. 4 | 5 | Решение: Замените это число константой с человеко-читаемым названием, объясняющим смысл этого числа. 6 | 7 | Примеры: Replace_Magic_Number_with_Symbolic_Constant.js, Replace_Magic_Number_with_Symbolic_Constant.py 8 | 9 | Причины рефакторинга: 10 | 11 | Магические числа — это числовые значения, встречающиеся в коде, но при этом неочевидно, что они означают. Данный антипаттерн затрудняет понимание программы и усложняет её рефакторинг. Дополнительные сложности возникают, когда нужно поменять определённое магическое число. Это нельзя сделать автозаменой, т.к. одно и то же число может использоваться для разных целей, а значит, вам нужно будет проверять каждый участок кода, где используется это число. 12 | 13 | Достоинства: 14 | 15 | 1. Символьная константа может служить живой документацией смысла значения, которое в ней хранится. 16 | 2. Значение константы намного проще заменить, чем искать нужное число по всему коду, при этом рискуя заменить такое же число, которое в данном конкретном случае использовалось для других целей. 17 | 3. Убирает дублирование использования числа или строки по всему коду. Это особенно актуально, если значение является сложным и длинным (например, -14159, 0xCAFEBABE). 18 | 19 | Полезные факты: 20 | 21 | Не все числа являются магическими. Если предназначения чисел очевидны, их не надо заменять константами, классический пример: 22 | 23 | for (i = 0; i < сount; i++) ... ; 24 | 25 | Альтернативы: 26 | 27 | 1. Иногда, магическое число можно заменить вызовом метода. Например, если у вас есть магическое число, обозначающее количество элементов коллекции, вам не обязательно использовать его для проверок последнего элемента коллекции. Вместо этого можно использовать встроенный метод получения длины коллекции. 28 | Магические числа могут быть использованы для реализации кодирования типа. Например, у вас есть два типа пользователей, и чтобы обозначить их, у вас есть числовое поле в классе, в котором для администраторов хранится число 1, а для простых пользователей – число 2. 29 | В этом случае имеет смысл использовать один из рефакторингов избавления от кодирования типа: 30 | 31 | - замена кодирования типа классом 32 | - замена кодирования типа подклассами 33 | - замена кодирования типа состоянием/стратегией 34 | 35 | Порядок рефакторинга: 36 | 37 | 1. Объявите константу и присвойте ей значение магического числа. 38 | 2. Найдите все упоминания магического числа. 39 | 3. Для всех найденных чисел проверьте, согласуется ли это магическое число с предназначением константы. Если да, замените его вашей константой. Эта проверка важна, т.к. одно и тоже число может означать совершенно разные вещи (в этом случае, они должны быть заменены разными константами). 40 | -------------------------------------------------------------------------------- /Primitive Obsession (Одержимость элементарными типами).md: -------------------------------------------------------------------------------- 1 | Одержимость элементарными типами 2 | 3 | Симптомы и признаки: 4 | 5 | 1. Использование элементарных типов вместо маленьких объектов для небольших задач (например, валюта, диапазоны, специальные строки для телефонных номеров и т.п.) 6 | 2. Использование констант для кодирования какой-то информации (например, константа USER_ADMIN_ROLE = 1 для обозначения пользователей с ролью администратора). 7 | 3. Использование строковых констант в качестве названий полей в массивах. 8 | 9 | Причины появления: 10 | 11 | 1. Как и большинство других запахов, этот начинается с маленькой слабости. Программисту понадобилось поле для хранения каких-то данных. Он подумал, что создать поле элементарного типа куда проще, чем заводить новый класс. Это и было сделано. Потом понадобилось другое поле, и оно было добавлено схожим образом. Не успели оглянуться, как класс уже разросся до грандиозных размеров. 12 | 2. Примитивные типы нередко используются для «симуляции» типов. Это когда вместо отдельного типа данных вы имеете набор чисел или строк, который составляет список допустимых значений для какой-то сущности. Зачастую этим конкретным числам и строкам даются понятные имена с помощью констант, что и является причиной их широкого распространения. 13 | 3. Ещё одним плохим способом использования примитивных типов является «симуляция» полей. При этом класс содержит большой массив разнообразных данных, а в роли индексов массива для получения этих данных используются строковые константы, заданные в классе. 14 | 15 | Лечение: 16 | 17 | 1. Если вы имеете множество разнообразных полей примитивных типов, возможно, некоторые из них можно логически сгруппировать и перенести в свой собственный класс. Ещё лучше, если в этот класс вы сможете перенести и поведения, связанные с этими данными. Справиться с этой проблемой поможет замена значения данных объектом. 18 | 2. Если значения этих примитивных полей используются в параметрах методов, используйте замену параметров объектом или передачу всего объекта. 19 | 3. В случаях, когда в переменных закодированы какие-то сложные данные, используйте замену кодирования типа классом, замену кодирования типа подклассами или замену кодирования типа состоянием/стратегией. 20 | 4. Если среди переменных есть массивы, используйте замену массива объектом. 21 | 22 | Выигрыш: 23 | 24 | 1. Повышает гибкость кода ввиду использования объектов вместо примитивных типов. 25 | 2. Улучшает понимание и организацию кода. Операции над определёнными данными теперь собраны в одном месте, и их не надо искать по всему коду. Теперь не нужно догадываться, зачем созданы все эти странные константы и почему поля содержатся в массиве. 26 | 3. Может вскрыть факты дублирования кода. 27 | -------------------------------------------------------------------------------- /Replace Type Code with State (Strategy) (Замена кодирования типа состоянием (стратегией)).md: -------------------------------------------------------------------------------- 1 | Замена кодирования типа состоянием/стратегией 2 | 3 | Что такое кодирование типа? Это когда вместо отдельного типа данных вы имеете набор чисел или строк, который составляет список допустимых значений для какой-то сущности. Зачастую этим конкретным числам и строкам даются понятные имена с помощью констант, что и является причиной их широкого распространения. 4 | 5 | Проблема: у вас есть закодированный тип, который влияет на поведение, но вы не можете использовать подклассы, чтобы избавиться от него. 6 | 7 | Решение: замените кодирование типа объектом-состоянием. При необходимости заменить значение поля с кодированием типа, в него подставляется другой объект-состояние. 8 | 9 | Причины рефакторинга: у нас есть кодирование типа, и оно влияет на поведение класса, поэтому мы не можем заменить кодирования типа классом. Кодирование типа влияет на поведение класса, но мы не можем создавать подклассы для закодированного типа ввиду наличия существующей иерархии классов или других причин. А значит, не можем применить замену кодирования типа подклассами. 10 | 11 | Достоинства: 12 | 13 | 1. Данный рефакторинг предоставляет выход из ситуации, когда поле с закодированным типом меняет своё значение в процессе жизни объекта. В этом случае, замена значения производится путём замены объекта-состояния, на который ссылается исходный класс. 14 | 2. Если вам потребуется добавить новое значение закодированного типа, все что придётся сделать — это добавить новый подкласс-состояние, не трогая существующий код (принцип открытости/закрытости). 15 | 16 | Недостаток: если у вас есть простой случай кодирования типа, но вы все равно применяете данный рефакторинг, то у вас появится много лишних классов. 17 | 18 | Полезные факты: 19 | 20 | 1. Данный рефакторинг может использовать реализацию одного из двух паттернов проектирования — Состояния либо Стратегии. Реализация этого рефакторинга остаётся той же, вне зависимости от того, какой из паттернов вы выберете. Тем не менее, какой паттерн стоит выбрать в вашей ситуации? 21 | 2. Вам подойдёт Стратегия, если вы пытаетесь разделить условный оператор, управляющий выбором того или иного алгоритма. 22 | 3. Однако если каждое значение закодированного типа отвечает не просто за альтернативную версию алгоритма, а за целое состояние класса, значение полей и множество других действий, вам больше подойдёт Состояние. 23 | 24 | Порядок рефакторинга: 25 | 26 | 1. Используйте «самоинкапсуляцию поля» (Self encapsulate field) для создания геттера для поля, которое содержит кодирование типа. 27 | 2. Создайте новый класс и дайте ему понятное название, соответствующее предназначению закодированного типа. Этот класс будет играть роль состояния (или стратегии). Создайте в нем абстрактный геттер закодированного поля. 28 | 3. Создайте подклассы класса-состояния для каждого значения закодированного типа. В каждом подклассе переопределите геттер закодированного поля так, чтобы он возвращал соответствующее значение закодированного типа. 29 | 4. В абстрактном классе состояния, создайте статический фабричный метод, принимающий в параметре значение закодированного типа. В зависимости от этого параметра, фабричные метод будет создавать объекты различных состояний. Для этого в его коде придётся создать большой условный оператор, но он будет единственным по завершению рефакторинга. 30 | 5. В исходном классе, поменяйте тип закодированного поля на класс-состояние. В сеттере этого поля, вызывайте фабричный метод состояния для получения новых объектов состояний. 31 | 6. Теперь, можете начинать перемещать поля и методы из суперкласса в соответствующие подклассы-состояния (при помощи спуска поля и спуска метода). 32 | 7. Когда все что можно перемещено, используйте замену условных операторов полиморфизмом, чтобы окончательно избавиться от условных операторов, использующий закодированный тип. 33 | -------------------------------------------------------------------------------- /Replace Type Code with Class (Замена кодирования типа классом).md: -------------------------------------------------------------------------------- 1 | Замена кодирования типа классом 2 | 3 | Что такое кодирование типа? Это когда вместо отдельного типа данных вы имеете набор чисел или строк, который составляет список допустимых значений для какой-то сущности. Зачастую этим конкретным числам и строкам даются понятные имена с помощью констант, что и является причиной их широкого распространения. 4 | 5 | Проблема: в классе есть поле, содержащее кодирование типа. Значения этого типа не используются в условных операторах и не влияют на поведение программы. 6 | 7 | Решение: создайте новый класс и применяйте его объекты вместо значений закодированного типа. 8 | 9 | Причины рефакторинга: 10 | 11 | 1. Одной из самых частых причин появления кодирования типа является работа с базой данных, в полях которой какая-то сложная концепция кодируется каким-то числом или строкой. Например, у вас есть класс Пользователь с полем роль_пользователя, в которой содержится информация о доступе каждого пользователя, будь то администратор, редактор или обычный пользователь. Причём эта информация кодируется в поле как символы A, E, и U соответственно. 12 | 13 | Недостатками этого подхода является то, что в сеттерах поля зачастую нет никаких проверок на то, какое значение туда приходит, что может привести к большим проблемам, если кто-то отправит в эти поля какие-то другие значения. Проблема также усугубляется тем, что для таких полей невозможно сделать проверку типов. В них можно отправить любое число или строку, что, безусловно, останется без внимания проверки типов вашей IDE и даже даст возможность программе запуститься. 14 | 15 | Достоинства: 16 | 17 | 1. Мы хотим превратить наборы значений примитивных типов, коими являются закодированные типы, в стройные классы, которые бы обладали всеми мощными свойствами ООП. 18 | 2. Заменив кодирование типа классами, мы обеспечим возможность контроля и проверки типов значений (type hinting), передаваемых в методы и поля на уровне языка программирования. Например, если раньше, при передаче значения в метод компилятор не видел разницы между вашей числовой константой и каким-то произвольным числом, то теперь при передаче данных, отличающихся чем-то от указанного класса типа, вы получите сообщения об ошибке ещё внутри вашей IDE. 19 | 3. Таким образом, мы сделаем возможным перенос кода в сами классы типа. Если вам нужно было проводить какие-то сложные манипуляции со значениями типа по всей программе, теперь этот код сможет «жить» внутри одного или нескольких классов типа. 20 | 21 | Когда нельзя применить: 22 | 23 | Если значения закодированного типа используются внутри управляющих структур (if, switch и др.), и управляют каким-то поведением класса, вам следует прибегнуть к одному из двух других рефакторингов устранения кодирования типа: 24 | 25 | - замена кодирования типа подклассами 26 | - замена кодирования типа состоянием/стратегией 27 | 28 | Порядок рефакторинга: 29 | 30 | 1. Создайте новый класс и дайте ему понятное название, соответствующее предназначению закодированного типа. Мы будем называть его класс типа. 31 | 2. В класс типа скопируйте поле, содержащее кодирование типа, и сделайте его приватным. Кроме того, создайте для этого поля геттер. Устанавливаться значение в это поле будет только из конструктора. 32 | 3. Для каждого значения закодированного типа, создайте статический метод в классе типа. Он будет создавать новый объект класса типа, соответствующий этому значению закодированного типа. 33 | 4. В исходном классе, замените тип закодированного поля на класс типа. Создавайте новый объект этого типа в конструкторе, а также в сеттере поля. Измените геттер поля так, чтобы он вызывал геттер класса типа. 34 | 5. Замените любые упоминания значений закодированного типа вызовами соответствующих статических методов класса типа. 35 | 6. Удалите константы закодированного типа из исходного класса. 36 | -------------------------------------------------------------------------------- /Dublicated Code.md: -------------------------------------------------------------------------------- 1 | Dublicated Code - дублирующий код. 2 | 3 | Симптомы и признаки: возникает, когда одно и то же выражение присутствует в двух методах одного и того же кода. 4 | 5 | Причины появления 6 | 7 | 1. В большинстве случаев дублирование возникает тогда, когда в проекте работает несколько человек, причём над разными его частями. Они работают над похожими задачами, но не знают, что коллега уже написал похожий код, который можно использовать вместо написания своего. 8 | 2. Встречается и косвенное дублирование, когда конкретные участки кода отличаются внешне, хотя и выполняют одну и ту же задачу. Такое дублирование бывает довольно сложно обнаружить и исправить. 9 | 3. В отдельных случаях дублирование создаётся намеренно. Зачастую, в спешке, когда поджимают сроки сдачи проекта. Начинающий программист видит в уже написанном коде фрагмент, выглядящий «почти так, как нужно» и не может устоять перед соблазном просто скопировать код куда-то в другое место (и так десяток раз). 10 | 4. А в самых запущенных случаях программист просто слишком ленив, чтобы избавить код от дублирования. 11 | 12 | Лечение 13 | 14 | 1. Один и тот же участок кода присутствует в двух методах одного и того же класса: необходимо применить извлечение метода и вызывать код созданного метода из обоих участков. 15 | 2. Один и тот же участок кода присутствует в двух подклассах, находящихся на одном уровне: 16 | - Необходимо применить извлечение метода для обоих классов с последующим подъёмом поля для полей, которые используются в поднятом методе. 17 | - Если общий код находится в конструкторе, следует использовать подъём тела конструктора. 18 | - Если участки кода похожи, но не совпадают полностью, нужно пользоваться созданием шаблонного метода. 19 | - Если оба метода делают одно и то же, но с помощью разных алгоритмов, можно выбрать более чёткий из этих алгоритмов и применить замещение алгоритма. 20 | 3. Дублирующийся код находится в двух разных классах: 21 | - Если эти классы не являются частью какой-то иерархии, следует использовать извлечение суперкласса, чтобы создать для интересующих классов один суперкласс, содержащий всю общую функциональность. 22 | - Если создание суперкласса нежелательно или невозможно, следует применить извлечение класса в одном классе, а затем использовать новый компонент в другом. 23 | 4. Присутствует череда условных операторов, которые исполняют один и тот же код и отличаются только условиями, следует объединить эти операторы в один с общим условием с помощью объединения условных операторов, а также применить извлечение метода, чтобы вынести это условие в отдельный метод с понятным названием. 24 | 5. Один и тот же код выполняется во всех ветках условного оператора: необходимо вынести одинаковый код за пределы условного оператора с помощью объединения дублирующихся фрагментов в условных операторах. 25 | 26 | Выигрыш 27 | 28 | 1. Объединение дублирующего кода позволяет улучшить структуру кода и уменьшить его объём. 29 | 2. Это, в свою очередь, ведёт к упрощению и удешевлению поддержки кода в будущем. 30 | 31 | Не стоит трогать, если объединение двух одинаковых участков кода может сделать код менее очевидным и понятным. 32 | 33 | --------------------------------------------------------------------------------