2010-04-30 6 views
7

¿Qué sucede si tengo pocos métodos de fábrica que devuelvan un tipo no público y un conjunto de métodos que da variables de este tipo que no es de público? Esto resulta con un mensaje de advertencia titulado en NetBeans.Exportación de tipo no público a través de API pública

En resultado, la API pública contendrá solo dos conjuntos de métodos de emparejamiento. El motivo es hacer que mi jerarquía de tipos quede sellada (como las clases de sellos en Scala) y permitir que los usuarios solo creen estos tipos mediante métodos de fábrica. Así que tenemos DSL en cierto sentido.

Por ejemplo, Clase de programación representada por contraints de campos de calendario. Hay algunos tipos de contraints - Range, Singleton, List, FullSet - con la interfaz NumberSet como raíz. No queremos exponer estos tipos y cómo Schedule interactúa con ellos. Solo queremos la especificación del usuario. Así que hacemos NumberSet package-private. En horario de clases creamos unos fábrica-métodos de limitaciones:

NumberSet singleton(int value); 
NumberSet range(int form, int to); 
NumberSet list(NumberSet ... components); 

y algunos métodos para la creación de objeto Programación:

Schedule everyHour(NumberSet minutes); 
Schedule everyDay(NumberSet minutes, NumberSet hours); 

usuario sólo puede utilizarlos en la forma:

Schedule s = Schedule.everyDay(singleton(0), list(range(10-15), singleton(8))); 

¿Es mala idea?

Respuesta

9

La idea en sí es sonido. Pero no funcionará, si hace que el tipo de raíz escriba (aquí: NumberSet) paquete privado. Exponga ese tipo o use una interfaz pública en su lugar. Los tipos de implementación reales deben (y pueden) estar ocultos.

public abstract class NumberSet { 

    // Constructor is package private, so no new classes can be derived from 
    // this guy outside of its package. 
    NumberSet() { 
    } 
} 

public class Factories { 

    public NumberSet range(int start, int length) { 
     return new RangeNumberSet(start, length); 
    } 

    // ... 
} 

class RangeNumberSet extends NumberSet { 
    // ... must be defined in the same package as NumberSet 
    // Is "invisible" to client code 
} 

Editar para exponer un tipo raíz oculta/privada de una API pública es un error. Tenga en cuenta la situación siguiente:

package example; 

class Bar { 
    public void doSomething() { 
      // ... 
    } 
} 

public class Foo { 

    public Bar newBar() { 
     return new Bar(); 
    } 
} 

y considere una aplicación cliente que utiliza esta API. ¿Qué puede hacer el cliente? No puede declarar correctamente una variable para que tenga el tipo Bar ya que ese tipo es invisible para cualquier clase fuera del paquete example. Ni siquiera puede llamar a los métodos public en una instancia Bar que obtuvo de algún lugar, ya que no sabe, que existe ese método público (no puede ver la clase, y mucho menos los miembros que expone). Entonces, lo mejor que puede hacer un cliente aquí es algo como:

Object bar = foo.newBar(); 

que es esencialmente inútil. Una cosa diferente sería tener una interfaz pública (o clase abstracta) en lugar del paquete privado, como en el código definido anteriormente. En este caso, el cliente realmente puede declarar una variable del tipo NumberSet. No puede crear sus propias instancias ni derivar subclases, ya que el constructor está oculto, pero puede acceder a la API pública definida.

Editar nuevamente Incluso si desea un valor "sin características" (desde la perspectiva del cliente), es decir, un valor que no defina ninguna API interesante que el cliente desee llamar, sigue siendo una buena idea exponer un tipo de base pública de la manera descrita anteriormente. Y solo para que el compilador pueda realizar la comprobación de tipos y permitir que el código del cliente almacene temporalmente dicho valor en una variable (declarada correctamente).

Si no desea que su cliente llame a ningún método de la API sobre el tipo: está bien. No hay nada que le impida no proporcionar una API pública en su tipo de base pública (de lo contrario). Simplemente no declares ninguno. Use una clase base abstracta "vacía" (vacía desde la perspectiva del cliente, ya que todos los métodos interesantes serían paquetes privados y, por lo tanto, ocultos). Sin embargo, debe proporcionar un tipo de base pública, o debe usar el Object como valor de retorno, pero luego perderá la comprobación de errores en tiempo de compilación.

Regla de oro: si el cliente tiene que llamar a algún método para obtener un valor y pasarlo a otra API, entonces el cliente realmente sabe, que hay algunos valores mágicos especiales. Y tiene que ser capaz de manejarlo de alguna manera ("pásalo", al menos). No proporcionarle un tipo apropiado para que el cliente maneje los valores no le compra nada, excepto advertencias de compilador (totalmente apropiadas).

public abstract class Specification { 

    Specification() { 
     // Package private, thus not accessible to the client 
     // No subclassing possible 
    } 

    Stuff getInternalValue1() { 
     // Package private, thus not accessible to the client 
     // Client cannot call this 
    } 
} 

La clase anterior está "vacía" en lo que se refiere al código del cliente; no ofrece una API utilizable excepto cosas, que el Object ya ofrece. El mayor beneficio de tenerlo: el cliente puede declarar variables de este tipo, y el compilador puede escribir cheques. Sin embargo, su marco sigue siendo el único lugar donde se pueden crear instancias concretas de este tipo y, por lo tanto, su marco de trabajo tiene control total sobre todos los valores.

+0

¿Por qué no funciona si creamos el tipo de raíz paquete privado? –

+0

Sí, pero es un cliente no deseado para ver este tipo. Solo necesitamos especificaciones a través de los métodos de fábrica y pasarlos a través de los métodos de fábrica para los objetos de programación. –

0

me ocurren dos maneras de hacer esto, pero tampoco realmente funcionan:

  1. Declarar NumberSet como paquete privado, pero cambiar los métodos de la API públicas que actualmente utilizan el tipo de NumberSet utilizar Object lugar, y haga que las implementaciones emitan argumentos desde Object hasta NumberSet según sea necesario. Esto se basa en que los que llaman siempre pasan el tipo correcto de objeto, y será propenso a ClassCastException s.

  2. Implemente una interfaz pública NumberSet sin métodos y paquete privado InternalNumberSet que implementa NumberSet y define los métodos requeridos internamente. La conversión de tipo se utiliza una vez más para emitir argumentos NumberSet a InternalNumberSet. Este es mejor el enfoque anterior, pero si alguien crea una clase que implementa NumberSet sin implementar también InternalNumberSet, el resultado será ClassCastException s una vez más.

Personalmente, creo que debe declarar la interfaz NumberSet como pública. No existe un daño real al exponer getters en la interfaz, y si quiere que sus clases de implementación sean inmutables, declare que son definitivas y no exponga a los setters.

Cuestiones relacionadas