Първото упражнение, което младшите програмисти често правят, включва IDE, уроци и старши разработчици, които ги учат на анти-модел.
проблеми
- Изменчивост
- Скриване на информация
- Анемични модели
- Fail Fast
- Интегритет
- Дублиран код
- Паралелно изпълнение на програмиране
Решения
- Избягвайте сетери
- Задайте основни атрибути на конструкцията на обекта.
Примерен код
погрешно
// Anemic mutable class data class PhoneCall( var origin: String? = null, var destination: String? = null, var duration: Long? = null ) fun main() { val janePhoneCall = PhoneCall() janePhoneCall.origin = "555-5555" janePhoneCall.destination = "444-4444" janePhoneCall.duration = 60 }
Мутацията носи много проблеми на вашия код.
fun main() { // Since we have a setter, we can create invalid combinations. // For example: // - We can't exchange the call destination during the call, // However, this is not enforced due to the setters' usage. val janePhoneCall = PhoneCall() janePhoneCall.origin = "555-5555" janePhoneCall.destination = "555-5555" janePhoneCall.duration = 60 } // Origin and Destination cannot be the same // To validate this, we're repeating the same code twice class PhoneCall( origin: String? = null, destination: String? = null, duration: Long? = null ) { var origin: String? = origin set(value) { if (value == origin) throw IllegalArgumentException("Destination cannot be the same as origin") field = value } var destination: String? = destination set(value) { if (value == destination) throw IllegalArgumentException("Destination cannot be the same as origin") field = value } // duration is exposed in seconds as a ripple effect // this violates information hiding principle and prevents // us from changing its representation var duration: Long? = duration set(durationInSeconds) { field = durationInSeconds } } // Moreover, multiple threads (or coroutines) can change the same // object. What is the right state of the object at the end?
вярно
class PhoneCall( val origin: String, val destination: String, val duration: Long ) { // Single control point. // We only create valid phone calls, and they remain valid // since they cannot mutate. init { if (origin == destination) throw IllegalArgumentException("Destination cannot be the same as origin") } // We're explicit about which measure of unit is used fun durationInSeconds() = duration fun durationInMilliSeconds() = duration * MILLISECONDS_IN_SECOND companion object { private const val MILLISECONDS_IN_SECOND = 1000 } }
Примери
- DTO
Изключения
- Задаването на атрибути е безопасно за несъществени атрибути. Той обаче има всички недостатъци и съображения, които вече споменахме.
Заключение
Създаването на непълни и анемични обекти е много лоша практика, която нарушава променливостта, принципа на бързия отказ и „биекциите“ в реалния свят.
Бъдете в течение с най-новите ми мисли и идеи, като се регистрирате за моя бюлетин. Свържете се с мен в LinkedIn или Twitter. Нека останем свързани и поддържаме разговора!
Кредити
- Code Smell 28 — Сетери от Максимилиано Контиери
Първоначално публикувано на https://yonatankarp.com.