¿Es “evitar el problema del yoyo” una razón válida para permitir la “obsesión primitiva”?

42

Según ¿ Cuándo la obsesión primitiva no es un código de olor? , Debería crear un objeto ZipCode para representar un código postal en lugar de un objeto String.

Sin embargo, en mi experiencia, prefiero ver

public class Address{
    public String zipCode;
}

en lugar de

public class Address{
    public ZipCode zipCode;
}

porque creo que la última requiere que me mueva a la clase ZipCode para comprender el programa.

Y creo que necesito moverme entre muchas clases para ver la definición si todos los campos de datos primitivos fueron reemplazados por una clase, que se siente como si sufriera el problema del yoyo (un antipatrón).

Entonces, me gustaría mover los métodos ZipCode a una nueva clase, por ejemplo:

Antiguo:

public class ZipCode{
    public boolean validate(String zipCode){
    }
}

Nuevo:

public class ZipCodeHelper{
    public static boolean validate(String zipCode){
    }
}

para que solo el que necesite validar el código postal dependa de la clase ZipCodeHelper. Y encontré otro "beneficio" de mantener la obsesión primitiva: mantiene la clase como su forma serializada, si la hay, por ejemplo: una tabla de direcciones con una columna de cadena zipCode.

Mi pregunta es, ¿es "evitar el problema del yo-yo" (moverse entre las definiciones de clase) una razón válida para permitir la "obsesión primitiva"?

ocomfd
fuente
99
@ jpmc26 Entonces, se sorprendería al ver lo complejo que es nuestro objeto de código postal, sin decir que es correcto, pero existe
Jared Goguen el
99
@ jpmc26, no veo cómo se pasa de "complejo" a "mal diseñado". El código complejo es a menudo el resultado de un código simple que entra en contacto con la complejidad del mundo real en lugar del mundo ideal que desearíamos que existiera. "Volviendo a la función de dos páginas. Sí, lo sé, es solo una función simple para mostrar una ventana, pero le han crecido pequeños pelos y cosas y nadie sabe por qué. Bueno, te diré por qué: esos son errores arreglos ".
Kyralessa
19
@ jpmc26: el objetivo de envolver objetos como ZipCode es la seguridad de tipos. El código postal no es una cadena, es un código postal. Si una función espera un código postal, solo debe poder pasar un código postal, no una cadena.
Davor Ždralo
44
Esto se siente altamente específico del idioma, diferentes idiomas hacen cosas diferentes aquí. @ Davor®dralo En el mismo tramo también deberíamos agregar muchos tipos numéricos. "solo enteros positivos", "solo números pares" también podrían ser todos tipos.
paul23
66
@ paul23 Sí, de hecho, y la razón principal por la que no tenemos esos es que muchos idiomas no admiten formas elegantes para definirlos. Es perfectamente razonable definir "edad" como un tipo diferente de "temperatura en grados centígrados", aunque solo sea para que "userAge == currentTemperature" se detecte como una tontería.
IMSoP

Respuestas:

116

La suposición es que no necesita ingresar a la clase ZipCode para comprender la clase Address. Si ZipCode está bien diseñado, debería ser obvio lo que hace simplemente leyendo la clase Address.

Los programas no se leen de principio a fin; por lo general, los programas son demasiado complejos para hacer esto posible. No puede tener todo el código de un programa en mente al mismo tiempo. Por lo tanto, utilizamos abstracciones y encapsulaciones para "fragmentar" el programa en unidades significativas, de modo que pueda ver una parte del programa (por ejemplo, la clase Address) sin tener que leer todo el código del que depende.

Por ejemplo, estoy seguro de que no lees el código fuente de String cada vez que te encuentras con String en el código.

Cambiar el nombre de la clase de ZipCode a ZipCodeHelper sugiere que ahora hay dos conceptos separados: un código postal y un asistente de código postal. Entonces, el doble de complejo. Y ahora el sistema de tipos no puede ayudarlo a distinguir entre una cadena arbitraria y un código postal válido, ya que tienen el mismo tipo. Aquí es donde la "obsesión" es apropiada: sugieres una alternativa más compleja y menos segura solo porque quieres evitar un tipo de envoltorio simple alrededor de un primitivo.

El uso de una primitiva está IMHO justificado en los casos en que no hay validación u otra lógica que dependa de este tipo en particular. Pero tan pronto como agregue alguna lógica, es mucho más simple si esta lógica se encapsula con el tipo.

En cuanto a la serialización, creo que suena como una limitación en el marco que está utilizando. Seguramente debería poder serializar un código postal en una cadena o asignarlo a una columna en una base de datos.

JacquesB
fuente
2
Estoy de acuerdo con la parte de "unidades significativas" (principal), pero no tanto como para que un código postal y una validación de código postal sean el mismo concepto. ZipCodeHelper(que preferiría llamar ZipCodeValidator) bien podría establecer una conexión a un servicio web para hacer su trabajo. Eso no sería parte de la responsabilidad única "mantener los datos del código postal". Hacer que el sistema de tipos no permita códigos postales no válidos aún se puede lograr haciendo que el ZipCodeconstructor sea el equivalente del paquete privado de Java y llamando eso con un ZipCodeFactoryque siempre llama al validador.
R. Schmitz
16
@ R.Schmitz: Eso no es lo que significa "responsabilidad" en el sentido del principio de responsabilidad única. Pero en cualquier caso, por supuesto, debe usar tantas clases como necesite siempre que encapsule el código postal y su validación. El OP sugiere un ayudante en lugar de encapsular el código postal, lo cual es una mala idea.
JacquesB
1
Quiero respetuosamente estar en desacuerdo. SRP significa que una clase debe tener "una, y solo una, razón para ser cambiada" (cambio en "en qué consiste un código postal" vs. "cómo se valida"). Este caso específico aquí se desarrolla en el libro Clean Code : "Los objetos ocultan sus datos detrás de abstracciones y exponen funciones que operan en esos datos. La estructura de datos expone sus datos y no tienen funciones significativas ". ZipCodeSería una "estructura de datos". y ZipCodeHelperun "objeto". En cualquier caso, creo que estamos de acuerdo en que no deberíamos tener que pasar conexiones web al constructor ZipCode.
R. Schmitz
99
El uso de una primitiva está IMHO justificado en los casos en que no hay validación u otra lógica que dependa de este tipo en particular. => No estoy de acuerdo. Incluso si todos los valores son válidos, aún preferiría transmitir la semántica al lenguaje en lugar de usar primitivas. Si se puede invocar una función en un tipo primitivo que no tiene sentido para su uso semántico actual, entonces no debería ser un tipo primitivo, debería ser un tipo apropiado con solo las funciones sensibles definidas. (Como ejemplo, usar intcomo ID permite multiplicar una ID por una ID ...)
Matthieu M.
@ R.Schmitz Creo que los códigos postales son un mal ejemplo de la distinción que está haciendo. Algo que cambia a menudo podría ser un candidato para la separación Fooy las FooValidatorclases. Nosotros podríamos tener una ZipCodeclase que valida el formato y una ZipCodeValidatorque golpea un poco de servicio Web para comprobar que un formato correcto ZipCodees en realidad actual. Sabemos que los códigos postales cambian. Pero prácticamente, vamos a tener una lista de códigos postales válidos encapsulados en ZipCode, o en alguna base de datos local.
No U
55

Si puede hacer:

new ZipCode("totally invalid zip code");

Y el constructor para ZipCode hace:

ZipCodeHelper.validate("totally invalid zip code");

Luego rompió la encapsulación y agregó una dependencia bastante tonta a la clase ZipCode. Si el constructor no llama, ZipCodeHelper.validate(...)entonces tiene lógica aislada en su propia isla sin aplicarla realmente. Puede crear códigos postales no válidos.

El validatemétodo debe ser un método estático en la clase ZipCode. Ahora el conocimiento de un código postal "válido" se agrupa con la clase ZipCode. Dado que sus ejemplos de código se parecen a Java, el constructor de ZipCode debería lanzar una excepción si se da un formato incorrecto:

public class ZipCode {
    private String zipCode;

    public ZipCode(string zipCode) {
        if (!validate(zipCode))
            throw new IllegalFormatException("Invalid zip code");

        this.zipCode = zipCode;
    }

    public static bool validate(String zipCode) {
        // logic to check format
    }

    @Override
    public String toString() {
        return zipCode;
    }
}

El constructor verifica el formato y lanza una excepción, evitando así que se creen códigos postales no válidos, y el validatemétodo estático está disponible para otro código, por lo que la lógica de verificar el formato se encapsula en la clase ZipCode.

No hay "yo-yo" en esta variante de la clase ZipCode. Simplemente se llama Programación Orientada a Objetos adecuada.


Aquí también vamos a ignorar la internacionalización, que puede requerir otra clase llamada ZipCodeFormat o PostalService (por ejemplo, PostalService.isValidPostalCode (...), PostalService.parsePostalCode (...), etc.).

Greg Burghardt
fuente
28
Nota: La principal ventaja con el enfoque de @Greg Burkhardt aquí es que si alguien le da un objeto ZipCode, puede confiar en que contiene una cadena válida sin tener que verificarlo nuevamente, ya que su tipo y el hecho de que se construyó con éxito le da Esa garantía. Si, en cambio, pasó las cadenas, puede sentir la necesidad de "afirmar validar (código postal)" en varios lugares de su código solo para asegurarse de que tiene un código postal válido, pero con un objeto ZipCode construido con éxito, puede confiar en que sus contenidos son válidos sin tener que revisarlos nuevamente.
Algún tipo el
3
@ R.Schmitz: El ZipCode.validatemétodo es la verificación previa que se puede realizar antes de invocar un constructor que arroja una excepción.
Greg Burghardt
10
@ R.Schmitz: Si le preocupa una excepción molesta, un enfoque alternativo para la construcción es hacer que el constructor ZipCode sea privado y proporcionar una función de fábrica estática pública (Zipcode.create?) Que realice la validación de los parámetros pasados, devuelve nulo si no tiene éxito, y de lo contrario construye un objeto ZipCode y lo devuelve. La persona que llama siempre tendrá que verificar un valor de retorno nulo, por supuesto. Por otro lado, si tiene la costumbre, por ejemplo, de validar siempre (regex? Validar? Etc.) antes de construir un código postal, la excepción puede no ser tan molesta en la práctica.
Algún tipo el
11
Una función de fábrica que devuelve un <ZipCode> opcional también es una posibilidad. Entonces la persona que llama no tiene más remedio que manejar explícitamente la posible falla de la función de fábrica. Independientemente, en cualquier caso, el error se descubrirá en algún lugar cerca de donde fue creado en lugar de posiblemente mucho más tarde, por un código de cliente alejado del problema original.
Algún tipo el
66
No puede validar el código postal de forma independiente, así que no lo haga. Realmente necesita el objeto País para buscar las reglas de validación de código postal / código postal.
Joshua
11

Si luchas mucho con esta pregunta, ¿quizás el lenguaje que usas no es la herramienta adecuada para el trabajo? Este tipo de "primitivas de tipo dominio" son trivialmente fáciles de expresar, por ejemplo, en F #.

Allí podría, por ejemplo, escribir:

type ZipCode = ZipCode of string
type Town = Town of string

type Adress = {
  zipCode: ZipCode
  town: Town
  //etc
}

let adress1 = {
  zipCode = ZipCode "90210"
  town = Town "Beverly Hills"
}

let faultyAdress = {
  zipCode = "12345"  // <-Compiler error
  town = adress1.zipCode // <- Compiler error
}

Esto es realmente útil para evitar errores comunes, como comparar identificaciones de diferentes entidades. Y dado que estas primitivas tipificadas son mucho más livianas que una clase C # o Java, terminarás usándolas.

Guran
fuente
Interesante: ¿cómo sería si quisieras hacer cumplir la validación ZipCode?
Hulk
44
@Hulk Puede escribir estilo OO en F # y convertir los tipos en clases. Sin embargo, prefiero el estilo funcional, declarando el tipo con tipo ZipCode = private ZipCode de cadena y agregando un módulo ZipCode con una función de creación. Aquí hay algunos ejemplos: gist.github.com/swlaschin/54cfff886669ccab895a
Guran
@ Bent-Tranberg Gracias por la edición. Tiene razón, una abreviatura de tipo simple no proporciona seguridad de tipo de tiempo de compilación.
Guran
Si le envié mi primer comentario, que eliminé, la razón fue que primero entendí mal su fuente. No lo leí con suficiente atención. Cuando intenté compilarlo, finalmente me di cuenta de que en realidad estabas tratando de demostrar exactamente esto, así que decidí editarlo para hacerlo bien.
Doblado Tranberg
Sí. Mi fuente original era válida, desafortunadamente incluyendo el ejemplo que se suponía que no era válido. Doh! Debería haber vinculado a Wlaschin en lugar de escribir el código yo mismo :) fsharpforfunandprofit.com/posts/…
Guran
6

La respuesta depende completamente de lo que realmente quieres hacer con los códigos postales. Aquí hay dos posibilidades extremas:

(1) Todas las direcciones están garantizadas en un solo país. No hay excepciones en absoluto. (Por ejemplo, no hay clientes extranjeros, ni empleados cuya dirección privada está en el extranjero mientras trabajan para un cliente extranjero). Este país tiene códigos postales y se espera que nunca sean muy problemáticos (es decir, no requieren entrada de forma libre) como "actualmente D4B 6N2, pero esto cambia cada 2 semanas"). Los códigos postales se utilizan no solo para direccionar, sino también para validar la información de pago o fines similares. - En estas circunstancias, una clase de código postal tiene mucho sentido.

(2) Las direcciones pueden estar en casi todos los países, por lo que son relevantes docenas o cientos de esquemas de direcciones con o sin códigos postales (y con miles de raras excepciones y casos especiales). Realmente solo se solicita un código "ZIP" para recordar a las personas de países donde se utilizan los códigos postales que no se olviden de proporcionar el suyo. Las direcciones solo se usan para que si alguien pierde el acceso a su cuenta y puede probar su nombre y dirección, se restablecerá el acceso. - En estas circunstancias, las clases de código postal para todos los países relevantes serían un esfuerzo enorme. Afortunadamente no son necesarios en absoluto.


fuente
3

Las otras respuestas han hablado sobre el modelado de dominio OO y el uso de un tipo más rico para representar su valor.

No estoy en desacuerdo, especialmente dado el código de ejemplo que publicaste.

Pero también me pregunto si eso realmente responde el título de su pregunta.

Considere el siguiente escenario (extraído de un proyecto real en el que estoy trabajando):

Tiene una aplicación remota en un dispositivo de campo que se comunica con su servidor central. Uno de los campos de la base de datos para la entrada del dispositivo es un código postal para la dirección en la que se encuentra el dispositivo de campo. No le importa el código postal (ni ninguna de las demás direcciones). Todas las personas que se preocupan por él están al otro lado de un límite HTTP: resulta que usted es la única fuente de verdad para los datos. No tiene lugar en su modelado de dominio. Solo debe grabarlo, validarlo, almacenarlo y, si lo solicita, mezclarlo en un blob JSON hacia otros puntos.

En este escenario, hacer mucho más que validar la inserción con una restricción de expresión regular SQL (o su equivalente ORM) es probablemente exagerado de la variedad YAGNI.

Jared Smith
fuente
66
Su restricción de expresiones regulares SQL podría verse como un tipo calificado: dentro de su base de datos, el código postal no se almacena como "VarChar" sino "VarChar limitado por esta regla". En algunos DBMS, puede asignar fácilmente un nombre a esa restricción de tipo + como "tipo de dominio" reutilizable, y estamos de vuelta en el lugar recomendado para dar a los datos un tipo significativo. Estoy de acuerdo con su respuesta en principio, pero no creo que ese ejemplo coincida; Un mejor ejemplo sería si sus datos son "datos sin procesar del sensor", y el tipo más significativo es "matriz de bytes" porque no tiene idea de lo que significan los datos.
IMSoP
Punto interesante de @IMSoP. Sin embargo, no estoy seguro de estar de acuerdo: podría validar un código postal de cadena en Java (o cualquier otro idioma) con una expresión regular pero aún así tratarlo como una cadena en lugar de un tipo más rico. Dependiendo de la lógica del dominio, es posible que se requiera una mayor manipulación (por ejemplo, garantizar que el código postal coincida con el estado, algo que sería difícil / imposible de validar con expresiones regulares).
Jared Smith
Usted podría , pero tan pronto como lo hace, usted está adscribiéndolo un comportamiento específico del dominio, y eso es exactamente lo que los artículos citados están diciendo debería conducir a la creación de un tipo personalizado. La pregunta no es si puede hacerlo de un estilo u otro, sino si debe hacerlo , suponiendo que su lenguaje de programación le dé la opción.
IMSoP
Puede modelar algo como a RegexValidatedString, que contiene la cadena en sí y la expresión regular utilizada para validarlo. Pero a menos que cada instancia tenga una expresión regular única (que es posible pero poco probable), esto parece un poco tonto y desperdicia la memoria (y posiblemente el tiempo de compilación de expresiones regulares). Por lo tanto, coloque la expresión regular en una tabla separada y deje una clave de búsqueda en cada instancia para encontrarla (lo que podría decirse que es peor debido a la indirección) o encuentra alguna forma de almacenarla una vez para cada tipo común de valor que comparte esa regla: - p.ej. un campo estático en un tipo de dominio, o método equivalente, como dijo IMSoP.
Miral
2

La ZipCodeabstracción solo podría tener sentido si su Addressclase no tuviera también una TownNamepropiedad. De lo contrario, tiene la mitad de una abstracción: el código postal designa la ciudad, pero estos dos bits de información relacionados se encuentran en diferentes clases. No tiene mucho sentido.

Sin embargo, incluso entonces, todavía no es una aplicación correcta (o más bien una solución para) la obsesión primitiva; que, según tengo entendido, se centra principalmente en dos cosas:

  1. Usar primitivas como los valores de entrada (o incluso salida) de un método, especialmente cuando se necesita una colección de primitivas.
  2. Clases que crecen propiedades adicionales con el tiempo sin volver a considerar si algunas de ellas deberían agruparse en una subclase propia.

Tu caso no es ninguno. Una dirección es un concepto bien definido con propiedades claramente necesarias (calle, número, código postal, ciudad, estado, país, ...). Hay poca o ninguna razón para dividir estos datos, ya que tiene una única responsabilidad: designar una ubicación en la Tierra. Una dirección requiere todos estos campos para ser significativa. La mitad de una dirección no tiene sentido.

Así es como sabe que no necesita subdividir más: desglosarlo más podría restar valor a la intención funcional de la Addressclase. Del mismo modo, no necesita una Namesubclase para usar en la Personclase, a menos que Name(sin una persona adjunta) sea un concepto significativo en su dominio. Lo cual (generalmente) no es. Los nombres se utilizan para identificar personas, por lo general no tienen valor por sí mismos.

Flater
fuente
1
@RikD: De la respuesta: "no es necesario Nameutilizar una subclase en la Personclase, a menos que Nombre (sin una persona adjunta) sea ​​un concepto significativo en su dominio ". Cuando tiene una validación personalizada para los nombres, el nombre se ha convertido en un concepto significativo en su dominio; que mencioné explícitamente como un caso de uso válido para usar un subtipo. En segundo lugar, para la validación del código postal, está introduciendo supuestos adicionales, como los códigos postales que necesitan seguir el formato de un país determinado. Estás abordando un tema que es mucho más amplio que la intención de la pregunta de OP.
Flater
55
" Una dirección es un concepto bien definido con propiedades claramente necesarias (calle, número, código postal, ciudad, estado, país) ". Bueno, eso es simplemente incorrecto. Para una buena manera de lidiar con esto, mire el formulario de dirección de Amazon.
R. Schmitz
44
@Flater Bueno, no te culparé por no leer la lista completa de falsedades, porque es bastante larga, pero literalmente contiene "Las direcciones tendrán una calle", "Una dirección requiere una ciudad y un país", "Una dirección tendrá un código postal "etc., que es contrario a lo que dice la oración citada.
R. Schmitz
8
@GregBurghardt "El código postal asume el Servicio Postal de los Estados Unidos y puede derivar el nombre de la ciudad a partir del código postal. Las ciudades pueden tener múltiples códigos postales, pero cada código postal solo está vinculado a 1 ciudad". Esto no es correcto en general. Tengo un código postal que se usa principalmente para una ciudad vecina, pero mi residencia no se encuentra allí. Los códigos postales no siempre se alinean con los límites gubernamentales. Por ejemplo, 42223 contiene condados de TN y KY .
JimmyJames
2
En Austria existe un valle al que solo se puede acceder desde Alemania ( en.wikipedia.org/wiki/Kleinwalsertal ). Existe un tratado especial para esta región que, entre otras cosas, también incluye que las direcciones en esta área tienen códigos postales austriacos y alemanes. Entonces, en general, ni siquiera puede suponer que una dirección tiene un solo código postal válido;)
Hulk
1

Del artículo:

En términos más generales, el problema del yoyo también puede referirse a cualquier situación en la que una persona debe seguir cambiando entre diferentes fuentes de información para comprender un concepto.

El código fuente se lee con mucha más frecuencia de lo que se escribe. Por lo tanto, el problema de yoyo, de tener que cambiar entre muchos archivos es una preocupación.

Sin embargo, no , el problema del yoyo se siente mucho más relevante cuando se trata de módulos o clases profundamente interdependientes (que se intercambian entre sí). Es un tipo especial de pesadilla para leer, y es probablemente lo que tenía en mente el entrenador del problema del yoyo.

Sin embargo, , ¡es importante evitar demasiadas capas de abstracción!

Todas las abstracciones no triviales, hasta cierto punto, tienen fugas. - La Ley de las abstracciones permeables.

Por ejemplo, no estoy de acuerdo con la suposición hecha en la respuesta de mmmaaa de que " no es necesario que yo-yo a [(visita)] la clase ZipCode para comprender la clase Dirección". Mi experiencia ha sido que lo haces , al menos las primeras veces que lees el código. Sin embargo, como otros han señalado, no son momentos en que una ZipCodeclase es apropiada.

YAGNI (Ya no lo va a necesitar) es un mejor patrón a seguir para evitar el código Lasagna (código con demasiadas capas): las abstracciones, como los tipos y las clases, están ahí para ayudar al programador, y no deben usarse a menos que sean Una ayuda.

Mi objetivo personal es "guardar líneas de código" (y, por supuesto, los "guardar archivos / módulos / clases" relacionados, etc.). Estoy seguro de que hay algunos que me aplicarían el epíteto de "obsesivo primitivo". Me resulta más importante tener un código que sea fácil de razonar que preocuparse por las etiquetas, los patrones y los antipatrones. La elección correcta de cuándo crear una función, un módulo / archivo / clase, o poner una función en una ubicación común es muy situacional. Apunto más o menos a 3-100 funciones de línea, 80-500 archivos de línea y "1, 2, n" para código de biblioteca reutilizable ( SLOC - sin incluir comentarios o repeticiones; normalmente quiero al menos 1 mínimo de SLOC adicional por línea de obligatorio repetitivo).

La mayoría de los patrones positivos han surgido de los desarrolladores que hacen exactamente eso, cuando los necesitan . Es mucho más importante aprender a escribir código legible que tratar de aplicar patrones sin el mismo problema que resolver. Cualquier buen desarrollador puede implementar el patrón de fábrica sin haberlo visto antes en el caso poco común de que sea el adecuado para su problema. He usado el patrón de fábrica, el patrón de observación y probablemente cientos además, sin saber su nombre (es decir, ¿hay un "patrón de asignación variable"?). Para un experimento divertido, vea cuántos patrones de GoF están integrados en el lenguaje JS , dejé de contar después de aproximadamente 12-15 en 2009. El patrón Factory es tan simple como devolver un objeto de un constructor JS, por ejemplo, no es necesario un WidgetFactory

Entonces, , a veces ZipCode es una buena clase. Sin embargo, no , el problema del yoyo no es estrictamente relevante.

Iiridayn
fuente
0

El problema de yoyo solo es relevante si tienes que voltear de un lado a otro. Eso es causado por una o dos cosas (a veces ambas):

  1. Mal nombre. ZipCode parece estar bien, ZoneImprovementPlanCode requerirá un vistazo por parte de la mayoría de las personas (y las pocas que no quedarán impresionadas).
  2. Acoplamiento inapropiado. Digamos que la clase ZipCode tiene una búsqueda de código de área. Puede pensar que tiene sentido porque es útil, pero en realidad no está relacionado con el código postal, y al usarlo significa que ahora la gente no sabe a dónde ir para las cosas.

Si puede mirar el nombre y tener una idea razonable de lo que hace, y los métodos y propiedades hacen cosas razonablemente obvias, no necesita mirar el código, simplemente puede usarlo. En primer lugar, ese es el objetivo de las clases: son piezas de código modulares que se pueden usar y desarrollar de forma aislada. Si tiene que mirar cualquier cosa que no sea la API para que la clase vea lo que hace, es, en el mejor de los casos, una falla parcial.

jmoreno
fuente
-1

Recuerde, no hay bala de plata. Si está escribiendo una aplicación extremadamente simple que necesita ser rastreada rápidamente, entonces una cadena simple puede hacer el trabajo. Sin embargo, en el 98% de las veces, un objeto de valor según lo descrito por Eric Evans en DDD, sería el ajuste perfecto. Puede ver fácilmente todos los beneficios que proporcionan los objetos Value al leer.

Alexios Plomaritis
fuente