Embora formalmente legítimo, isso pode indicar um cheiro de código . Para descobrir se isso é verdade no seu caso, faça três perguntas a si mesmo:
- Você vai precisar disso?
- Por que você empacotou dessa maneira?
- Como você sabe se a API do seu pacote é conveniente de usar?
Você vai precisar disso?
Se você não vê o motivo de investir um esforço extra na manutenção do código , considere deixá-lo como está. Esta abordagem é baseada no princípio YAGNI
o programador não deve adicionar funcionalidades até que seja considerado necessário ... "Sempre implemente as coisas quando você realmente precisar delas, nunca quando você apenas prever que precisa delas."
As considerações fornecidas abaixo pressupõem que o esforço de investimento em uma análise mais aprofundada seja justificado, digamos que você espere que o código seja mantido a longo prazo ou que esteja interessado em praticar e aperfeiçoar habilidades para escrever código sustentável. Se isso não se aplicar, fique à vontade para pular o resto - porque você não precisará disso .
Por que você empacotou dessa maneira?
A primeira coisa que vale a pena notar é que nenhuma das convenções e tutoriais oficiais de codificação fornece qualquer orientação sobre isso, enviando um forte sinal de que espera-se que decisões desse tipo sejam feitas a critério de um programador.
Mas se você observar o design implementado pelos desenvolvedores do JDK , notará que "vazar" a API de subpacotes para os de nível superior não é praticada lá. Por exemplo, veja o pacote util simultâneo e seus subpacotes - bloqueios e atômica .
- Vale notar que usando subpackages API dentro é considerado OK, por exemplo ConcurrentHashMap implementação importações fechaduras e usa ReentrantLock. Apenas não expõe a API de bloqueios como pública.
Tudo bem, os principais desenvolvedores de API parecem evitar isso e descobrir por que isso acontece, pergunte a si mesmo, por que você empacotou dessa maneira? A razão natural disso seria o desejo de comunicar aos usuários do pacote algum tipo de hierarquia, tipo de camadas , para que o subpacote corresponda a conceitos de uso de nível mais baixo / mais especializados / mais estreitos.
Isso geralmente é feito para facilitar o uso e a compreensão da API, para ajudar os usuários da API a operar dentro de uma determinada camada, evitando incomodá-los com preocupações pertencentes a outras camadas. Se for esse o caso, expor a API de nível inferior a partir de subpacotes certamente vai contra sua intenção.
- Nota lateral, se você não pode dizer por que o empacota dessa maneira, considere voltar ao seu design e analisá-lo completamente até entender isso. Pense nisso, se é difícil explicar até para você, mesmo agora, quão difícil seria para aqueles que manterão e usarão seu pacote no futuro?
Como você sabe se a API do seu pacote é conveniente de usar?
Para isso, eu recomendo fortemente escrever testes que exercitem a API fornecida pelo seu pacote. Pedir a alguém para revisar também não prejudicaria, mas o revisor experiente da API provavelmente solicitará que você forneça esses testes de qualquer maneira. O problema é que é difícil superar assistir a um exemplo de uso real ao revisar a API.
- Pode-se olhar para a classe com método único com parâmetro único e uau como é simples , mas se o teste para invocá-lo exige a criação de 10 outros objetos, invocando como 20 métodos com 50 parâmetros no total, isso quebra uma ilusão perigosa e revela a verdadeira complexidade de o design.
Outra vantagem de ter testes é que eles podem simplificar bastante a avaliação de várias idéias que você considera para melhorar seu design.
Às vezes me ocorreu que alterações relativamente pequenas na implementação desencadearam grandes simplificações nos testes, reduzindo a quantidade de importações, objetos, invocações de métodos e parâmetros. Mesmo antes da execução dos testes, isso serve como uma boa indicação do caminho que vale a pena seguir adiante.
O contrário também aconteceu comigo - ou seja, quando mudanças grandes e ambiciosas em minhas implementações destinadas a simplificar a API foram comprovadas erradas pelo respectivo impacto insignificante e menor nos testes, ajudando-me a abandonar as idéias infrutíferas e deixar o código como está (com talvez apenas um comentário) adicionado para ajudar a entender os antecedentes do design e ajudar outras pessoas a aprender sobre o caminho errado).
Para o seu caso concreto, a primeira coisa que vem à mente é considerar a alteração da herança na composição - ou seja, em vez de SomeClass
implementar diretamente Something
, inclua um objeto implementando essa interface dentro da classe,
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 ... */
}
Essa abordagem pode compensar no futuro, impedindo o risco de uma subclasse de SomeClass
substituir acidentalmente um método Something
, fazendo com que ele se comporte de uma maneira que você não planejou.