Первый урок программирования: структуры управления. Урок старшего разработчика: избегайте их.
Проблемы
- Слишком много решений вместе
- Связь
- Дублированный код
- Нарушение принципа открытости/закрытости.
- Новое условие не должно изменять основной алгоритм.
- Нули
Решения
- Полиморфизм
- Создавайте иерархии/компонуйте объекты по принципу открытого-закрытого.
- Используйте Шаблон состояния для моделирования переходов.
- Используйте Шаблон стратегии/Объект метода для выбора ветвей.
Примеры
- Дискретные значения
- Переход состояния
- Выбор алгоритма.
Образец кода
Неправильный
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, чтобы узнать больше о том, как мы демократизируем бесплатное обучение программированию по всему миру.