¿Nunca usa cadenas en Java? [cerrado]

72

Me topé con un entrada de blog desalentar el uso de cadenas en Java para hacer que su código carezca de semántica, lo que sugiere que debería usar clases de envoltura delgada en su lugar. Estos son los ejemplos de antes y después 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, llegué 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 correcto, pero no pude precisar qué es lo que está mal con el estilo de programación.

    
pregunta DPM 25.01.2012 - 23:04

10 respuestas

87

La encapsulación está ahí para proteger su programa contra cambiar . ¿Va a cambiar la representación de un nombre? Si no, estás perdiendo el tiempo y se aplica YAGNI.

Edit: He leído la publicación del blog y tiene una idea fundamentalmente buena. El problema es que lo ha escalado demasiado lejos. Algo así como String orderId es realmente malo, porque presumiblemente "!"£$%^&*())ADAFVF no es un orderId válido. Esto significa que String representa muchos más valores posibles que los orderId s válidos. Sin embargo, para algo como un name , entonces no es posible predecir qué podría o no ser un nombre válido, y cualquier String es un name válido.

En la primera instancia, está (correctamente) reduciendo las entradas posibles solo a las válidas. En la segunda instancia, no ha logrado reducir las entradas válidas posibles.

Editar de nuevo: considere el caso de una entrada no válida. Si escribe "Gareth Gobulcoque" como su nombre, se verá ridículo, no será el fin del mundo. Si coloca un Id. De pedido no válido, es probable que simplemente no funcione.

    
respondido por el DeadMG 25.01.2012 - 23:10
46

Eso es una locura :)

respondido por el Makach 25.01.2012 - 23:09
23

Estoy de acuerdo con el autor, en su mayoría. Si hay un comportamiento cualquiera propio de un campo, como la validación de un ID de pedido, 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.

    
respondido por el kevin cline 26.01.2012 - 00:28
16

No lo hagas; se complicará demasiado 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 comencé a migrar el código antiguo a este formato, no porque no tengo 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í han visto ese código, pero creo que esto es algo que merece un pensamiento serio.

Beneficios

El principal de los beneficios es la capacidad de modificar y extender el código. Si usas

class Point {
    int x,y;
    // other point operations
}

en lugar de simplemente pasar un par de enteros, que es algo que, lamentablemente, hacen muchas interfaces, luego es mucho más fácil agregar otra dimensión. O cambie el tipo a double . Si usas List<Author> authors o List<Person> authors en lugar de List<String> authors , luego será mucho más fácil agregar más información a lo que representa un autor. Escribiéndolo así, parece que estoy diciendo lo obvio, pero en la práctica he sido culpable de usar cadenas de esta manera muchas veces, especialmente en los casos en que no era obvio al principio, entonces necesitaba más que una cadena.

Actualmente estoy tratando de refactorizar una 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, a menudo no es el caso de los campos o locales.

El beneficio final es 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 sin esfuerzo, especialmente si necesita muchas de estas clases. Si se encuentra deteniéndose cada 3 minutos para escribir alguna nueva clase de envoltorio, también puede ser un verdadero detrimento para su concentración. Sin embargo, me gustaría pensar que este estado de esfuerzo usualmente solo ocurrirá en la primera etapa de la escritura de cualquier pieza de código; Por lo general, puedo tener una idea bastante buena de qué entidades deberán participar.

Puede involucrar a muchos definidores (o construcciones) redundantes y captadores . El autor del blog da el ejemplo verdaderamente feo de new Point(x(10), y(10)) en lugar de new Point(10, 10) , y me gustaría agregar que un uso también podría involucrar cosas como Math.max(p.x.get(), p.y.get()) en lugar de Math.max(p.x, p.y) . Y el código largo a menudo se considera más difícil de leer, y con razón. Pero con toda honestidad, tengo la sensación de que mucho de código mueve los 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).

Debatable

Diría que es discutible si esto ayuda o no con la legibilidad del código. Sí, más información semántica, pero código más largo. Sí, es más fácil comprender el rol de cada local, pero es más difícil entender qué puede hacer con él a menos que vaya y lea su documentación.

Al igual que con la mayoría de las otras escuelas de pensamiento de programación, creo que no es saludable llevar esto al extremo. Nunca me veo a mí mismo separando las coordenadas x e y para que sean de un tipo diferente. No creo que Count sea necesario cuando un int debería ser suficiente. No me gusta el uso de unsigned int en C - aunque teóricamente es bueno, simplemente no te da suficiente información, y prohíbe extender tu código más tarde para admitir ese mágico -1. A veces necesitas simplicidad.

Creo que la publicación del blog es un poco del lado extremo. Pero en general, he aprendido de la dolorosa experiencia que la idea básica detrás de esto está hecha de lo correcto.

Tengo una profunda aversión al código sobre-diseñado. Realmente lo hago. Pero usado correctamente, no creo que esto sea una ingeniería excesiva.

    
respondido por el Oak 26.01.2012 - 22:30
5

Aunque es un poco excesivo, a menudo pienso que la mayoría de las cosas que he visto están mal diseñadas.

No es solo "Seguridad", una de las cosas realmente buenas de Java es que te ayuda mucho a recordar / averiguar qué es lo que necesita / espera cualquier método de biblioteca dado.

La biblioteca de Java de WORST (de lejos) con la que he trabajado fue escrita por alguien a quien le gustaba Smalltalk y modelaba la biblioteca de GUI para hacer que funcionara más como smalltalk: el problema era que todos los métodos tomaban lo mismo. objeto base, pero en realidad no podía UTILIZAR todo a lo que se podía convertir el objeto base, así que volviste a adivinar qué pasaría a los métodos y no sabías si habías fallado hasta el tiempo de ejecución (algo que solía tener que lidiar con cada uno de ellos). cuando trabajaba en C).

Otro problema: si pasas por alto cadenas, entradas, colecciones y matrices sin objetos, todo lo que tienes 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 ellos. términos de cómo interactúan estos objetos de alto nivel. Si está transmitiendo primitivas en lugar de objetos, entonces, por definición, está modificando los datos en un lugar diferente al que está definido (esta es la razón por la que realmente no me gustan los Setters y los Getters: el mismo concepto, usted está operando en datos que no son tuyos).

Finalmente, si define objetos separados para todo, siempre tiene un gran 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. tienes un lugar perfecto para ponerlo.

En realidad no es una mala idea. Pensando en ello, ni siquiera estoy seguro de que esté sobregrabado en absoluto, es más fácil trabajar con él en casi todos los aspectos: la única excepción es la proliferación de clases reducidas, pero las clases de Java son muy ligeras y fáciles. para escribir que esto es apenas un costo (incluso se pueden generar).

Me interesaría mucho ver un proyecto Java bien escrito que en realidad tenía demasiadas clases definidas (donde eso dificultaba la programación), estoy empezando a pensar que no es posible tener demasiadas clases. / p>     

respondido por el Bill K 26.01.2012 - 02:11
3

Creo que tienes que ver este concepto desde otro punto de partida. Eche un vistazo desde la perspectiva de un diseñador de base 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.

public void bookTicket(
  String name,
  String firstName,
  String film,
  int count,
  String cinema);

Se necesitan dos parámetros para especificar el usuario real que está reservando boletos, puede que tenga dos películas diferentes con nombres idénticos (por ejemplo, remakes), que tenga la misma película con nombres diferentes (por ejemplo, traducciones). Una determinada cadena de salas de cine puede tener sucursales diferentes, así que, ¿cómo va a lidiar con eso en una cadena y de manera coherente (por ejemplo, está utilizando $chain ($city) o $chain in $city o incluso otra cosa y cómo va?) para asegurarse de que se usa de manera consistente. Lo peor en realidad es especificar a su usuario por medio de 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 dos John Doe s) .

La respuesta a esto es declarar tipos, pero estos raramente serán envolturas delgadas como se muestra arriba. Lo más probable es que funcionen como su almacenamiento de datos o que se acoplen con algún tipo de base de datos. Por lo tanto, un objeto Cinema probablemente tenga un nombre, ubicación, ... y de esa manera usted se libra de tales ambigüedades. Si son envolturas delgadas, son por coincidencia.

Entonces, en mi humilde opinión, la publicación del blog solo dice "asegúrate de pasar los tipos correctos", su autor acaba de tomar la decisión demasiado restringida de seleccionar los tipos de datos básicos en particular (que es el mensaje incorrecto).

La alternativa propuesta es mejor:

public void bookTicket(
  Name name,
  FirstName firstName,
  Film film,
  Count count,
  Cinema cinema);

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 donde el sistema de tipos me permite realizar operaciones sin sentido. Por supuesto, puede aplicar la misma lógica que en el blog y definir los tipos CountOfOranges , etc., pero eso también es una tontería.

Para lo que valga, en realidad escribiría algo como

public Ticket bookTicket(
  Person patron,
  Film film,
  int numberOfTickets,
  Cinema cinema);

Breve historia corta: no debes pasar variables sin sentido; las únicas veces que realmente especifica 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á armando una prueba de concepto. Mantenga limpio su sistema de tipos, así que no use un tipo que sea demasiado general (por ejemplo, una película representada por String ) o demasiado restrictivo / específico / creado (por ejemplo, Count en lugar de int ). Utilice un tipo que defina su objeto de forma única y sin ambigüedades 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? Simplemente use String o int y continúe con él.

Para aplicaciones grandes: ¿es realmente probable que tenga muchas clases que consistan en un solo campo con un tipo de datos básico? Si tiene pocas clases de este tipo, solo tiene objetos "normales", no hay nada especial que suceda 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.

    
respondido por el Egon 28.01.2012 - 17:46
2

Para mí, hacer esto es lo mismo que usar regiones en C #. Generalmente, si cree que esto es necesario para que su código sea legible, entonces tiene problemas más graves en los que debería gastar su tiempo.

    
respondido por el Tom Squires 25.01.2012 - 23:21
2

Yo diría que es una muy buena idea en un lenguaje con una característica similar a typff fuertemente tipada.

En Java no tiene esto, por lo que crear una clase completamente nueva para estas cosas significa que el costo probablemente supera el beneficio. También puede obtener el 80% del beneficio si tiene cuidado con el nombre de su variable / parámetro.

    
respondido por el jk. 26.01.2012 - 09:12
0

Sería bueno, IF String (end Integer, y ... hablando solo sobre String) no fue 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 sabe cómo manejar el tipo de base (sin conversar de un lado a otro).

Y las "bondades" de esto aumentan cuando hay, por ejemplo. restricciones en todos los nombres.

Pero al crear alguna aplicación (no una biblioteca), siempre es posible refactorizarla. Así que prefiero empezar sin él.

    
respondido por el user470365 26.01.2012 - 16:38
0

Para poner un ejemplo: en nuestro sistema actualmente desarrollado, 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 se debe pasar como parámetro, no se muestra un 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 propósito principal de este principio no es proteger contra el cambio (aunque también sirve para eso), sino protegernos de los insectos. Y de todos modos, si alguien diseña una API, tiene que 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, y esto puede ser muy importante. Facilitado por el sistema de tipos!

    
respondido por el thSoft 20.04.2012 - 14:20

Lea otras preguntas en las etiquetas