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.
¿Por qué no funciona si creamos el tipo de raíz paquete privado? –
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. –