Me topé con una entrada de blog que desalienta el uso de Strings en Java para hacer que su código carezca de semántica, sugiriendo que en su lugar debería usar clases de envoltura delgada. Este es el antes y el después de los ejemplos que proporciona dicha entrada para ilustrar el asunto:
public void bookTicket(
String name,
String firstName,
String film,
int count,
String cinema);
public void bookTicket(
Name name,
FirstName firstName,
Film film,
Count count,
Cinema cinema);
En mi experiencia leyendo blogs de programación, he llegado a la conclusión de que el 90% no tiene sentido, pero me pregunto si este es un punto válido. De alguna manera no me parece bien, pero no pude precisar exactamente qué está mal con el estilo de programación.
Respuestas:
La encapsulación está ahí para proteger su programa contra cambios . ¿Va a cambiar la representación de un Nombre? Si no, estás perdiendo el tiempo y aplica YAGNI.
Editar: He leído la publicación del blog y tiene una idea fundamentalmente buena. El problema es que lo ha escalado demasiado. Algo así
String orderId
es realmente malo, porque presumiblemente"!"£$%^&*())ADAFVF
no es válidoorderId
. Esto significa queString
representa muchos más valores posibles que valores válidosorderId
. Sin embargo, para algo así comoname
, entonces no es posible predecir qué podría o no ser un nombre válido, y cualquieraString
es válidoname
.En primera instancia, está (correctamente) reduciendo las posibles entradas a solo las válidas. En la segunda instancia, no ha logrado reducir las posibles entradas válidas.
Editar de nuevo: considere el caso de una entrada no válida. Si escribes "Gareth Gobulcoque" como tu nombre, se verá tonto, no será el fin del mundo. Si ingresa un OrderID no válido, es probable que simplemente no funcione.
fuente
Name
, no puede pasar accidentalmente otro valor de cadena no relacionado. Donde esperas unName
, soloName
se compilará un, y la Cadena "Te debo $ 5" no puede aceptarse accidentalmente. (¡Tenga en cuenta que esto no está relacionado con ningún tipo de validación de nombre!)Eso es una locura :)
fuente
var p = new Point(x: 10, y:20);
Además, no es que Cinema sea tan diferente de una cadena. Entendería si se tratara de cantidades físicas, como presión, temperatura, energía, donde las unidades difieren y algunas no pueden ser negativas. El autor del blog necesita probar funcional.Estoy de acuerdo con el autor, en su mayoría. Si hay algún comportamiento peculiar de un campo, como la validación de una identificación de pedido, entonces crearía una clase para representar ese tipo. Su segundo punto es aún más importante: si tiene un conjunto de campos que representan algún concepto como una dirección, cree una clase para ese concepto. Si está programando en Java, está pagando un alto precio por la escritura estática. También puede obtener todo el valor que pueda.
fuente
No lo hagas; complicará demasiado las cosas y no lo vas a necesitar
... es la respuesta que habría escrito aquí hace 2 años. Ahora, sin embargo, no estoy tan seguro; de hecho, en los últimos meses he comenzado a migrar código antiguo a este formato, no porque no tenga nada mejor que hacer, sino porque realmente lo necesitaba para implementar nuevas funciones o cambiar las existentes. Entiendo las aversiones automáticas que otros aquí tienen al ver ese código, pero creo que esto es algo que merece una reflexión seria.
Beneficios
El principal de los beneficios es la capacidad de modificar y extender el código. Si utiliza
en lugar de simplemente pasar un par de enteros, que es algo que, desafortunadamente, muchas interfaces hacen, entonces se vuelve mucho más fácil agregar luego otra dimensión. O cambie el tipo a
double
. Si usaList<Author> authors
o en suList<Person> authors
lugarList<String> authors
más tarde se vuelve mucho más fácil agregar más información a lo que representa un autor. Al escribirlo de esta manera, siento que estoy diciendo lo obvio, pero en la práctica he sido culpable de usar cuerdas de esta manera muchas veces, especialmente en los casos en que no era obvio al principio, entonces necesitaría más de una cuerda.Actualmente estoy tratando de refactorizar alguna lista de cadenas que se entrelaza a lo largo de mi código porque necesito más información allí, y siento el dolor: \
Más allá de eso, estoy de acuerdo con el autor del blog en que contiene más información semántica , lo que facilita la comprensión del lector. Si bien los parámetros a menudo reciben nombres significativos y obtienen una línea de documentación dedicada, este no suele ser el caso con los campos o los locales.
El beneficio final es la seguridad de tipo , por razones obvias, pero en mi opinión es algo menor aquí.
Inconvenientes
Se tarda más en escribir . Escribir una clase pequeña es rápido y fácil, pero no es fácil, especialmente si necesita muchas de estas clases. Si te detienes cada 3 minutos para escribir una nueva clase de envoltura, también puede ser un verdadero perjuicio para tu concentración. Sin embargo, me gustaría pensar que este estado de esfuerzo generalmente solo ocurrirá en la primera etapa de la escritura de cualquier código; Por lo general, puedo tener una idea bastante rápida de qué entidades necesitarán participar.
Puede involucrar a muchos setters (o construcciones) redundantes y getters . El autor del blog da el ejemplo realmente feo de en
new Point(x(10), y(10))
lugar denew Point(10, 10)
, y me gustaría agregar que un uso también podría involucrar cosas como enMath.max(p.x.get(), p.y.get())
lugar deMath.max(p.x, p.y)
. Y el código largo a menudo se considera más difícil de leer, y con justicia. Pero con toda honestidad, tengo la sensación de que mucho código mueve objetos, y solo los métodos seleccionados lo crean, y aún menos necesitan acceso a sus detalles arenosos (que de todos modos no son OOPy).Discutible
Diría que si esto ayuda o no con la legibilidad del código es discutible. Sí, más información semántica, pero código más largo. Sí, es más fácil comprender el papel de cada local, pero es más difícil entender qué puede hacer con él a menos que vaya y lea su documentación.
Como con la mayoría de las otras escuelas de pensamiento de programación, creo que no es saludable llevar esto al extremo. No me veo separando las coordenadas x e y para ser de un tipo diferente. No creo que
Count
sea necesario cuandoint
debería ser suficiente. No me gusta elunsigned int
uso en C, aunque en teoría es bueno, simplemente no le brinda suficiente información y prohíbe extender su código más tarde para admitir ese -1 mágico. A veces necesitas simplicidad.Creo que la publicación del blog es un poco extrema. Pero, en general, aprendí por experiencia dolorosa que la idea básica detrás de esto está hecha de las cosas correctas.
Tengo una profunda aversión al código sobredimensionado. Realmente lo hago Pero bien usado, no creo que esto sea una ingeniería excesiva.
fuente
Aunque es una especie de exageración, a menudo creo que la mayoría de las cosas que he visto no están diseñadas.
No es solo "seguridad", una de las cosas realmente buenas de Java es que te ayuda mucho a recordar / descifrar qué necesita / espera cualquier llamada a un método de biblioteca.
La PEOR (con mucho) biblioteca de Java con la que he trabajado fue escrita por alguien muy aficionado a Smalltalk y modeló la biblioteca GUI después de ella para que actuara más como smalltalk; el problema es que cada método tomó el mismo objeto base, pero en realidad no podía UTILIZAR todo a lo que se podía lanzar el objeto base, así que volviste a adivinar qué pasar a los métodos y no sabías si había fallado hasta el tiempo de ejecución (Algo con lo que solía lidiar cada vez que estaba trabajando en C).
Otro problema: si pasa cadenas, entradas, colecciones y matrices sin objetos, todo lo que tiene son bolas de datos sin significado. Esto parece natural cuando piensa en términos de bibliotecas que usará "alguna aplicación", pero al diseñar una aplicación completa es mucho más útil asignar significado (código) a todos sus datos en el lugar donde se definen los datos y solo pensar en términos de cómo interactúan estos objetos de alto nivel. Si está pasando primitivas en lugar de objetos, entonces, por definición, está alterando los datos en un lugar diferente de donde está definido (Esta es también la razón por la que realmente no me gustan los Setters y Getters - mismo concepto, usted está operando en datos que no son tuyos).
Finalmente, si define objetos separados para todo, siempre tiene un excelente lugar para validar todo, por ejemplo, si crea un objeto para el código postal y luego descubre que debe asegurarse de que el código postal siempre incluya la extensión de 4 dígitos que tiene un Lugar perfecto para ponerlo.
En realidad no es una mala idea. Pensando en ello, ni siquiera estoy seguro de decir que fue sobre-diseñado en absoluto, es más fácil trabajar con él en casi todos los sentidos, la única excepción es la proliferación de clases pequeñas, pero las clases Java son tan ligeras y fáciles escribir que esto apenas es un costo (incluso se pueden generar).
Me interesaría mucho ver un proyecto de Java bien escrito que en realidad tuviera demasiadas clases definidas (donde eso dificultara la programación), estoy empezando a pensar que no es posible tener demasiadas clases.
fuente
Creo que hay que mirar este concepto desde otro punto de partida. Eche un vistazo desde la perspectiva de un diseñador de bases de datos: los tipos pasados en el primer ejemplo no definen sus parámetros de una manera única, y mucho menos de una manera útil.
Se necesitan dos parámetros para especificar el usuario real que está reservando boletos, puede tener dos películas diferentes con nombres idénticos (por ejemplo, remakes), puede tener la misma película con nombres diferentes (por ejemplo, traducciones). Una cierta cadena de salas de cine podría tener diferentes sucursales, así que ¿cómo se va a tratar con el de una cadena y de una manera consistente (por ejemplo, está usando
$chain ($city)
o$chain in $city
incluso algo más y cómo se va a asegurarse de que se trata usado de manera consistente. Lo peor en realidad es especificar su usuario mediante dos parámetros, el hecho de que se proporcionen tanto un nombre como un apellido no garantiza un cliente válido (y no puede distinguir dosJohn Doe
).La respuesta a esto es declarar tipos, pero estos raramente serán envoltorios delgados como lo muestro arriba. Lo más probable es que funcionen como su almacenamiento de datos o se combinen con algún tipo de base de datos. Por lo tanto,
Cinema
es probable que un objeto tenga un nombre, una ubicación, ... y de ese modo se eliminen esas ambigüedades. Si son envoltorios delgados, son por coincidencia.Entonces, en mi humilde opinión, la publicación del blog solo dice "asegúrese de pasar los tipos correctos", su autor acaba de tomar la decisión demasiado restringida de elegir tipos de datos básicos en particular (que es el mensaje equivocado).
La alternativa propuesta es mejor:
Por otro lado, creo que la publicación del blog va demasiado lejos al envolver todo.
Count
es demasiado genérico, podría contar manzanas o naranjas con eso, agregarlas y aún tener una situación en mis manos en la que el sistema de tipos me permite realizar operaciones sin sentido. Por supuesto, puede aplicar la misma lógica que en el blog y definir tiposCountOfOranges
, etc., pero eso también es una tontería.Por lo que vale, en realidad escribiría algo como
Larga historia corta: no deberías pasar variables sin sentido; las únicas veces que realmente especificaría un objeto con un valor que no determina el objeto real, es cuando ejecuta una consulta (por ejemplo
public Collection<Film> findFilmsWithTitle(String title)
) o cuando está elaborando una prueba de concepto. Mantenga su sistema de tipos limpio, así que no use un tipo que sea demasiado general (por ejemplo, una película representada por aString
) o demasiado restrictivo / específico / artificial (por ejemplo, enCount
lugar deint
). Utilice un tipo que defina su objeto de manera única e inequívoca siempre que sea posible y viable.editar : un resumen aún más corto. Para aplicaciones pequeñas (por ejemplo, prueba de conceptos): ¿por qué molestarse con un diseño complicado? Solo usa
String
oint
y sigue adelante.Para aplicaciones grandes: ¿es realmente probable que tenga muchas clases que consisten en un solo campo con un tipo de datos básico? Si tienes pocas clases de este tipo, solo tienes objetos "normales", no pasa nada especial allí.
Siento que la idea de encapsular cadenas, ... es solo un diseño que está incompleto: demasiado complicado para aplicaciones pequeñas, no lo suficientemente completo para aplicaciones grandes.
fuente
Para mí, hacer esto es lo mismo que usar regiones en C #. En general, si cree que esto es necesario para que su código sea legible, entonces tiene problemas más grandes en los que debería dedicar su tiempo.
fuente
Yo diría que es una muy buena idea en un lenguaje con una característica tipo typedef fuertemente tipada.
En Java no tiene esto, por lo que crear una clase completamente nueva para estas cosas significa que el costo probablemente supere el beneficio. También puede obtener el 80% del beneficio al tener cuidado con su nombre de variable / parámetro.
fuente
Sería bueno que IF String (end Integer, y ... hablar solo de String) no fuera definitivo, por lo que estas clases podrían ser algunas String (restringidas) con significado y aún podrían enviarse a algún objeto independiente que sepa cómo manejar el tipo base (sin conversar de un lado a otro).
Y "goodnes" aumenta cuando hay, por ejemplo. restricciones en todos los nombres.
Pero al crear alguna aplicación (no biblioteca), siempre es posible refactorizarla. Así que prefiero comenzar sin él.
fuente
Para poner un ejemplo: en nuestro sistema desarrollado actualmente, hay muchas entidades diferentes que pueden identificarse mediante múltiples tipos de identificadores (debido al uso de sistemas externos), a veces incluso el mismo tipo de entidad. Todos los identificadores son cadenas, por lo que si alguien mezcla qué tipo de identificación debe pasarse como parámetro, no se muestra ningún error de tiempo de compilación, pero el programa explotará en tiempo de ejecución. Esto sucede con bastante frecuencia. Así que tengo que decir que el objetivo principal de este principio no es proteger contra el cambio (aunque también sirve para eso), sino protegernos de los errores. Y de todos modos, si alguien diseña una API, debe reflejar los conceptos del dominio, por lo que es conceptualmente útil definir clases específicas del dominio; todo depende de si hay un orden en la mente de los desarrolladores,
fuente