L'ambito a livello di pacchetto Java è utile?


11

Comprendo l'idea dell'ambito del pacchetto e, a volte, ho persino pensato di volerlo. Tuttavia, ogni volta che ho deciso con l'intento serio di provare ad usarlo, ho scoperto che non si adattava alle esigenze che pensavo potesse servire.

Il mio problema principale sembra sempre che le cose che desidero limitare l'ambito di applicazione non siano mai nello stesso pacchetto. Concettualmente possono essere tutti collegati, ma la divisione logica dei dati all'interno dell'applicazione li ha come pacchetti figlio separati di un pacchetto più grande.

Ad esempio, potrei avere un modello Mission e voglio che solo altri strumenti Mission, come i miei servizi Mission, usino alcuni metodi. Tuttavia, finisco con Missions.models e Missions.services come i miei pacchetti, quindi MissionModel e MissionService non hanno lo stesso scopo del pacchetto. Non sembra mai che ci sia una situazione in cui i pacchetti contengono appropriatamente le cose che vorrei avere autorizzazioni elevate senza includere anche numerose cose che non desidero avere quelle autorizzazioni; e raramente sento il vantaggio del scoping del pacchetto che un metodo giustifica la modifica della mia architettura di progetto per posizionare tutto nello stesso pacchetto. Spesso Aspetti o una sorta di inversione del controllo si rivelano l'approccio migliore a qualsiasi problema per cui ho brevemente considerato l'ambito del pacchetto.

Sono curioso piuttosto che questo sia generalmente considerato vero per tutti gli sviluppatori Java, o è solo un colpo di fortuna del lavoro che faccio. L'ambito del pacchetto è molto utilizzato nel mondo reale? Ci sono molti casi in cui è considerata una buona forma da usare o è vista principalmente come un comportamento ereditario da sfruttare raramente nello sviluppo moderno?

Non sto chiedendo nulla sul perché l'ambito privato del pacchetto è predefinito, sto chiedendo quando dovrebbe essere usato indipendentemente dalle impostazioni predefinite. La maggior parte delle discussioni sul motivo per cui il valore predefinito non è realmente utile quando l'ambito del pacchetto è effettivamente utile, argomentando semplicemente perché gli altri due ambiti comunemente usati non dovrebbero essere predefiniti, quindi il pacchetto vince per processo di eliminazione. Inoltre, la mia domanda riguarda l' attuale stato di sviluppo. In particolare, abbiamo sviluppato al punto che altri strumenti e paradigmi rendono meno utile l'ambito del pacchetto, quindi era tornato quando la decisione di renderlo predefinito aveva un senso.


Esperimento di pensiero: come sarebbero i programmi Java se non avessi i pacchetti? È possibile che tu non abbia guardato o lavorato su grandi programmi Java.
Robert Harvey,

Guarda il codice per java.util.Stringe java.util.Integer.


2
La risposta più votata sulla domanda duplicata copre l'uso più importante del pacchetto privato, rispondendo implicitamente al motivo per cui è utile.

2
Non sono affatto convinto dal duplicato. Questa domanda è "A cosa serve il pacchetto?" mentre l'altro chiede (tra le altre cose) "Dato l'ambito del pacchetto è l'impostazione predefinita, a cosa serve l'ambito privato?" Inoltre la risposta accettata sul dupe e la risposta accettata qui stanno facendo punti completamente diversi.
Ixrec,

Risposte:


2

In tutto il java.util.*pacchetto ci sono casi in cui il codice è scritto con protezione a livello di pacchetto. Ad esempio, questo bit di java.util.String- un costruttore in 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;
}

o questo metodo 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);
}

La ragione di ciò è che i progettisti del codice (e java.util.*possono essere pensati come una libreria piuttosto grande) volevano la capacità di avere prestazioni più veloci a scapito di una perdita di sicurezza (controlli della gamma su array, accesso diretto ad altri campi) ciò implica l'implementazione piuttosto che l'interfaccia, l'accesso "non sicuro" a parti della classe che altrimenti sarebbero considerate immutabili con metodi pubblici).

Limitando questi metodi e campi a cui possono accedere solo altre classi nel java.utilpacchetto, consente un accoppiamento più stretto tra loro ma evita anche di esporre questi dettagli di implementazione al mondo.

Se stai lavorando su un'applicazione e non devi preoccuparti di altri utenti, la protezione a livello di pacchetto non è qualcosa di cui devi preoccuparti. Oltre ai vari aspetti della purezza del design, potresti fare tutto publiced essere d'accordo con quello.

Tuttavia, se stai lavorando su una libreria che deve essere utilizzata da altri (o vuoi evitare di intrecciare troppo le tue classi per un successivo refactoring), dovresti usare il livello di protezione più rigoroso che ti è stato concesso per le tue esigenze. Spesso questo significa private. A volte, tuttavia, è necessario esporre un po 'dei dettagli di implementazione ad altre classi nello stesso pacchetto per evitare di ripetere se stessi o per rendere l'implementazione effettiva un po' più semplice a spese dell'accoppiamento delle due classi e delle loro implementazioni. Pertanto, la protezione a livello di pacchetto.


1
Ho guardato java.util e lo vedo. Tuttavia, noto anche che è una GRANDE libreria. Oggi, a volte, sembra che le librerie di grandi dimensioni vengano raramente scritte senza l'uso di sotto-pacchetti; ed è quelle divisioni del sotto-pacchetto che portano a limiti. Non sto dicendo che java.util sia sbagliato, ma sembra che potrebbero cavarsela con un pacchetto di grandi dimensioni solo perché avevano ESTREMAMENTE buoni programmatori di cui potevano fidarsi di fare tutto bene con l'incapsulamento piuttosto limitato all'interno di un pacchetto; Mi sentirei nudo fidandomi di quel potenziale potenziale per fare degli sviluppatori mediocri che potrebbero funzionare su pacchetti simili a me.
dsollen,

1
@dsollen Se scavi in ​​Spring, e Hibernate o librerie simili simili troverai cose simili. Ci sono momenti in cui è necessario l'accoppiamento più vicino. Si dovrebbe avere l'opportunità di rivedere il codice degli invii e assicurarsi che tutto sia corretto. Se non hai bisogno di un accoppiamento più stretto o non ti fidi affatto degli altri programmatori, i campi pubblici, i pacchetti o i campi o metodi protetti non sono sempre appropriati. Tuttavia, ciò non significa che non sia utile.

in breve, sarei tentato di creare pacchetti più limitati e non preoccuparmi del livello di ottimizzazione (che è solo uno spreco nel 95% dei progetti) per le mie esigenze quotidiane. Quindi la mia domanda di follow-up diventa l'ambito a livello di pacchetto un potenziale di ottimizzazione molto specifico utilizzato solo in alcune API di massa ampiamente utilizzate? IE solo i grandi avranno bisogno o vorranno rilassare le protezioni a quel livello? Ci sono ragioni per cui i bravi programmatori che lavorano anche su API "standard" con basi di utenti più piccole potrebbero trovare utile questo scopo?
dsollen,

Non ho ricevuto il mio post di follow-up abbastanza velocemente :) Capisco e vedo il punto che hai sollevato, non lo sto discutendo affatto. Più semplicemente porre una domanda di follow-up sulla sua utilità per il resto di noi bravi programmatori, ma quelli che non riescono a lavorare su API così enormi. In particolare, è principalmente un'ottimizzazione a scapito del compromesso sull'incapsulamento che viene effettuata solo quando è davvero necessario modificare le prestazioni.
dsollen,

2
@dsollen non si tratta di prestazioni (anche se questo è uno dei motivi per esporre l'implementazione) ma piuttosto di incapsulamento. Lo fai per evitare di ripetere il codice Integer.toString()e String.valueOf(int)puoi condividere il codice senza esporlo al mondo. Le java.utilclassi devono lavorare insieme per fornire una più ampia gamma di funzionalità piuttosto che essere singole classi che sono interamente isole per se stesse - sono insieme in un pacchetto e condividono la funzionalità di implementazione tramite scoping a livello di pacchetto.

5

A volte intensifico la visibilità dei metodi privati ​​o protetti per creare pacchetti per consentire a un test junit di accedere ad alcuni dettagli di implementazione a cui non si dovrebbe accedere dall'esterno.

poiché junit-test ha lo stesso pacchetto di item-under-test, può accedere a questi dettagli di implementazione

esempio

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

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

È discutibile se si tratta di un "buon" utilizzo o meno ma è pragmatico


2
Metto sempre i test in un pacchetto diverso, altrimenti trovo che ho lasciato un metodo chiave nell'ambito predefinito che non consente a nessun altro di chiamarlo ...
soru,

Non lo faccio mai con i metodi, ma è una bella funzionalità da utilizzare per le dipendenze iniettate. Ad esempio, durante il test di EJB, EntityManager iniettati può essere deriso impostando un livello pacchetto EM con un oggetto Mock in cui verrebbe iniettato in fase di esecuzione dal server delle applicazioni.
jwenting

Perché elevare protectedall'ambito del pacchetto? fornisce protected già l' accesso al pacchetto. È una strana stranezza, ma penso che non volessero richiedere dichiarazioni di ambito composte come protected packagescope void doSomething()(che richiede anche una nuova parola chiave).
tgm1024 - Monica fu maltrattata il

2

Non è poi così utile, soprattutto come predefinito, in un mondo in cui le persone tendono a usare i nomi punteggiati dei pacchetti come se fossero sotto-pacchetti. Poiché Java non ha un sub-pacchetto, quindi xyinternals non ha più accesso a xy di quanto non faccia ad ab I nomi dei pacchetti sono uguali o diversi, una corrispondenza parziale non è diversa dal non avere nulla in comune.

Immagino che gli sviluppatori originali non si sarebbero mai aspettati di usare quella convenzione e volevano mantenere le cose semplici senza aggiungere la complessità dei pacchetti gerarchici in stile Ada.

Java 9 risolve questo problema, in quanto è possibile inserire diversi pacchetti in un modulo ed esportarne solo alcuni. Ma ciò renderà l'ambito del pacchetto ancora più ridondante.

In un mondo ideale, cambierebbero l'ambito predefinito in "ambito modulo, se esiste un modulo contenente, altrimenti ambito pacchetto". Quindi è possibile utilizzarlo per condividere l'implementazione all'interno di un modulo, consentendo il refactoring senza interrompere le interfacce esportate. Ma forse c'è qualche sottigliezza sul motivo per cui ciò romperebbe la compatibilità all'indietro, o sarebbe altrimenti negativo.


Trovo interessante il commento di java 9. Penso che una semplice capacità di riconoscere le herachie dei pacchetti e in qualche modo definire l'ambito dei pacchetti rispetto ad alcuni pacchetti principali (usando le annotazioni?) Sarebbe buona.
dsollen,

1
@dsollen, forse, ma la mia prima inclinazione è che avremmo iniziato a stirare i ferri della gomma con quello. Un primo passo molto migliore per la chiarezza (che porta la sicurezza in una certa misura) secondo me sarebbe inventare una vera e propria parola chiave dell'ambito del pacchetto. Potrebbe anche essere un "pacchetto" :)
tgm1024 - Monica è stata maltrattata il

2

Il tipo di coesione che descrivi non è in realtà il miglior esempio di dove utilizzeresti l'accesso al pacchetto. Il pacchetto è utile per la creazione di componenti, moduli intercambiabili su un'interfaccia.

Ecco un esempio approssimativo di uno scenario. Supponiamo che il tuo codice sia stato riorganizzato per favorire maggiormente la coesione funzionale e la componentizzazione. Forse 3 vasetti come:

**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 ...

Usando il livello del pacchetto in Mission5000.java, non puoi essere certo che nessun altro pacchetto o componente come MissionManager sia in grado di accoppiarsi alla realizzazione della missione. Questo è solo il componente fornito da "terzi" di "M co" in realtà crea una propria implementazione con marchio speciale di una missione. Il responsabile della missione deve chiamare MissionBuiler.createMission (...) e utilizzare l'interfaccia definita.

In questo modo, se il componente di implementazione di Mission viene sostituito, sappiamo che gli implementatori di MissionManager.jar non hanno bypassato l'API e hanno utilizzato l'implementazione di MCo direttamente.

Quindi possiamo sostituire MCo-missionizer5000.jar con un prodotto concorrente. (O forse un Mock per test unitari del Mission Manager)

Al contrario, MCo può fare un po 'di svelamento dei segreti commerciali dei suoi missionari proprietari.

(Questo è correlato applicando idee di instabilità e astrattezza anche nei componenti.)


-1

Poiché l'ambito del pacchetto in Java non è progettato in modo gerarchico, non è molto utile. Non utilizziamo affatto l'ambito del pacchetto e definiamo tutte le classi come pubbliche.

Utilizziamo invece le nostre regole di accesso basate sulla gerarchia dei pacchetti e sui nomi dei pacchetti predefiniti.

Se sei interessato ai dettagli google per "CODERU-Rules".

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.