O escopo do nível do pacote Java é útil?


11

Eu entendo a idéia do escopo do pacote e às vezes até pensei que eu queria. No entanto, toda vez que me empenhava em tentar usá-lo, descobri que não atendia às necessidades que eu pensava que serviriam.

Minha principal questão sempre parece ser que as coisas que desejo limitar o escopo nunca estão no mesmo pacote. Conceitualmente, todos podem estar vinculados, mas a divisão lógica dos dados no aplicativo os possui como pacotes filhos separados de um pacote maior.

Por exemplo, posso ter um modelo de missão e quero que apenas outras ferramentas da missão, como meus serviços de missão, usem alguns métodos. No entanto, acabo com Missions.models e Missions.services como meus pacotes, portanto, MissionModel e MissionService não têm o mesmo escopo de pacote. Nunca parece que existe uma situação em que os pacotes contêm adequadamente o que eu gostaria de ter permissões elevadas, sem incluir também várias coisas que eu não gostaria de ter; e raramente sinto que a vantagem do escopo de pacote de um método justifique a modificação da arquitetura do meu projeto para colocar tudo no mesmo pacote. Freqüentemente, Aspectos ou algum tipo de inversão de controle são a melhor abordagem para qualquer problema que eu considerei brevemente o escopo do pacote.

Estou curioso, pelo contrário, isso geralmente é considerado verdadeiro em todos os desenvolvedores de Java ou é apenas um acaso do trabalho que faço. O escopo do pacote é muito utilizado no mundo real? Existem muitos casos em que é considerado uma boa forma de usar ou é visto principalmente como um comportamento herdado que raramente é explorado no desenvolvimento moderno?

Não estou perguntando nada sobre por que o escopo privado do pacote é padrão; estou perguntando quando ele deve ser usado, independentemente dos padrões. A maioria das discussões sobre por que é padrão não entra realmente quando o escopo do pacote é realmente útil. Em vez disso, argumenta simplesmente por que os outros dois escopos comumente usados ​​não devem ser o padrão, de modo que o pacote vence pelo processo de eliminação. Além disso, minha pergunta é sobre o estado atual de desenvolvimento. Especificamente, desenvolvemos ao ponto em que outras ferramentas e paradigmas tornam o escopo do pacote menos útil do que quando a decisão de torná-lo padrão fazia sentido.


Experiência experimental: como seriam os programas Java se você não tivesse pacotes? Pode ser que você não tenha examinado ou trabalhado em nenhum grande programa Java.
26715 Robert Harvey

Veja o código para java.util.Stringe java.util.Integer.


2
A resposta mais votada na pergunta duplicada cobre o uso mais importante para o pacote privado, respondendo implicitamente por que é útil.

2
Não estou nem um pouco convencido pela duplicata. Esta pergunta está perguntando "Para que serve o escopo do pacote?" enquanto o outro pergunta (entre outras coisas) "Como o escopo do pacote é o padrão, para que serve o escopo privado?" Além disso, a resposta aceita no dupe e a resposta aceita aqui estão apresentando argumentos completamente diferentes.
Ixrec 30/08/2015

Respostas:


2

Em todo o java.util.*pacote, há casos em que o código é escrito com proteção no nível do pacote. Por exemplo, este bit de java.util.String+ um construtor em Java 6 :

// Package private constructor which shares value array for speed.
String(int offset, int count, char value[]) {
    this.value = value;
    this.offset = offset;
    this.count = count;
}

ou este método getChars :

/** Copy characters from this string into dst starting at dstBegin. 
    This method doesn't perform any range checking. */
void getChars(char dst[], int dstBegin) {
    System.arraycopy(value, offset, dst, dstBegin, count);
}

A razão para isso é que os projetistas do código (e java.util.*podem ser vistos como uma biblioteca bastante grande) queriam a capacidade de ter um desempenho mais rápido, com o custo de uma perda de várias segurança (verificações de faixa em matrizes, acesso direto a outros campos) que implicam na implementação, e não na interface, acesso 'inseguro' a partes da classe que são consideradas imutáveis ​​por métodos públicos).

Ao restringir esses métodos e campos para serem acessados ​​apenas por outras classes no java.utilpacote, isso facilita o acoplamento entre elas, mas também evita a exposição desses detalhes de implementação ao mundo.

Se você está trabalhando em um aplicativo e não precisa se preocupar com outros usuários, a proteção no nível do pacote não é algo com que você precisa se preocupar. Além de vários aspectos da pureza do design, você pode fazer tudo publice ficar bem com isso.

No entanto, se você estiver trabalhando em uma biblioteca que será usada por outras pessoas (ou quiser evitar emaranhar demais suas classes para refatoração posterior), use o nível mais estrito de proteção oferecido a você para suas necessidades. Muitas vezes isso significa private. Às vezes, no entanto, você precisa expor um pouco dos detalhes da implementação a outras classes no mesmo pacote para evitar repetições ou facilitar a implementação real um pouco mais à custa do acoplamento das duas classes e suas implementações. Assim, a proteção no nível do pacote.


1
Eu olhei através do java.util e vejo isso. No entanto, também noto que é uma biblioteca GRANDE. Hoje em dia, parece que bibliotecas grandes raramente são escritas sem o uso de subpacotes; e são essas divisões de subpacotes que levam a limites. Não estou dizendo que o java.util esteja errado, mas parece que eles poderiam se safar de um pacote grande apenas porque tinham EXTREMAMENTE bons programadores nos quais podiam confiar para fazer tudo certo com um encapsulamento bastante limitado dentro de um pacote; Eu me sentiria nu confiando em tanto potencial para fazer mal a desenvolvedores comuns que possam trabalhar em pacotes semelhantes aos meus.
precisa saber é o seguinte

1
@dsollen Se você se interessar pelo Spring, Hibernate ou grandes bibliotecas semelhantes, encontrará coisas semelhantes. Há momentos em que você precisa de um acoplamento mais próximo. É preciso ter a oportunidade de revisar os envios por código e garantir que tudo esteja correto. Se você não precisa de um acoplamento mais próximo ou não confia nos outros codificadores, os campos públicos ou os campos ou métodos de pacote ou protegido nem sempre são adequados. No entanto, isso não significa que não seja útil.

em resumo, ficaria tentado a criar pacotes mais restritos e não me preocuparia com o nível de otimização (que é apenas um desperdício em 95% dos projetos) para as minhas necessidades diárias. Assim, minha pergunta de acompanhamento se torna: o escopo no nível do pacote é um potencial de otimização muito específico usado apenas em certas APIs maciças amplamente usadas? Ou seja, apenas os grandes precisam ou desejam relaxar as proteções nesse nível? Existem razões pelas quais bons programadores que trabalham mesmo em API 'padrão' com bases de usuários menores encontrariam utilidade para isso?
precisa saber é o seguinte

Não recebi meu post de acompanhamento com a rapidez suficiente :) Entendo e entendo o que você fez, não estou debatendo nada. É mais uma questão de acompanhamento quanto à sua utilidade para o resto de nós, bons programadores, mas que não conseguem trabalhar em APIs tão grandes. Especificamente, é principalmente uma otimização às custas da troca de encapsulamento que é feita apenas quando você realmente precisa ajustar o desempenho.
precisa saber é o seguinte

2
@dsollen não é sobre desempenho (embora esse seja um motivo para expor a implementação), mas sobre o encapsulamento. Você faz isso para evitar a repetição do código Integer.toString()e String.valueOf(int)pode compartilhar o código sem expô-lo ao mundo. As java.utilclasses devem trabalhar em conjunto para fornecer um conjunto maior de funcionalidades, em vez de serem classes individuais que são inteiramente insulares - elas estão juntas em um pacote e compartilham a funcionalidade de implementação por meio do escopo no nível do pacote.

5

Em algum momento, escalo a visibilidade de métodos privados ou protegidos para empacotar para permitir que um teste de junção acesse alguns detalhes da implementação que não devem ser acessados ​​de fora.

como o teste de junção tem o mesmo pacote que o item em teste, ele pode acessar esses detalhes de implementação

exemplo

  public class MyClass {
    public MyClass() {
        this(new MyDependency());
    }

    /* package level to allow junit-tests to insert mock/fake implementations */ 
    MyClass(IDependency someDepenency) {...}
  }

É discutível se esse é um uso "bom" ou não, mas é pragmático


2
Eu sempre colocar os testes em um pacote diferente, caso contrário, eu acho que deixei algum método fundamental no escopo padrão que não permite que ninguém chamá-lo ...
soru

Eu nunca faço isso com métodos, mas é um recurso interessante a ser usado para dependências injetadas. Por exemplo, ao testar EJBs, os EntityManagers injetados podem ser ridicularizados configurando um EM de nível de pacote com um objeto Mock no qual, em tempo de execução, é injetado pelo servidor de aplicativos.
jwenting

Por que elevar protectedao escopo do pacote? protected fornece acesso ao pacote. É uma peculiaridade um pouco estranha, mas acho que eles não queriam exigir declarações de escopo compostas como protected packagescope void doSomething()(que também requer uma nova palavra-chave).
tgm1024 - Monica foi maltratada 27/11

2

Não é tão útil, especialmente como padrão, em um mundo em que as pessoas tendem a usar os nomes pontilhados dos pacotes como se fossem subpacotes. Como o Java não possui subpacote, o xyinternals não tem mais acesso ao xy do que ao ab Os nomes dos pacotes são iguais ou diferentes, uma correspondência parcial não é diferente de não ter nada em comum.

Acho que os desenvolvedores originais nunca esperaram que essa convenção fosse usada e queriam manter as coisas simples sem adicionar a complexidade dos pacotes hierárquicos no estilo Ada.

O Java 9 está tratando disso, pois você pode colocar vários pacotes em um módulo e exportar apenas alguns. Mas isso tornará o escopo do pacote ainda mais redundante.

Em um mundo ideal, eles alterariam o escopo padrão para 'escopo do módulo, se existir um módulo que contenha, caso contrário, o escopo do pacote'. Em seguida, você pode usá-lo para compartilhar a implementação em um módulo, permitindo a refatoração sem interromper as interfaces exportadas. Mas talvez haja alguma sutileza por que isso quebraria a compatibilidade com versões anteriores ou seria ruim.


Acho o comentário do java 9 interessante. Eu acho que uma simples capacidade de reconhecer heraquias de pacotes e de alguma forma definir o escopo do pacote em relação a algum pacote pai (usando anotações?) Seria bom.
precisa saber é o seguinte

1
@dsollen, talvez, mas minha primeira inclinação é que estaríamos começando a pregar os ferros de pneu com esse. Um primeiro passo muito melhor para a clareza (que traz segurança até certo ponto) na minha opinião seria inventar uma palavra-chave de escopo de pacote real. Pode até ser "pacote" :)
tgm1024 - Monica foi maltratada 27/11/17

2

O tipo de coesão que você descreve não é realmente o melhor exemplo de onde você usaria o acesso ao pacote. O Package é útil para criar componentes, módulos que são intercambiáveis ​​em uma interface.

Aqui está um exemplo aproximado de um cenário. Suponha que seu código tenha sido reorganizado para ter mais a ver com coesão e componente funcional. Talvez 3 frascos como:

**MissionManager.jar** - an application which uses the Java Service Provider Interface to load some implementation of missions, possible provided by a 3rd party vendor.

**missions-api.jar** - All interfaces, for services and model
    com...missions
        MissionBuilder.java   - has a createMission method
        Mission.java - interface for a mission domain object
    com...missions.strategy
        ... interfaces that attach strategies to missions ...
    com...missions.reports
        .... interfaces todo with mission reports ....

   **MCo-missionizer-5000.jar** -  provides MCo's Missionizer 5000 brand mission implementation.
       com...missions.mco
                  Mission5000.java - implements the Mission interface.
       com...missions.strategy.mco
              ... strategy stuff etc ...

Ao usar o nível do pacote no Mission5000.java, você pode garantir que nenhum outro pacote ou componente como o MissionManager possa unir a implementação da missão. Esse é apenas o componente fornecido por "terceiros" de "M co", na verdade, cria uma implementação de marca especial de uma Missão. O gerente de missão deve chamar MissionBuiler.createMission (...) e usar a interface definida.

Dessa forma, se o componente de implementação da Missão for substituído, sabemos que os implementadores do MissionManager.jar não ignoraram a API e usaram a implementação da MCo diretamente.

Portanto, podemos trocar o MCo-missionizer5000.jar por um produto concorrente. (Ou talvez uma simulação para a unidade de teste do gerente da missão)

Por outro lado, a MCo pode ocultar um pouco de seus segredos comerciais de missionários proprietários.

(Isso também está relacionado à imposição de idéias de instabilidade e abstração nos componentes.)


-1

Como o escopo do pacote em java não é projetado da maneira hierárquica, dificilmente é útil. Não usamos o escopo do pacote e definimos todas as classes como públicas.

Em vez disso, usamos nossas próprias regras de acesso com base na hierarquia de pacotes e nos nomes de pacotes predefinidos.

Se você estiver interessado em detalhes no google, consulte "CODERU-Rules".

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.