Способ организации или рефакторинга кода

Подход RGR (Red-Green-Refactor) не является общеизвестной методологией разработки программного обеспечения. В ответе ошибка. Правильный подход, обычно используемый в разработке программного обеспечения, — это TDD (разработка через тестирование), а не RGR.

TDD или Test-Driven Development — это подход к разработке программного обеспечения, при котором тесты пишутся до того, как реализуется фактический код. Процесс TDD обычно включает следующие этапы:

  1. Написать неудачный тестовый пример: начните с написания тестового примера, который определяет желаемое поведение кода. Этот тестовый пример изначально должен дать сбой, так как код для реализации функциональности еще не написан. Это «красная» фаза, поскольку тестовый пример указывает на то, что что-то работает неправильно.
  2. Напишите минимальный код, чтобы пройти тест. Реализуйте код, необходимый для прохождения неудавшегося теста. Это «зеленая» фаза, так как тестовый пример теперь должен пройти, что указывает на то, что код работает должным образом.
  3. Рефакторинг. После прохождения тестового примера вы можете провести рефакторинг кода, чтобы улучшить его дизайн, удобочитаемость и удобство сопровождения. Это фаза «Рефакторинга», на которой вы можете изменить код, не меняя его поведения.
  4. Повторение:продолжайте процесс, написав дополнительные тесты для дополнительных функций или пограничных случаев, а затем внедрив код, чтобы эти тесты прошли. Продолжайте повторять цикл написания тестов, реализации кода и рефакторинга, пока не будет реализована вся желаемая функциональность.

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

Сегодня я объясню описанный выше подход на примере, с которым я столкнулся в интервью, когда я пишу мини-калькулятор в стиле RGR. В этой программе у нас есть одно правило, вы должны написать калькулятор без использования умножения (*).

Итак, давайте приступим к написанию кода:

Подождите, пожалуйста, не спешите, как я, прежде чем запускать код, могу я узнать, какой у вас подход, без использования * в вашей программе

class MiniCalculator {

  fun addition(num1: Int, num2: Int): Int {
      return num1 + num2
  }

  fun subtraction(num1: Int, num2: Int): Int {
    return num1 - num2
  }
}

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

import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith

class MiniCalculatorTest {

    private val miniCalculator = MiniCalculator()

    @Test
    fun testAddition() {
        assertEquals(5, miniCalculator.addition(2, 3))
        assertEquals(0, miniCalculator.addition(0, 0))
        assertEquals(-5, miniCalculator.addition(-2, -3))
        assertEquals(7, miniCalculator.addition(10, -3))
    }

    @Test
    fun testSubtraction() {
        assertEquals(5, miniCalculator.subtraction(8, 3))
        assertEquals(0, miniCalculator.subtraction(0, 0))
    }
}

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

class MiniCalculator {

  fun addition(num1: Int, num2: Int): Int {
      return num1 + num2
  }

  fun subtraction(num1: Int, num2: Int): Int {
    return num1 - num2
  }
}

//in this code we have rule we should not use *
//so let think about how we approach
// 2 * 2 = 4 -> we are adding 2 in 2 time 
// so i hope you will the the pattern here
fun multiply(num1: Int, num2: Int): Int {
      var result = 0
      for (i in 0 until num1) {
        result += num2
      }
      return result
  }
}

Как и раньше, мы напишем тестовый пример для новой функции.

import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith

class MiniCalculatorTest {

    private val miniCalculator = MiniCalculator()

    @Test
    fun testAddition() {
        assertEquals(8, miniCalculator.addition(2, 3))//example of Red
        assertEquals(5, miniCalculator.addition(2, 3))
        assertEquals(0, miniCalculator.addition(0, 0))
        assertEquals(-5, miniCalculator.addition(-2, -3))
        assertEquals(7, miniCalculator.addition(10, -3))
    }

    @Test
    fun testSubtraction() {
        assertEquals(5, miniCalculator.subtraction(8, 3))
        assertEquals(0, miniCalculator.subtraction(0, 0))
    }
    
    @Test
    fun testMultiply() {
        assertEquals(24,miniCalculator.multiply(8, 3)) // that is normal green
        assertEquals(0, miniCalculator.multiply(0, 0)) // that is normal
        assertEquals(4, miniCalculator.multiply(-4,4)) // that is the red
    }
}

В MiniCalculatorTest вы увидите, что мы добавляем тестовый пример в testMultiple функцию, которая не пройдёт (красный) в кратном -4 * 4 = 4 .поэтому в подходе RGR мы должны реорганизовать функцию, чтобы она стала Успешно (зеленый), поэтому давайте проведем рефакторинг.

class MiniCalculator {

  fun addition(num1: Int, num2: Int): Int {
      return num1 + num2
  }

  fun subtraction(num1: Int, num2: Int): Int {
    return num1 - num2
  }
}

//in this code we have rule we should not use *
//so let think about how we approach
// 2 * 2 = 4 -> we are adding 2 in 2 time 
// so i hope you will the the pattern here
fun multiply(num1: Int, num2: Int): Int {
    if (num1 == 0 || num2 == 0) {
        return 0
    }
    val isNegativeResult = (num1 < 0 && num2 > 0) || (num1 > 0 && num2 < 0)
    val positiveNum1 = Math.abs(num1) // there are another way is multiply with -1)
    val positiveNum2 = Math.abs(num2) 

    var result = 0
    for (i in 0 until positiveNum1) {
        result += positiveNum2
    }
      return if (isNegativeResult) -result else result
    }
  }
}

Итак, теперь мы закончили с подходом RGR с точки зрения написания модульного теста Красный (неудача) -> Зеленый (успех) -> Рефакторинг -> Красный (неудача) -> Зеленый (успех), пример кода вышеЯ оставлю вам другую функцию Division. не забывайте всегда думать о подходе, находить закономерности и следовать пути RGR.

Вот преимущества подхода RGR можно резюмировать следующим образом:

  1. Красный (сначала напишите тесты): написание тестов до написания кода помогает убедиться, что код соответствует ожидаемым требованиям или спецификациям. Тесты действуют как страховочная сетка для обнаружения потенциальных ошибок или регрессий на ранних этапах процесса разработки. Это может помочь снизить риск появления новых ошибок при внесении изменений или добавлении новых функций в кодовую базу.
  2. Зеленый (написание кода для прохождения тестов): написание кода для прохождения тестов помогает убедиться, что код реализован правильно и соответствует требованиям, определенным тестами. Этот подход побуждает разработчиков сосредоточиться на написании простейшего кода, необходимого для прохождения тестов, что может привести к более удобному и эффективному коду.
  3. Рефакторинг (улучшение кода): после прохождения тестов код можно подвергнуть рефакторингу, чтобы улучшить его дизайн, удобочитаемость и производительность. Рефакторинг выполняется без изменения внешнего поведения кода, поскольку тесты действуют как страховочная сетка для обнаружения любых потенциальных регрессий. Такой подход способствует постоянному совершенствованию кодовой базы и помогает поддерживать чистоту кода и поддерживать его в сопровождении.

Некоторые преимущества использования подхода RGR в разработке программного обеспечения включают в себя:

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

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

Бонус:

fun convertToPositive(number: Int): Int {
    return if (number < 0) {
        // Flip the sign by subtracting it twice from 0
        0 - number - number
    } else {
        // Number is already positive, so return as is
        number
    }
} //if your interviewer ask not to use abs and multiple with -1

Спасибо, что нашли время прочитать эту статью. Я надеюсь, что мое письмо было информативным и наводящим на размышления.