Навигация в кода мирише: Разкриване на проблеми и създаване на решения в проекти
Миризмите на код са фините индикатори за по-дълбоки проблеми в кодовата база, които, ако не бъдат проверени, могат да доведат до намалена поддръжка, повишена сложност и потенциални грешки.
В проектите на Kotlin разпознаването и справянето с тези миризми на код е от първостепенно значение за производството на чист, ефективен и поддържаем код. Тази статия изследва някои често срещани миризми на код, техните последици и стратегии за коригирането им, за да се подобри качеството на проектите на Kotlin.
Разбиране на това какво е кодови миризми: Кодовите миризми са модели в кода, които намекват за потенциални проблеми с дизайна или изпълнението. Те не се равняват непременно на грешки, а по-скоро показват области, които могат да се възползват от рефакторинг.
Някои преобладаващи кодови миризми включват дълги функции, дублиран код, големи класове, примитивна мания и неподходящи коментари. Идентифицирането на тези миризми изисква бдителност и дълбоко разбиране на принципите на добър софтуерен дизайн.
Дълги функции: Дългите функции, често наричани „функции на Бога“, изпълняват твърде много задачи в рамките на един блок. Това не само пречи на четливостта, но и прави поддръжката трудна задача. Разбиването на такива функции на по-малки, по-фокусирани единици не само подобрява четливостта, но също така подобрява повторното използване и поддръжката.
//Incorrect Example - Long Function for Wine Order: Code Smell fun placeWineOrder(customer: Customer, items: List<WineItem>) { // Validate customer if (customer.name.isEmpty() || customer.age < 18) { throw IllegalArgumentException("Invalid customer.") } // Calculate total price var totalPrice = 0.0 for (item in items) { if (item.quantity <= 0) { throw IllegalArgumentException("Invalid item quantity.") } totalPrice += item.price * item.quantity } // Apply discount if (totalPrice > 200) { totalPrice *= 0.9 } // Generate order details val orderDetails = StringBuilder() orderDetails.append("Order for ${customer.name}\n") for (item in items) { orderDetails.append("${item.quantity} x ${item.name}: $${item.price * item.quantity}\n") } orderDetails.append("Total price: $totalPrice") // Send order confirmation EmailService.sendEmail(customer.email, "Order Confirmation", orderDetails.toString()) // Update inventory for (item in items) { Inventory.updateQuantity(item, -item.quantity) } // Update customer points customer.points += (totalPrice / 10).toInt() Database.updateCustomer(customer) } //Correct Refactor - Breaking Down the Function for Wine Order fun placeWineOrder(customer: Customer, items: List<WineItem>) { validateCustomer(customer) val totalPrice = calculateTotalPrice(items) applyDiscount(totalPrice) val orderDetails = generateOrderDetails(customer, items, totalPrice) sendOrderConfirmation(customer, orderDetails) updateInventory(items) updateCustomerPoints(customer, totalPrice) } fun validateCustomer(customer: Customer) { if (customer.name.isEmpty() || customer.age < 18) { throw IllegalArgumentException("Invalid customer.") } } fun calculateTotalPrice(items: List<WineItem>): Double { return items.sumByDouble { it.price * it.quantity } } fun applyDiscount(totalPrice: Double) { if (totalPrice > 200) { totalPrice *= 0.9 } } fun generateOrderDetails(customer: Customer, items: List<WineItem>, totalPrice: Double): String { val orderDetails = StringBuilder() orderDetails.append("Order for ${customer.name}\n") for (item in items) { orderDetails.append("${item.quantity} x ${item.name}: $${item.price * item.quantity}\n") } orderDetails.append("Total price: $totalPrice") return orderDetails.toString() } fun sendOrderConfirmation(customer: Customer, orderDetails: String) { EmailService.sendEmail(customer.email, "Order Confirmation", orderDetails) } fun updateInventory(items: List<WineItem>) { for (item in items) { Inventory.updateQuantity(item, -item.quantity) } } fun updateCustomerPoints(customer: Customer, totalPrice: Double) { customer.points += (totalPrice / 10).toInt() Database.updateCustomer(customer) }
Дублиран код: Дублираният код е издайнически знак за пропуснати възможности за абстракция и повторно използване на кода. Тези повтарящи се блокове код могат да доведат до несъответствия и грешки, когато са необходими промени. Извличането на обща функционалност в отделни функции или класове насърчава последователността, намалява шансовете за грешки и улеснява усилията за поддръжка.
//Code Smell fun calculateCircleArea(radius: Double): Double { return 3.14 * radius * radius } fun calculateRectangleArea(width: Double, height: Double): Double { return 3.14 * width * height // Oops, copy-paste mistake } //Correct Refactor fun calculateCircleArea(radius: Double): Double { return 3.14 * radius * radius } fun calculateRectangleArea(width: Double, height: Double): Double { return width * height }
Големи класове: Големите класове, които се справят с множество отговорности, нарушават принципа на единната отговорност. Такива класове стават трудни за разбиране и се променят с течение на времето. Разбиването им на по-малки, фокусирани класове, придържащи се към принципа на единичната отговорност, води до по-модулен и поддържаем код.
//Long Class - WineManager class WineManager { fun addWine(wine: Wine) { // Add wine to database } fun updateWine(wine: Wine) { // Update wine details in database } fun calculateTotalRevenue() { // Calculate total revenue from all wines } fun processOrder(order: Order) { // Validate order, deduct quantities, update inventory, and send confirmation } fun generateSalesReport() { // Generate sales report with various statistics } // ... more methods related to wine management ... } //Refactored - Smaller, Focused Classes: class WineCatalog { fun addWine(wine: Wine) { // Add wine to database } fun updateWine(wine: Wine) { // Update wine details in database } } class RevenueCalculator { fun calculateTotalRevenue() { // Calculate total revenue from all wines } } class OrderProcessor { fun processOrder(order: Order) { // Validate order, deduct quantities, update inventory, and send confirmation } } class SalesReportGenerator { fun generateSalesReport() { // Generate sales report with various statistics } }
Примитивно обсебване: Примитивното обсебване включва използването на примитивни типове данни за сложни концепции на домейна, което води до липса на яснота и повишен риск от грешка. Чрез създаване на специални класове за капсулиране на концепции на домейн и тяхното поведение, като например клас Currency
, разработчиците подобряват яснотата на кода, безопасността на типа и поддръжката.
// Wrong: Using primitive types for currencies val usdAmount: Double = 100.0 val eurAmount: Double = 80.0 val exchangeRateToUSD: Double = 1.18 // Performing calculations without type safety or clarity val totalAmount: Double = usdAmount + (eurAmount * exchangeRateToUSD) println("Total amount in USD: $totalAmount") // Correct: Using a dedicated Currency class class Currency(val code: String, val exchangeRateToUSD: Double) { fun convertToUSD(amount: Double): Double { return amount * exchangeRateToUSD } } fun main() { val usd = Currency("USD", 1.0) val eur = Currency("EUR", 1.18) val amountInEur = 80.0 // Using the Currency class for conversions val amountInUSD = eur.convertToUSD(amountInEur) println("Amount in USD: $amountInUSD") }
Неподходящи коментари: Неподходящите или прекомерни коментари често показват, че кодът не се обяснява сам по себе си. Вместо да претрупвате кода с коментари, стремежът към самодокументиращ се код със смислени имена на променливи и функции прави кодовата база по-четлива и намалява необходимостта от коментари, които могат да остареят.
//Code Smell - Inappropriate Comments: class WineManager { // This is the WineManager class // It manages various wine-related functionalities // Constructor for creating a new WineManager instance constructor() { // Initialize the WineManager instance } // Method to add a wine to the catalog fun addWine(wine: Wine) { // Code to add the wine to the catalog } // Method to update the wine details // Takes a Wine object as parameter fun updateWine(wine: Wine) { // Code to update the wine details } // ... other methods with similar comments ... } //Refactored - Clearer Code, Concise Comments: class WineManager { constructor() { // Initialize the WineManager } fun addWine(wine: Wine) { // Add the wine to the catalog } fun updateWine(wine: Wine) { // Update the wine details } // ... other methods without redundant comments ... }
Дълги списъци с параметри: Функциите с твърде много параметри могат да станат объркващи и това често е знак, че функцията се опитва да направи твърде много.
//Code Smell : Long Parameter fun processWineOrder( customerId: Int, customerName: String, customerEmail: String, wines: List<Wine>, quantities: List<Int>, isPreferredCustomer: Boolean, shippingAddress: String, billingAddress: String ) { // Process the order using the provided parameters } //Refactored - Introducing a Data Class: data class WineOrder( val customer: Customer, val items: List<OrderItem>, val shippingAddress: String, val billingAddress: String ) data class Customer( val id: Int, val name: String, val email: String, val isPreferred: Boolean ) data class OrderItem( val wine: Wine, val quantity: Int ) fun processWineOrder(order: WineOrder) { // Process the order using the encapsulated details }
Стратегии за разрешаване на миризми на код: Справянето с миризмите на код изисква стратегически подход:
- Прегледи на кода: Редовните прегледи на кода с колеги могат да идентифицират миризмите на кода, които може да бъдат пренебрегнати.
- Рефакторинг: Отделете време за рефакторинг, за да премахнете систематично неприятните миризми на кода, като същевременно запазите функционалността.
- Инструменти за анализ на код: Използвайте инструменти като Detekt в процеса на изграждане, за да забележите автоматично миризми на код.
- Документация и обучение: Обучете екипа си относно миризмите на кода, техните последици и най-добрите практики за избягването им.
Миризмите на код са като указателни табели, насочващи разработчиците към по-поддържаем и ефективен код. Разпознаването на тези миризми и разбирането как да се справят с тях е жизненоважно умение за всеки разработчик.
Чрез активно идентифициране и коригиране на миризми на код, проектите могат да бъдат издигнати до нови нива на четимост, поддръжка и качество. В крайна сметка преследването на чист код е непрекъснато пътуване, при което бдителността и ангажиментът за подобряване водят до софтуер, който не само работи, но и работи добре.