Je regarde quelques projets Java open source pour entrer dans Java et je remarque que beaucoup d'entre eux ont une sorte d'interface «constantes».
Par exemple, processing.org a une interface appelée PConstants.java , et la plupart des autres classes de base implémentent cette interface. L'interface est criblée de membres statiques. Y a-t-il une raison à cette approche ou est-ce considéré comme une mauvaise pratique? Pourquoi ne pas utiliser des énumérations là où cela a du sens , ou une classe statique?
Je trouve étrange d'utiliser une interface pour permettre une sorte de pseudo «variables globales».
public interface PConstants {
// LOTS OF static fields...
static public final int SHINE = 31;
// emissive (by default kept black)
static public final int ER = 32;
static public final int EG = 33;
static public final int EB = 34;
// has this vertex been lit yet
static public final int BEEN_LIT = 35;
static public final int VERTEX_FIELD_COUNT = 36;
// renderers known to processing.core
static final String P2D = "processing.core.PGraphics2D";
static final String P3D = "processing.core.PGraphics3D";
static final String JAVA2D = "processing.core.PGraphicsJava2D";
static final String OPENGL = "processing.opengl.PGraphicsOpenGL";
static final String PDF = "processing.pdf.PGraphicsPDF";
static final String DXF = "processing.dxf.RawDXF";
// platform IDs for PApplet.platform
static final int OTHER = 0;
static final int WINDOWS = 1;
static final int MACOSX = 2;
static final int LINUX = 3;
static final String[] platformNames = {
"other", "windows", "macosx", "linux"
};
// and on and on
}
static final
n'est pas nécessaire, il est redondant pour une interface.platformNames
peut êtrepublic
,static
etfinal
, mais ce n'est certainement pas une constante. Le seul tableau constant est un tableau de longueur nulle.static final
n'est pas nécessairement redondant. Un champ de classe ou d'interface avec uniquement lefinal
mot - clé créerait des instances distinctes de ce champ lorsque vous créez des objets de la classe ou de l'interface. Utiliserstatic final
ferait en sorte que chaque objet partage un emplacement mémoire pour ce champ. En d'autres termes, si une classe MyClass avait un champfinal String str = "Hello";
, pour N instances de MyClass, il y aurait N instances du champ str en mémoire. L'ajout dustatic
mot clé ne donnerait qu'une seule instance.Réponses:
C'est généralement considéré comme une mauvaise pratique. Le problème est que les constantes font partie de "l'interface" publique (faute d'un meilleur mot) de la classe d'implémentation. Cela signifie que la classe d'implémentation publie toutes ces valeurs dans des classes externes, même lorsqu'elles ne sont requises qu'en interne. Les constantes prolifèrent dans tout le code. Un exemple est l' interface SwingConstants dans Swing, qui est implémentée par des dizaines de classes qui «réexportent» toutes ses constantes (même celles qu'elles n'utilisent pas) comme les leurs.
Mais ne me croyez pas sur parole, Josh Bloch dit aussi que c'est mauvais:
Une énumération peut être une meilleure approche. Ou vous pouvez simplement placer les constantes sous forme de champs statiques publics dans une classe qui ne peut pas être instanciée. Cela permet à une autre classe d'y accéder sans polluer sa propre API.
la source
Au lieu d'implémenter une "interface de constantes", dans Java 1.5+, vous pouvez utiliser des importations statiques pour importer les constantes / méthodes statiques d'une autre classe / interface:
Cela évite la laideur de faire en sorte que vos classes implémentent des interfaces qui n'ont aucune fonctionnalité.
Quant à la pratique d'avoir une classe juste pour stocker des constantes, je pense que c'est parfois nécessaire. Il y a certaines constantes qui n'ont tout simplement pas de place naturelle dans une classe, il est donc préférable de les avoir dans un endroit «neutre».
Mais au lieu d'utiliser une interface, utilisez une classe finale avec un constructeur privé. (Rendant impossible l'instanciation ou la sous-classe de la classe, l'envoi d'un message fort indiquant qu'elle ne contient pas de fonctionnalités / données non statiques.)
Par exemple:
la source
Je ne prétends pas avoir raison, mais voyons ce petit exemple:
ToyotaCar ne sait rien de FordCar, et FordCar ne sait rien de ToyotaCar. principe CarConstants doit être changé, mais ...
Les constantes ne doivent pas être changées, car la roue est ronde et l'égine est mécanique, mais ... À l'avenir, les ingénieurs de recherche de Toyota ont inventé le moteur électronique et les roues plates! Voyons notre nouvelle interface
et maintenant nous pouvons changer notre abstraction:
à
Et maintenant, si jamais nous avons besoin de changer la valeur fondamentale du MOTEUR ou de la ROUE, nous pouvons changer l'interface ToyotaCar au niveau de l'abstraction, ne touchez pas aux implémentations
Ce n'est PAS SÛR, je sais, mais je veux quand même savoir que pensez-vous de ça
la source
Il y a beaucoup de haine pour ce modèle en Java. Cependant, une interface de constantes statiques a parfois de la valeur. Vous devez essentiellement remplir les conditions suivantes:
Les concepts font partie de l'interface publique de plusieurs classes.
Leurs valeurs pourraient changer dans les versions futures.
Par exemple, supposons que vous écrivez une extension dans un langage de requête hypothétique. Dans cette extension, vous allez développer la syntaxe du langage avec de nouvelles opérations, qui sont prises en charge par un index. Par exemple, vous allez avoir un R-Tree prenant en charge les requêtes géospatiales.
Vous écrivez donc une interface publique avec la constante statique:
Plus tard, un nouveau développeur pense qu'il a besoin de créer un meilleur index, alors il vient et construit une implémentation R *. En implémentant cette interface dans sa nouvelle arborescence, il garantit que les différents index auront une syntaxe identique dans le langage de requête. De plus, si vous décidiez plus tard que "nearTo" était un nom déroutant, vous pourriez le changer en "withinDistanceInKm" et savoir que la nouvelle syntaxe serait respectée par toutes vos implémentations d'index.
PS: L'inspiration de cet exemple est tirée du code spatial Neo4j.
la source
Compte tenu de l'avantage du recul, nous pouvons voir que Java est cassé à bien des égards. Un échec majeur de Java est la restriction des interfaces aux méthodes abstraites et aux champs finaux statiques. Les langages OO plus récents et plus sophistiqués comme Scala subsument les interfaces par des traits qui peuvent (et le font généralement) inclure des méthodes concrètes, qui peuvent avoir une arité nulle (constantes!). Pour une exposition sur les traits en tant qu'unités de comportement composable, voir http://scg.unibe.ch/archive/papers/Scha03aTraits.pdf . Pour une brève description de la façon dont les traits de Scala se comparent aux interfaces de Java, voir http://www.codecommit.com/blog/scala/scala-for-java-refugees-part-5. Dans le contexte de l'enseignement de la conception OO, des règles simplistes comme affirmer que les interfaces ne devraient jamais inclure de champs statiques sont stupides. De nombreux traits incluent naturellement des constantes et ces constantes font de façon appropriée partie de «l'interface» publique supportée par le trait. Lors de l'écriture de code Java, il n'y a pas de moyen propre et élégant de représenter les traits, mais l'utilisation de champs finaux statiques dans les interfaces fait souvent partie d'une bonne solution de contournement.
la source
Selon la spécification JVM, les champs et les méthodes d'une interface ne peuvent avoir que Public, Static, Final et Abstract. Réf de Inside Java VM
Par défaut, toutes les méthodes de l'interface sont abstraites même si vous ne les avez pas mentionnées explicitement.
Les interfaces sont destinées à ne donner que des spécifications. Il ne peut contenir aucune implémentation. Donc, pour éviter d'implémenter des classes pour changer la spécification, elle est rendue définitive. Puisque l'interface ne peut pas être instanciée, elles sont rendues statiques pour accéder au champ en utilisant le nom de l'interface.
la source
Je n'ai pas assez de réputation pour faire un commentaire à Pleerock, je dois donc créer une réponse. Je suis désolé pour cela, mais il y a fait de gros efforts et je voudrais lui répondre.
Pleerock, vous avez créé l'exemple parfait pour montrer pourquoi ces constantes devraient être indépendantes des interfaces et indépendantes de l'héritage. Pour le client de l'application, il n'est pas important qu'il y ait une différence technique entre ces implémentations de voitures. Ce sont les mêmes pour le client, juste des voitures. Donc, le client veut les regarder de cette perspective, qui est une interface comme I_Somecar. Tout au long de l'application, le client n'utilisera qu'une seule perspective et non des perspectives différentes pour chaque marque de voiture.
Si un client souhaite comparer des voitures avant d'acheter, il peut avoir une méthode comme celle-ci:
Une interface est un contrat sur le comportement et montre différents objets d'un point de vue. La façon dont vous la concevez, chaque marque automobile aura sa propre ligne d'héritage. Bien que ce soit en réalité tout à fait correct, parce que les voitures peuvent être si différentes que cela peut être comme comparer des types d'objets complètement différents, à la fin, il y a le choix entre différentes voitures. Et c'est la perspective de l'interface que toutes les marques doivent partager. Le choix des constantes ne doit pas rendre cela impossible. Veuillez considérer la réponse de Zarkonnen.
la source
Cela est venu d'un temps avant que Java 1.5 existe et nous apporte des énumérations. Auparavant, il n'y avait pas de bon moyen de définir un ensemble de constantes ou de valeurs contraintes.
Ceci est toujours utilisé, la plupart du temps soit pour la compatibilité ascendante, soit en raison de la quantité de refactoring nécessaire pour se débarrasser, dans de nombreux projets.
la source