Първото упражнение, което младшите програмисти често правят, включва 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. Нека останем свързани и поддържаме разговора!

Кредити

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