Метод извлечения является наиболее часто используемым методом рефакторинга. Он реорганизует код для лучшего повторного использования и удобочитаемости.
Ах! хорошо. Давайте продолжим.
Используйте метод извлечения в следующих случаях —
Когда ваш класс содержит длинный метод.
Длинный метод делает несколько вещей и не считается связным. Метод более 10 строк можно считать максимально длинным.
Перед применением метода извлечения:
См. ниже основной метод. Он делает больше, чем одну вещь.
using System; using System.Collections.Generic; namespace NMart.Billing { internal class NMartStore { private static Dictionary<string, int> CategoryGstRatesInPercentage = new Dictionary<string, int>(); private static Dictionary<string, string> ItemsCategoryMapping = new Dictionary<string, string>(); private static void Main() { CategoryGstRatesInPercentage.Add("Food-grains", 0); CategoryGstRatesInPercentage.Add("Furniture", 5); CategoryGstRatesInPercentage.Add("Electronics", 18); CategoryGstRatesInPercentage.Add("Cosmetics", 28); ItemsCategoryMapping.Add("Rice", "Food-grains"); ItemsCategoryMapping.Add("Wheat", "Food-grains"); ItemsCategoryMapping.Add("Sofa", "Furniture"); ItemsCategoryMapping.Add("Chairs", "Furniture"); ItemsCategoryMapping.Add("TV", "Electronics"); ItemsCategoryMapping.Add("Mobile", "Electronics"); ItemsCategoryMapping.Add("Shampoo", "Cosmetics"); ItemsCategoryMapping.Add("Perfume", "Cosmetics"); Console.WriteLine("Welcome to NMart store"); Console.WriteLine("***************************"); Console.Write("Enter name of item: "); string itemName = Console.ReadLine(); Console.Write("Enter quantity of item: "); int itemQuantity = int.Parse(Console.ReadLine()); Console.Write("Enter rate per product item: "); int ratePerUnitItem = int.Parse(Console.ReadLine()); string categoryName = ""; foreach (var item in ItemsCategoryMapping) { if (item.Key == itemName) { categoryName = item.Value; break; } } int gstPercentageForItem = 0; foreach (var categoryGstRate in CategoryGstRatesInPercentage) { if (categoryGstRate.Key == categoryName) { gstPercentageForItem = categoryGstRate.Value; break; } } double finalPrice = itemQuantity * (ratePerUnitItem + ratePerUnitItem * gstPercentageForItem / 100.0); string output = "*******************************************\n" + "Billing Details for " + itemName + ":\n" + "*******************************************\n" + "Quantity: " + itemQuantity + "\nPrice per unit: " + ratePerUnitItem + "\nFinal rate: " + finalPrice; Console.WriteLine(output); Console.WriteLine("\n*********************************\n"); } } }
Настройка ставок GST, сопоставление товаров/категорий, прием пользовательских данных, расчет и т. д.
Длинный метод считается запахом кода.
После применения метода извлечения:
Чтобы избавиться от длинного метода, я разделил основной метод на небольшие методы, которые выполняют такие действия, как настройка ставок GST, сопоставление товаров/категорий, прием пользовательских данных, расчеты и так далее.
using System; using System.Collections.Generic; namespace NMart.Billing { internal class NMartStore { private static Dictionary<string, int> CategoryGstRatesInPercentage = new Dictionary<string, int>(); private static Dictionary<string, string> ItemsCategoryMapping = new Dictionary<string, string>(); public static void Main() { SetupGstRateForCategories(); SetupItemsPerCategory(); Console.WriteLine("Welcome to NMart store"); Console.WriteLine("***************************"); string itemName; int itemQuantity, ratePerUnitItem; AcceptUserInput(out itemName, out itemQuantity, out ratePerUnitItem); double finalPrice = CalculateFinalRate(itemName, itemQuantity, ratePerUnitItem); PrintBillingDetails(itemName, itemQuantity, ratePerUnitItem, finalPrice); } private static void AcceptUserInput(out string itemName, out int itemQuantity, out int ratePerUnitItem) { Console.Write("Enter name of item: "); itemName = Console.ReadLine(); Console.Write("Enter quantity of item: "); itemQuantity = int.Parse(Console.ReadLine()); Console.Write("Enter rate per product item: "); ratePerUnitItem = int.Parse(Console.ReadLine()); } private static double CalculateFinalRate(string itemName, int itemQuantity, int ratePerUnitItem) { string categoryName = GetCategory(itemName); int gstPercentageForItem = GetGstPercentage(categoryName); double gstRatePerItem = ratePerUnitItem * gstPercentageForItem / 100.0; double finalPrice = itemQuantity * (ratePerUnitItem + gstRatePerItem); return finalPrice; } private static string GetCategory(string itemName) { string categoryName = ""; foreach (var item in ItemsCategoryMapping) { if (item.Key == itemName) { categoryName = item.Value; break; } } return categoryName; } private static int GetGstPercentage(string categoryName) { int gstPercentageForItem = 0; foreach (var categoryGstRate in CategoryGstRatesInPercentage) { if (categoryGstRate.Key == categoryName) { gstPercentageForItem = categoryGstRate.Value; break; } } return gstPercentageForItem; } private static void PrintBillingDetails(string itemName, int itemQuantity, int ratePerUnitItem, double finalPrice) { string output = "*******************************************\n" + "Billing Details for " + itemName + ":\n" + "*******************************************\n" + "Quantity: " + itemQuantity + "\nPrice per unit: " + ratePerUnitItem + "\nFinal rate: " + finalPrice; Console.WriteLine(output); Console.WriteLine("\n*********************************\n"); } private static void SetupGstRateForCategories() { CategoryGstRatesInPercentage.Add("Food-grains", 0); CategoryGstRatesInPercentage.Add("Furniture", 5); CategoryGstRatesInPercentage.Add("Electronics", 18); CategoryGstRatesInPercentage.Add("Cosmetics", 28); } private static void SetupItemsForCosmetics() { ItemsCategoryMapping.Add("Shampoo", "Cosmetics"); ItemsCategoryMapping.Add("Perfume", "Cosmetics"); } private static void SetupItemsForElectronics() { ItemsCategoryMapping.Add("TV", "Electronics"); ItemsCategoryMapping.Add("Mobile", "Electronics"); } private static void SetupItemsForFoodGrains() { ItemsCategoryMapping.Add("Rice", "Food-grains"); ItemsCategoryMapping.Add("Wheat", "Food-grains"); } private static void SetupItemsForFurniture() { ItemsCategoryMapping.Add("Sofa", "Furniture"); ItemsCategoryMapping.Add("Chairs", "Furniture"); } private static void SetupItemsPerCategory() { SetupItemsForFoodGrains(); SetupItemsForFurniture(); SetupItemsForElectronics(); SetupItemsForCosmetics(); } } }
Он делится на маленькие, сплоченные, мелкие методы, которые делают одну и только одну вещь. Они хорошо передают свои намерения.
Когда вы предпочитаете комментарий, чтобы выразить цель кода.
До
После
Блок кода, для которого был написан комментарий, я извлек этот блок кода в функцию: «ParseInput».
Когда метод извлечения помогает улучшить читаемость.
Знаете ли вы, что люди извлекают метод и для одной строки? Потому что это повышает читабельность кода. Иногда заявление может быть трудно интерпретировать. В этом случае вы можете использовать метод извлечения, чтобы передать намерение, вместо того, чтобы прибегать к помощи комментариев.
Когда происходит дублирование кода или копипаст.
Рассмотрим пример ниже, это дата форматирования, а код дублируется. Возможно, строки копируются и вставляются.
До
var formattedStartDate = startDate.ToString("dd MMMM yyyy"); var formattedEndDate = endDate.ToString("dd MMMM yyyy");
Как только вы увидите копирование и вставку строк кода, приступайте к рефакторингу метода извлечения, даже если это небольшая часть кода.
После
var formattedStartDate = FormatDate(startDate); var formattedEndDate = FormatDate(endDate); public string function FormatDate(DateTime date) { return date.ToString("dd MMMM yyyy"); }
Преимущество
- Код становится более читаемым.
- Детализированные методы упрощают понимание кода.
- Вы можете легко подключать и отключать реализацию некоторых подпрограмм.
- Когда ваш метод станет целостным, вы сможете легко обнаружить ошибки в коде.
- Ваш метод вызова больше похож на предложение.
Наводит на размышления —
- Помните, чтобы метод был кратким, хорошо названным и последовательным, чтобы извлечь из него больше пользы.
- Ваш метод вызова должен больше напоминать предложения.
- При извлечении метода разработчик должен думать о связности класса. следует задать себе вопрос:Действительно ли извлеченный метод принадлежит этому классу? Если нет, то переместите его в соответствующий класс, который должен выполнять эту операцию.
- Маленькие методы действительно работают только тогда, когда у вас хорошие имена.
- Не извлекайте метод просто так, если вы не можете дать осмысленное имя.
Первоначально опубликовано на https://beingcraftsman.com 31 июля 2018 г.