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

Понимание рефакторинга кода

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

Ключевые методы рефакторинга кода Swift

Для разработчика усовершенствование кода посредством рефакторинга является важнейшим навыком. Перерабатывая и улучшая внутреннюю структуру вашего кода без изменения его внешнего поведения, вы получаете более эффективную, читаемую и поддерживаемую кодовую базу. Существует несколько методов, которые вы можете использовать для рефакторинга кода Swift. Давайте углубимся в некоторые ключевые методы рефакторинга с наглядными примерами «до» и «после».

Метод извлечения

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

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

func parseJSONAndCreateUser(data: Data) throws -> User {
   let decoder = JSONDecoder()
   let json = try decoder.decode([String: Any].self, from: data)
   guard let name = json["name"] as? String,
         let email = json["email"] as? String,
         let addressDict = json["address"] as? [String: Any],
         let street = addressDict["street"] as? String,
         let city = addressDict["city"] as? String else {
       throw NSError(domain: "", code: -1, userInfo: nil)
   }
   let address = Address(street: street, city: city)
   return User(name: name, email: email, address: address)
}

Рефакторинг кода:

func parseJSONAndCreateUser(data: Data) throws -> User {
    let json = try decodeJSON(data: data)
    let (name, email, address) = try parseUserDetails(from: json)
    return User(name: name, email: email, address: address)
}

func decodeJSON(data: Data) throws -> [String: Any] {
    let decoder = JSONDecoder()
    let json = try decoder.decode([String: Any].self, from: data)
    return json
}

func parseUserDetails(from json: [String: Any]) throws -> (String, String, Address) {
    guard let name = json["name"] as? String,
          let email = json["email"] as? String,
          let addressDict = json["address"] as? [String: Any],
          let street = addressDict["street"] as? String,
          let city = addressDict["city"] as? String else {
        throw NSError(domain: "", code: -1, userInfo: nil)
    }
    let address = Address(street: street, city: city)
    return (name, email, address)
}

Встроенный метод

Используется, когда тело метода так же ясно как и его имя. В этом случае метод не вносит дополнительной ясности и может быть вообще удален. Заменив вызов метода самим содержимым метода мы можем уменьшить ненужную косвенность.

Плохой пример:

class Order {
    var items: [Item]
    
    func hasDiscountableItems() -> Bool {
        return items.contains { $0.isDiscountable }
    }
    
    func calculateTotal() -> Double {
        if hasDiscountableItems() {
            // сложный расчет скидки
        } else {
            // обычный расчет
        }
    }
}

Рефакторинг кода:

class Order {
    var items: [Item]
    
    func calculateTotal() -> Double {
        if items.contains { $0.isDiscountable } {
            // сложный расчет скидки
        } else {
            // обычный расчет
        }
    }
}

Метод переименования

По мере развития кодовой базы и добавления новых функций, некоторые имена методов могут перестать точно отражать то, что делает этот метод. Вот тут и пригодится техника переименования. Метод предполагает изменение имени метода на новое имя, которое более точно отражает его функциональность или поведение. Основная цель — повысить ясность кода и гарантировать, что другие разработчики смогут понять назначение метода, просто прочитав его название.

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

Плохой пример:

class DataProcessor {
    func process() {
        // ...
    }
}

Рефакторинг кода:

class DataProcessor {
    func processRawDataIntoModel() {
        // ...
    }
}

Замените условия на guard

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

Плохой пример:

func process(data: Data?) {
    if let data = data {
        // данные обработки
    } else {
        // необрабатываемые данные
    }
}

Рефакторинг кода:

func process(data: Data?) {
    guard let data = data else {
        // данные обработки
        return
    }
    // необрабатываемые данные
}

Замена временной переменной запросом

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

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

Плохой пример:

class Order {
    var items: [Item]
    
    func calculateTotal() -> Double {
        let basePrice = items.reduce(0, { $0 + $1.price })
        // сложный расчет с basePrice
    }
}

Рефакторинг кода:

class Order {
    var items: [Item]
    
    var basePrice: Double {
        return items.reduce(0, { $0 + $1.price })
    }

    func calculateTotal() -> Double {
        // сложный расчет с basePrice
    }
}

Заключение

На вашем пути разработчика рефакторинг играет важную роль. Речь идет не только об очистке кода — речь идет о долговечности, масштабируемости и постоянном совершенствовании. Методы, которые мы обсуждали служат ценными инструментами в вашем наборе инструментов рефакторинга.

Каждый метод имеет свои преимущества, освоив эти методы рефакторинга, вы сможете гарантировать, что ваш код останется надежным, читаемым и поддерживаемым, даже если ваши проекты растут в размере и сложности. Помните, что рефакторинг — это не разовое мероприятие, а постоянная приверженность качеству кода и профессионализму.

Комментарии (4)


  1. valery_e378he
    20.11.2023 06:01

    Спасибо! Коротко, четко и с примерами. И для знающего полезно, а для начинающих - тем более. "Я не волшебник, я только учусь ... "


    1. Mulish Автор
      20.11.2023 06:01

      всегда рад помочь!


  1. vsting
    20.11.2023 06:01

    Длинные названия методов как processRawDataIntoModel() это хороший код?


    1. Mulish Автор
      20.11.2023 06:01
      +1

      хороший код это когда ты открываешь проект и из любой части программы понимаешь о чем идет речь, поэтому в этом случае да