Usar clases públicas anidadas para organizar constantes

21

Estoy trabajando en una aplicación con muchas constantes. En la última revisión de código, se descubrió que las constantes están demasiado dispersas y que todas deberían organizarse en un único archivo de constantes "maestro". El desacuerdo se trata de cómo organizarlos. La mayoría siente que usar el nombre constante debería ser lo suficientemente bueno, pero esto conducirá a un código similar al siguiente:

public static final String CREDITCARD_ACTION_SUBMITDATA = "6767";
public static final String CREDITCARD_UIFIELDID_CARDHOLDER_NAME = "3959854";
public static final String CREDITCARD_UIFIELDID_EXPIRY_MONTH = "3524";
public static final String CREDITCARD_UIFIELDID_ACCOUNT_ID = "3524";
...
public static final String BANKPAYMENT_UIFIELDID_ACCOUNT_ID = "9987";

Encuentro que este tipo de convención de nombres es engorroso. Pensé que podría ser más fácil usar una clase anidada pública y tener algo como esto:

public class IntegrationSystemConstants
{
    public class CreditCard
    {
        public static final String UI_EXPIRY_MONTH = "3524";
        public static final String UI_ACCOUNT_ID = "3524";
        ...
    }
    public class BankAccount
    {
        public static final String UI_ACCOUNT_ID = "9987";
        ...
    }
}

Esta idea no fue bien recibida porque era "demasiado complicada" (no obtuve muchos detalles de por qué esto podría ser demasiado complicado). Creo que esto crea una mejor división entre grupos de constantes relacionadas y el autocompletado también hace que sea más fácil encontrarlos. Sin embargo, nunca he visto esto, así que me pregunto si esta es una práctica aceptada o si hay mejores razones por las que no debería hacerse.

FrustratedWithFormsDesigner
fuente
1
Me gusta más la idea. Me encontré haciendo algo similar en C ++ cuando quería un alcance y comportamiento similar a la enumeración de estilo C #, pero con valores significativos (creo que la idea surgió porque me había acostumbrado a usar clases de casos en lugar de enumeraciones en Scala). Por supuesto, este fue un proyecto personal por diversión, y no un esfuerzo de equipo.
KChaloux

Respuestas:

13

No estoy de acuerdo con ninguna de las dos propuestas.

Las constantes deben estar en sus clases pertinentes , no en una clase completamente constante en ninguna de las dos formas propuestas.

No debería haber clases / interfaces solo constantes.

CreditCardDebe existir una clase (no una clase interna). Esta clase / interfaz tiene métodos relativos a las tarjetas de crédito, así como a las constantes UI_EXPIRY_MONTHy UI_ACCOUNT_ID.

Debe existir una clase / interfaz BankAccount(no una clase interna). Esta clase / interfaz tiene métodos relativos a las cuentas bancarias, así como constantes UI_ACCOUNT_ID.

Por ejemplo, en la API de Java, cada clase / interfaz tiene sus constantes. No están en una clase / interfaz Constantes con cientos de constantes, ya sea agrupadas en clases internas o no.

Por ejemplo, estas constantes pertinentes a los conjuntos de resultados están en la interfaz ResultSet:

static int  CLOSE_CURSORS_AT_COMMIT 
static int  CONCUR_READ_ONLY 
static int  CONCUR_UPDATABLE 
static int  FETCH_FORWARD 
static int  FETCH_REVERSE 
static int  FETCH_UNKNOWN 
static int  HOLD_CURSORS_OVER_COMMIT 
static int  TYPE_FORWARD_ONLY 
static int  TYPE_SCROLL_INSENSITIVE 
static int  TYPE_SCROLL_SENSITIVE 

Esta interfaz tiene firmas de método relativas a los conjuntos de resultados, y la implementación de clases implementa ese comportamiento. No son simples titulares constantes.

Estas constantes pertinentes a las acciones de la interfaz de usuario están en la interfaz javax.swing.Action:

static String   ACCELERATOR_KEY 
static String   ACTION_COMMAND_KEY 
static String   DEFAULT 
static String   LONG_DESCRIPTION 
static String   MNEMONIC_KEY 
static String   NAME 
static String   SHORT_DESCRIPTION 
static String   SMALL_ICON 

Las clases de implementación tienen un comportamiento relativo a las acciones de la interfaz de usuario, no son simples titulares constantes.

Conozco al menos una interfaz de "constantes" ( SwingConstants) sin métodos, pero no tiene cientos de constantes, solo unas pocas, y todas están relacionadas con direcciones y posiciones de elementos de la interfaz de usuario:

static int  BOTTOM 
static int  CENTER 
static int  EAST 
static int  HORIZONTAL 
static int  LEADING 
static int  LEFT 
static int  NEXT 
static int  NORTH 
static int  NORTH_EAST 
static int  NORTH_WEST 
static int  PREVIOUS 
static int  RIGHT 
static int  SOUTH 
static int  SOUTH_EAST 
static int  SOUTH_WEST 
static int  TOP 
static int  TRAILING 
static int  VERTICAL 
static int  WEST 

Creo que las clases de constantes solo no son un buen diseño OO.

Tulains Córdova
fuente
1
Si bien estoy de acuerdo en que las clases de constantes solo son en la mayoría de los casos un signo de mal diseño, hay usos legítimos. Por ejemplo, la constante no siempre "pertenece" a una clase en particular. Las claves para Intent extras en la programación de Android son un ejemplo concreto.
curioustechizen
1
+1, pero las constantes de la interfaz de usuario probablemente no deberían estar en modelos comerciales como BankAccount. Pertenecen a alguna clase de UI.
Kevin Cline
10

surgió que las constantes están demasiado dispersas y deben organizarse en un solo archivo de constantes "maestro".

Esta es la solución 100% incorrecta al problema de las constantes que son "difíciles de encontrar". Es un error fundamental (desafortunadamente cometido con demasiada frecuencia por los programadores) agrupar cosas por criterios técnicos como ser constantes, enumeraciones o interfaces.

Excepto por las arquitecturas de capa, las cosas deben agruparse por su dominio y sus constantes, ya que son elementos de muy bajo nivel. Las constantes deben ser parte de las clases o interfaces que las usan como parte de su API. Si no puede encontrar un lugar natural para que vivan, debe limpiar su diseño de API.

Además, un solo archivo de constantes enormes mata la velocidad de desarrollo en Java porque las constantes están integradas en las clases que las usan en tiempo de compilación, por lo que cualquier cambio obliga a una recopilación de todos esos archivos y todo lo que depende de ellos. Si tiene un archivo con constantes que se usan en todas partes, pierde en gran medida el beneficio de la compilación incremental: observe el bloqueo de Eclipse durante 15 minutos, ya que recompila todo su proyecto para cada cambio en ese archivo. Y dado que ese archivo contiene todas las constantes utilizadas en cualquier lugar, lo cambiará con bastante frecuencia. Sí, estoy hablando por experiencia personal.

Michael Borgwardt
fuente
No estaba al tanto del impacto potencial en el entorno de desarrollo, y supongo que el enfoque de clase anidada no ayudará mucho con eso, ¿verdad?
FrustratedWithFormsDesigner
1
@FrustratedWithFormsDesigner: depende del esfuerzo que el mecanismo de compilación incremental dedique a las dependencias de modelado.
Michael Borgwardt
9

Tienes razón. Su sugerencia permite que un programador import static Constants.BankAccount.*elimine innumerables repeticiones de 'BANKACCOUNT_'. La concatenación es un pobre sustituto del alcance real. Dicho esto, no tengo muchas esperanzas de que cambies la cultura del equipo. Tienen una noción de prácticas adecuadas, y no es probable que cambien incluso si les muestra 100 expertos que dicen que están equivocados.

También me pregunto por qué tiene un solo archivo 'Constantes'. Esta es una práctica muy mala, a menos que estas constantes estén relacionadas de alguna manera y sean muy estables. Más típicamente se convierte en una especie de clase de fregadero que cambia constantemente y depende de todo.

Kevin Cline
fuente
Actualmente tenemos un archivo de Constantes que contiene la mayoría de las constantes de ID de campo de UI y un par de otros archivos de constantes que son específicos de subproyecto. Pero ahora, las constantes específicas del subproyecto deben ser utilizadas por otros subproyectos relacionados, de modo que eso fue lo que llevó a la idea de fusionarlas todas en un archivo de constantes "maestro". Lo cual es mejor que ahora porque a veces ni siquiera sé qué archivo de constantes se debe hacer referencia para un valor, y alguien más creó un duplicado de una constante porque no pudieron encontrarlo en un archivo de constantes de un subproyecto diferente.
FrustratedWithFormsDesigner
8
Una clase de constantes que cambia constantemente ... hay algo zen allí, si cavas lo suficiente.
KChaloux
2
@FrustratedWithFormsDesigner: ¿ID de campo de IU? ¿No sabes dónde encontrar un valor? Suena como una gran pila de WTF. Tienes mi simpatía
Kevin Cline
6

Ambos enfoques funcionan, y eso es lo importante al final del día.

Dicho esto, su enfoque es lo suficientemente común como para ser considerado un patrón, o más exactamente, una mejora lo suficientemente buena sobre el constante anti-patrón de interfaz . No veo lo que es complicado, pero esa es una discusión que debería tener con su equipo.

Yannis
fuente
Para una lista lo suficientemente larga de constantes, incluso preferiría interfaces constantes a tenerlas todas en la misma clase. Es peligrosamente posible elegir el incorrecto de autocompletar. Por supuesto, eso depende de cuánto tiempo sea suficiente :)
Goran Jovic
1
@GoranJovic Bueno, cada antipatrón es útil de alguna manera (o al menos conveniente), no se convertiría en un patrón si no lo fuera.
Yannis
1

Uno de los desafíos con la larga lista de constantes es encontrar la constante "correcta" para usar. Invariablemente, alguien agrega una constante al final de la línea en lugar de agregarla al grupo al que pertenecía.

Lo que trae a colación otro punto para discutir con su equipo: ya existe una categorización de facto de las constantes a través de sus nombres. Por lo tanto, realmente no debería ser un gran cambio para ellos del estado actual a lo que está proponiendo. El uso de autocompletar facilita aún más las búsquedas constantes.

En todo caso, su sugerencia ayuda a desalentar el uso de la constante "incorrecta" a pesar de que podría encajar en una situación particular. Al envolver las constantes dentro de una clase representativa, alentará a los desarrolladores a pensar si deberían usarla. Por ejemplo, si hago CreditCard.SomeConstantuso general, me pregunto por qué no hay una General.SomeConstantpara esa situación.

He estado en proyectos con cantidades monstruosas de constantes, y hubiera preferido tener el método que sugieres. Es más limpio, más directo y ayuda a evitar el uso accidental.


fuente
1

Hago esto ocasionalmente (en C #), particularmente si necesito programar contra / analizar una estructura XML bien conocida y fija o algo anidado de manera similar y una cadena pesada ... por ejemplo, un analizador de bloques de configuración personalizado.

Construyo una estructura de clase anidada que coincide con la estructura jerárquica del XML con los nombres de clase correspondientes a los elementos que contienen una propiedad const string "ElementName" y una propiedad similar correspondiente a cada atributo (si corresponde).

Hace que sea muy fácil programar contra el Xml correspondiente.

Ed Hastings
fuente