Aunque formalmente legítimo, esto puede indicar un olor a código . Para saber si esto es así en su caso, hágase tres preguntas:
- ¿Lo vas a necesitar?
- ¿Por qué lo empacaste de esa manera?
- ¿Cómo saber si su paquete API es conveniente de usar?
¿Lo vas a necesitar?
Si no ve la razón para invertir un esfuerzo adicional en el mantenimiento del código , considere dejarlo como está. Este enfoque se basa en el principio YAGNI
el programador no debe agregar funcionalidad hasta que se considere necesario ... "Implemente siempre las cosas cuando realmente las necesite, nunca cuando prevea que las necesita".
Las consideraciones que se proporcionan a continuación suponen que el esfuerzo de inversión en análisis posteriores está justificado, digamos que espera que el código se mantenga a largo plazo, o que esté interesado en practicar y perfeccionar habilidades para escribir código que se pueda mantener. Si esto no aplica, no dude en omitir el resto, porque no lo va a necesitar .
¿Por qué lo empacaste de esa manera?
Lo primero que vale la pena señalar es que ninguna de las convenciones oficiales de codificación y el tutorial brindan orientación al respecto, enviando una fuerte señal de que se espera que este tipo de decisiones sean a discreción de un programador.
Pero si observa el diseño implementado por los desarrolladores de JDK , notará que la API de subpaquetes "con fugas" en los de nivel superior no se practica allí. Por ejemplo, mire el paquete de utilidad concurrente y sus subpaquetes: bloqueos y atómico .
- Vale la pena señalar que el uso de subpaquetes API en el interior se considera correcto, por ejemplo, la implementación de ConcurrentHashMap importa bloqueos y usa ReentrantLock. Simplemente no expone los bloqueos API como públicos.
De acuerdo, desarrolladores del núcleo de la API parecen evitar eso, y para averiguar por qué es tan pregúntese, ¿por qué te empaquetar esa manera? La razón natural para eso sería el deseo de comunicar a los usuarios de paquetes algún tipo de jerarquía, tipo de capas , de modo que el subpaquete corresponda a conceptos de uso de menor nivel / más especializados / más estrechos.
Esto a menudo se hace para hacer que la API sea más fácil de usar y comprender, para ayudar a los usuarios de API a operar dentro de una capa en particular, evitando molestarlos con preocupaciones que pertenecen a otras capas. Si este es el caso, exponer la API de nivel inferior de los subpaquetes seguramente iría en contra de su intención.
- Nota al margen, si no puede decir por qué lo empaqueta de esta manera, considere volver a su diseño y analizarlo a fondo hasta que lo entienda. Piénselo, si es difícil de explicarle a usted, incluso ahora, ¿qué tan difícil sería para aquellos que mantendrán y usarán su paquete en el futuro?
¿Cómo saber si su paquete API es conveniente de usar?
Para eso, recomiendo escribir pruebas que ejerciten la API proporcionada por su paquete. Pedirle a alguien que revise tampoco estaría de más, pero el revisor de API experimentado probablemente le pedirá que proporcione tales pruebas de todos modos. La cuestión es que es difícil superar ver un ejemplo de uso real al revisar API.
- Uno puede mirar la clase con un método único que tiene un parámetro único y soñar wow lo simple , pero si la prueba para invocarlo requiere la creación de otros 10 objetos, invocando como 20 métodos con 50 parámetros en total, esto rompe una ilusión peligrosa y revela la verdadera complejidad de el diseño.
Otra ventaja de tener pruebas es que estas pueden simplificar enormemente la evaluación de varias ideas que considera para mejorar su diseño.
A veces me ocurrió que cambios de implementación relativamente menores desencadenaron simplificaciones importantes en las pruebas, reduciendo la cantidad de importaciones, objetos, invocaciones de métodos y parámetros. Incluso antes de ejecutar las pruebas, esto sirve como una buena indicación de la forma en que vale la pena seguir adelante.
Lo opuesto también me sucedió a mí, es decir, cuando grandes cambios ambiciosos en mis implementaciones destinadas a simplificar la API se probaron incorrectos por un impacto menor e insignificante en las pruebas, lo que me ayudó a descartar las ideas infructuosas y dejar el código como está (tal vez solo con un comentario agregado para ayudar a comprender los antecedentes del diseño y ayudar a otros a aprender sobre el camino equivocado).
Para su caso concreto, lo primero que viene a la mente es considerar cambiar la herencia a la composición , es decir, en lugar de SomeClass
implementar directamente Something
, incluir un objeto que implemente esa interfaz dentro de la clase,
package me.my;
import me.my.pkg.Something;
public class SomeClass {
private Something something = new Something() {
/* ... implementation of Something goes here ... */
}
/* ... some more method implementations go here too ... */
}
Este enfoque puede ser rentable en el futuro, evitando el riesgo de que una subclase SomeClass
anule accidentalmente un método de Something
, haciendo que se comporte de una manera que no planificó.