Interfaces con campos estáticos en Java para compartir 'constantes'

Resuelto kitsune asked hace 16 años • 8 respuestas

Estoy analizando algunos proyectos Java de código abierto para ingresar a Java y noto que muchos de ellos tienen algún tipo de interfaz de "constantes".

Por ejemplo, Processing.org tiene una interfaz llamada PConstants.java y la mayoría de las otras clases principales implementan esta interfaz. La interfaz está plagada de miembros estáticos. ¿Existe alguna razón para este enfoque o se considera una mala práctica? ¿Por qué no utilizar enumeraciones donde tenga sentido o una clase estática?

Me resulta extraño utilizar una interfaz que permita algún tipo 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

}
kitsune avatar Nov 26 '08 19:11 kitsune
Aceptado

Generalmente se considera una mala práctica. El problema es que las constantes son parte de la "interfaz" pública (a falta de una palabra mejor) de la clase implementadora. Esto significa que la clase implementadora publica todos estos valores en clases externas incluso cuando solo se requieren internamente. Las constantes proliferan por todo el código. Un ejemplo es la interfaz SwingConstants en Swing, que está implementada por docenas de clases que "reexportan" todas sus constantes (incluso las que no usan) como propias.

Pero no confíes sólo en mi palabra, Josh Bloch también dice que es malo:

El patrón de interfaz constante es un mal uso de las interfaces. Que una clase utilice algunas constantes internamente es un detalle de implementación. La implementación de una interfaz constante hace que este detalle de implementación se filtre en la API exportada de la clase. No tiene ninguna consecuencia para los usuarios de una clase que la clase implemente una interfaz constante. De hecho, puede que incluso los confunda. Peor aún, representa un compromiso: si en una versión futura la clase se modifica para que ya no necesite usar las constantes, aún debe implementar la interfaz para garantizar la compatibilidad binaria. Si una clase no final implementa una interfaz constante, todas sus subclases tendrán sus espacios de nombres contaminados por las constantes de la interfaz.

Una enumeración puede ser un mejor enfoque. O simplemente podría poner las constantes como campos públicos estáticos en una clase de la que no se pueden crear instancias. Esto permite que otra clase acceda a ellos sin contaminar su propia API.

Dan Dyer avatar Nov 26 '2008 13:11 Dan Dyer

En lugar de implementar una "interfaz de constantes", en Java 1.5+, puede usar importaciones estáticas para importar las constantes/métodos estáticos desde otra clase/interfaz:

import static com.kittens.kittenpolisher.KittenConstants.*;

Esto evita la fealdad de hacer que tus clases implementen interfaces que no tienen funcionalidad.

En cuanto a la práctica de tener una clase sólo para almacenar constantes, creo que a veces es necesaria. Hay ciertas constantes que simplemente no tienen un lugar natural en una clase, por lo que es mejor tenerlas en un lugar "neutral".

Pero en lugar de utilizar una interfaz, utilice una clase final con un constructor privado. (Haciendo imposible crear una instancia o subclasificar la clase, enviando un mensaje contundente de que no contiene funcionalidad/datos no estáticos).

P.ej:

/** Set of constants needed for Kitten Polisher. */
public final class KittenConstants
{
    private KittenConstants() {}

    public static final String KITTEN_SOUND = "meow";
    public static final double KITTEN_CUTENESS_FACTOR = 1;
}
Zarkonnen avatar Nov 26 '2008 12:11 Zarkonnen

No pretendo que el derecho tenga razón, pero veamos este pequeño ejemplo:

public interface CarConstants {

      static final String ENGINE = "mechanical";
      static final String WHEEL  = "round";
      // ...

}

public interface ToyotaCar extends CarConstants //, ICar, ... {
      void produce();
}

public interface FordCar extends CarConstants //, ICar, ... {
      void produce();
}

// and this is implementation #1
public class CamryCar implements ToyotaCar {

      public void produce() {
           System.out.println("the engine is " + ENGINE );
           System.out.println("the wheel is " + WHEEL);
      }
}

// and this is implementation #2
public class MustangCar implements FordCar {

      public void produce() {
           System.out.println("the engine is " + ENGINE );
           System.out.println("the wheel is " + WHEEL);
      }
}

ToyotaCar no sabe nada sobre FordCar y FordCar no sabe nada sobre ToyotaCar. El principio CarConstants debería cambiarse, pero...

No se deben cambiar las constantes, porque la rueda es redonda y el motor es mecánico, pero... ¡En el futuro, los ingenieros de investigación de Toyota inventaron el motor electrónico y las ruedas planas! Veamos nuestra nueva interfaz.

public interface InnovativeCarConstants {

          static final String ENGINE = "electronic";
          static final String WHEEL  = "flat";
          // ...
}

y ahora podemos cambiar nuestra abstracción:

public interface ToyotaCar extends CarConstants

a

public interface ToyotaCar extends InnovativeCarConstants 

Y ahora, si alguna vez necesitamos cambiar el valor central si el MOTOR o la RUEDA, podemos cambiar la interfaz ToyotaCar en un nivel de abstracción, sin tocar las implementaciones.

NO ES SEGURO, lo sé, pero aún así quiero saber qué piensas sobre esto.

pleerock avatar Oct 15 '2012 03:10 pleerock