Utilisation de classes publiques imbriquées pour organiser les constantes

21

Je travaille sur une application avec de nombreuses constantes. Lors de la dernière révision du code, il est apparu que les constantes sont trop dispersées et devraient toutes être organisées en un seul fichier de constantes "maître". Le désaccord porte sur la façon de les organiser. La majorité pense que l'utilisation du nom constant devrait être suffisante, mais cela conduira à un code qui ressemble à ceci:

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";

Je trouve que ce type de convention de dénomination est lourd. J'ai pensé qu'il pourrait être plus facile d'utiliser une classe publique imbriquée et d'avoir quelque chose comme ceci:

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";
        ...
    }
}

Cette idée n'a pas été bien reçue car elle était "trop ​​compliquée" (je n'ai pas obtenu beaucoup de détails sur la raison pour laquelle cela pourrait être trop compliqué). Je pense que cela crée une meilleure division entre les groupes de constantes connexes et la saisie semi-automatique facilite également la recherche de celles-ci. Je n'ai jamais vu cela se faire cependant, donc je me demande si c'est une pratique acceptée ou s'il y a de meilleures raisons pour lesquelles cela ne devrait pas être fait.

FrustratedWithFormsDesigner
la source
1
J'aime plutôt l'idée. Je me suis retrouvé à faire quelque chose de similaire en C ++ quand je voulais une portée et un comportement de type énum en style C #, mais avec des valeurs significatives (je pense que l'idée est née parce que je m'étais habitué à utiliser des classes de cas à la place des énumérations dans Scala). Certes, c'était un projet personnel pour le plaisir, et non un effort d'équipe.
KChaloux

Réponses:

13

Je ne suis d'accord avec aucune des deux propositions.

Les constantes doivent être dans leurs classes pertinentes , pas dans une classe toutes constantes sous l'une ou l'autre des deux formes proposées.

Il ne devrait pas y avoir de classes / interfaces uniquement constantes.

Une classe CreditCard(pas une classe interne) doit exister. Cette classe / interface a des méthodes relatives aux cartes de crédit ainsi qu'aux constantes UI_EXPIRY_MONTHet UI_ACCOUNT_ID.

Il devrait exister une classe / interface BankAccount(pas une classe interne). Cette classe / interface a des méthodes relatives aux comptes bancaires ainsi que constantes UI_ACCOUNT_ID.

Par exemple, dans l'API Java, chaque classe / interface a ses constantes. Ils ne sont pas dans une classe / interface Constantes avec des centaines de constantes, groupées ou non en classes internes.

Par exemple, ces constantes pertinentes aux jeux de résultats se trouvent dans l'interface 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 

Cette interface a des signatures de méthode par rapport aux jeux de résultats et les classes d'implémentation implémentent ce comportement. Ce ne sont pas de simples détenteurs constants.

Ces constantes pertinentes aux actions de l'interface utilisateur se trouvent dans l'interface 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 

Les classes d'implémentation ont un comportement par rapport aux actions de l'interface utilisateur, elles ne sont pas de simples détenteurs constants.

Je connais au moins une interface "constantes" ( SwingConstants) sans méthodes mais elle n'a pas des centaines de constantes, juste quelques unes, et elles sont toutes liées aux directions et positions des éléments de l'interface utilisateur:

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 

Je pense que les classes à constantes ne sont pas une bonne conception OO.

Tulains Córdova
la source
1
Bien que je convienne que les constantes seules les classes sont dans la plupart des cas un signe de mauvaise conception, il existe des utilisations légitimes. Par exemple, la constante «n'appartient» pas toujours à une classe particulière. Les clés des extras d'intention dans la programmation Android en sont un exemple concret.
curioustechizen
1
+1, mais les constantes d'interface utilisateur ne devraient probablement pas figurer dans des modèles commerciaux tels que BankAccount. Ils appartiennent à une classe d'interface utilisateur.
kevin cline
10

il s'est avéré que les constantes sont trop dispersées et devraient toutes être organisées en un seul fichier de constantes "maître".

C'est la solution 100% erronée au problème des constantes "difficiles à trouver". C'est une erreur fondamentale (malheureusement trop souvent commise par les programmeurs) de grouper les choses par critères techniques comme les constantes, les énumérations ou les interfaces.

Hormis les architectures de couches, les choses doivent être regroupées par domaine , et les constantes d'autant plus qu'elles sont des éléments de très bas niveau. Les constantes doivent faire partie des classes ou interfaces qui les utilisent dans le cadre de leur API. Si vous ne trouvez pas un endroit naturel où vivre, vous devez nettoyer la conception de votre API.

De plus, un seul fichier de constantes énorme tue la vitesse de développement en Java car les constantes sont intégrées dans les classes qui les utilisent au moment de la compilation, donc tout changement force une recompilation de tous ces fichiers et de tout ce qui en dépend. Si vous avez un fichier avec des constantes qui sont utilisées partout, vous perdez largement l'avantage de la compilation incrémentielle: regardez Eclipse se bloquer pendant 15 minutes car il recompile tout votre projet pour chaque modification de ce fichier. Et puisque ce fichier contient toutes les constantes utilisées n'importe où, vous le changerez assez souvent. Oui, je parle d'expérience personnelle.

Michael Borgwardt
la source
Je n'étais pas conscient de l'impact potentiel sur l'environnement de développement, et je suppose que l'approche de classe imbriquée n'y aidera pas beaucoup, non?
FrustratedWithFormsDesigner
1
@FrustratedWithFormsDesigner: Cela dépend de combien d'efforts le mécanisme de compilation incrémentielle consacre à la modélisation des dépendances.
Michael Borgwardt
9

Tu as raison. Votre suggestion permet à un programmeur deimport static Constants.BankAccount.* supprimer et d'innombrables répétitions de «BANKACCOUNT_». La concaténation est un mauvais substitut à la portée réelle. Cela dit, je n'espère pas beaucoup que vous changerez la culture d'équipe. Ils ont une notion de bonnes pratiques et ne changeront probablement pas même si vous leur montrez 100 experts qui disent qu'ils ont tort.

Je me demande également pourquoi vous avez un seul fichier "Constantes". C'est une très mauvaise pratique, à moins que ces constantes ne soient toutes liées d'une manière ou d'une autre et soient très stables. Plus généralement, il devient une sorte de classe d'évier de cuisine qui change constamment et dépend de tout.

Kevin Cline
la source
Nous avons actuellement un fichier de constantes qui contient la plupart des constantes d'ID de champ d'interface utilisateur et quelques autres fichiers de constantes spécifiques au sous-projet. Mais maintenant, les constantes spécifiques au sous-projet doivent être utilisées par d'autres sous-projets connexes, c'est donc ce qui a poussé l'idée à les fusionner toutes dans un fichier de constantes "maître". Ce qui est mieux que maintenant parce que parfois je ne sais même pas quel fichier de constantes doit être référencé pour une valeur, et quelqu'un d'autre a créé un doublon d'une constante parce qu'il n'a pas pu le trouver dans un fichier de constantes d'un sous-projet différent.
FrustratedWithFormsDesigner
8
Une classe de constantes qui change constamment ... il y a quelque chose de zen là-dedans, si vous creusez assez fort.
KChaloux
2
@FrustratedWithFormsDesigner: ID de champ d'interface utilisateur? Vous ne savez pas où trouver une valeur? Cela ressemble à un énorme tas de WTF. Vous avez ma sympathie.
Kevin Cline du
6

Les deux approches fonctionnent, et c'est ce qui est important à la fin de la journée.

Cela dit, votre approche est suffisamment courante pour être considérée comme un modèle, ou plus précisément une amélioration suffisamment bonne par rapport à l' anti-modèle d' interface constante . Je ne vois pas ce qui est compliqué, mais c'est une discussion que vous devriez avoir avec votre équipe.

yannis
la source
Pour une liste suffisamment longue de constantes, je préférerais même des interfaces constantes plutôt que de les avoir toutes dans la même classe. Il est dangereusement possible de choisir la mauvaise parmi la saisie semi-automatique. Bien sûr, cela dépend de combien de temps est assez long :)
Goran Jovic
1
@GoranJovic Eh bien chaque anti-pattern est utile d'une certaine manière (ou du moins pratique), il ne deviendrait pas un pattern s'il ne l'était pas.
yannis
1

L'un des défis de la longue liste de constantes est de trouver la constante «correcte» à utiliser. Invariablement, quelqu'un pointe une constante à la fin de la ligne au lieu de l'ajouter au groupe auquel il appartenait.

Ce qui soulève un autre point à discuter avec votre équipe - il existe déjà une catégorisation de facto des constantes à travers leurs noms. Donc, cela ne devrait vraiment pas être un grand changement pour eux de l'état actuel à ce que vous proposez. L'utilisation de la saisie semi-automatique rend les recherches constantes encore plus faciles.

Si quoi que ce soit, votre suggestion aide à décourager l'utilisation de la «mauvaise» constante, même si elle peut s'adapter à une situation particulière. En enveloppant les constantes dans une classe représentative, cela encouragera les développeurs à réfléchir s'ils devraient l'utiliser. Par exemple, si je tire CreditCard.SomeConstantpour un usage général, je me demande vraiment pourquoi il n'y en a pas General.SomeConstantpour cette situation à la place.

J'ai été sur des projets avec des quantités énormes de constantes, et j'aurais préféré avoir la méthode que vous proposez. C'est plus propre, plus direct et cela permet d'éviter une utilisation par inadvertance.


la source
1

Je le fais de temps en temps (en C #), en particulier si j'ai besoin de programmer contre / analyser une structure XML bien connue et fixe ou quelque chose de même imbriqué et une chaîne lourde ... par exemple un analyseur de bloc de configuration personnalisé.

Je construis une structure de classe imbriquée correspondant à la structure hiérarchique du XML avec les noms de classe correspondant aux éléments contenant une chaîne de const "ElementName" et une propriété similaire correspondant à chaque attribut (le cas échéant).

Il est très facile de programmer contre le Xml correspondant.

Ed Hastings
la source