Первый урок программирования: структуры управления. Урок старшего разработчика: избегайте их.

Проблемы

Решения

  1. Полиморфизм
  2. Создавайте иерархии/компонуйте объекты по принципу открытого-закрытого.
  3. Используйте Шаблон состояния для моделирования переходов.
  4. Используйте Шаблон стратегии/Объект метода для выбора ветвей.

Примеры

  • Дискретные значения
  • Переход состояния
  • Выбор алгоритма.

Образец кода

Неправильный

fun convertToMp3(source: Path, mimeType: String) =
    when(mimeType) {
        "audio/mpeg" -> convertMpegToMp3(source)
        "audio/wav" -> convertWavToMp3(source)
        "audio/ogg" -> convertOggToMp3(source)
        // Lots of new if-else cases
        else -> throw IllegalArgumentException("Unknown mime type")
    }

fun convertMpegToMp3(source: Path) = println("Convert from mpeg")
fun convertWavToMp3(source: Path) = println("Convert from wav")
fun convertOggToMp3(source: Path) = println("Convert from ogg")

Верно

val registeredConverters = mapOf(
    "audio/mpeg" to ::convertMpegToMp3,
    "audio/wav" to ::convertWavToMp3,
    "audio/ogg" to ::convertOggToMp3,
    // Lots of other converters
)

fun convertToMp3(source: Path, mimeType: String) =
    registeredConverters[mimeType]
        ?.let { converter -> converter(source) }
        ?: throw IllegalArgumentException("No converter found")

fun convertMpegToMp3(source: Path) = println("Convert from mpeg")
fun convertWavToMp3(source: Path) = println("Convert from wav")
fun convertOggToMp3(source: Path) = println("Convert from ogg")

После дальнейшего рефакторинга (с использованием объектов)

interface Mp3Converter {
    fun convertToMp3(source: Path)
}

class ConvertMpegToMp3 : Mp3Converter {
    override fun convertToMp3(source: Path) =
        println("Convert from mpeg")
}

class ConvertWavToMp3 : Mp3Converter {
    override fun convertToMp3(source: Path) =
        println("Convert from wav")
}

class ConvertOggToMp3 : Mp3Converter {
    override fun convertToMp3(source: Path) =
        println("Convert from ogg")
}

// Many more converters

val registeredConverters = mapOf(
    "audio/mpeg" to ConvertMpegToMp3(),
    "audio/wav" to ConvertWavToMp3(),
    "audio/ogg" to ConvertOggToMp3(),
    // Lots of other converters
)

fun convertToMp3(source: Path, mimeType: String) =
    registeredConverters[mimeType]
        ?.convertToMp3(source)
        ?: throw IllegalArgumentException("No converter found")

Заключение

Чрезмерное использование операторов if-else/when в Kotlin может привести к появлению запахов в коде, что затруднит его поддержку и понимание. Чтобы избежать этих проблем, важно использовать шаблоны проектирования, такие как полиморфизм, шаблоны состояния и шаблоны стратегии.

Вы почти всегда можете заменить оператор if-else/when реализацией карты.

Надеюсь, вам понравилось это путешествие и вы узнали что-то новое. Если вы хотите быть в курсе моих последних мыслей и идей, не стесняйтесь подписаться на мою информационную рассылку. Вы также можете найти меня в LinkedIn или Twitter. Оставайтесь на связи и продолжайте общение!

Кредиты

Первоначально опубликовано на https://yonatankarp.com.

Спасибо, что дочитали до конца. Пожалуйста, следите за автором и этой публикацией. Посетите Stackademic, чтобы узнать больше о том, как мы демократизируем бесплатное обучение программированию по всему миру.