L'ambito del livello del pacchetto Java è utile?

11

Comprendo l'idea dello scope del pacchetto, e a volte ho persino pensato di volerlo. Tuttavia, ogni volta che ho deciso di provare a utilizzarlo ho scoperto che non corrispondeva ai bisogni che pensavo avrebbe servito.

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

Per esempio, potrei avere un modello di missione e voglio solo altri strumenti di missione, come i miei servizi di missione, per usare alcuni metodi. Comunque, finisco con Missions.models e Missions.services come i miei pacchetti, quindi MissionModel e MissionService non sono lo stesso scope 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 molte cose che non desidero avere quelle autorizzazioni; e raramente ritengo che il vantaggio dello scope del pacchetto sia un metodo che giustifica la modifica della mia architettura di progetto per posizionare tutto nello stesso pacchetto. Spesso gli Aspetti o qualche tipo di inversione di controllo risultano essere l'approccio migliore a qualsiasi problema per il quale ho considerato brevemente l'ambito del pacchetto.

Sono curioso, piuttosto, questo è 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 utilizzare, o è vista principalmente come un comportamento legacy da sfruttare raramente nello sviluppo moderno?

Non sto chiedendo nulla sul motivo per cui l'ambito privato del pacchetto è quello predefinito, sto chiedendo quando dovrebbe essere usato a prescindere dai valori predefiniti. La maggior parte delle discussioni sul motivo per cui è predefinita non entrano realmente in gioco quando l'ambito del pacchetto è effettivamente utile, invece di discutere 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 lo stato di sviluppo attuale . In particolare, abbiamo sviluppato fino al punto in cui altri strumenti e paradigmi rendono meno utile lo scope del pacchetto, ma è tornato quando la decisione di renderlo predefinito aveva senso.

    
posta dsollen 26.08.2015 - 16:57
fonte

4 risposte

2

Nel pacchetto java.util.* 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 getChars metodo :

/** 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 al costo di una perdita di varie sicurezza (controlli di gamma sugli array , accesso diretto ad altri campi che implicano l'implementazione piuttosto che l'interfaccia, accesso "non sicuro" a parti della classe che sono altrimenti considerate immutabili tramite metodi pubblici).

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

Se stai lavorando su un'applicazione e non devi preoccuparti degli altri utenti, la protezione a livello di pacchetto non è qualcosa di cui devi preoccuparti. Oltre ai vari aspetti della purezza del design, potresti rendere tutto public e andarci bene.

Tuttavia, se stai lavorando su una libreria che deve essere utilizzata da altri (o vuoi evitare di impigliare troppo le tue classi per un futuro refactoring), dovresti usare il livello più severo di protezione 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 ripetersi o rendere l'implementazione effettiva un po' più semplice a scapito dell'accoppiamento delle due classi e delle loro implementazioni. Quindi, la protezione a livello di pacchetto.

    
risposta data 26.08.2015 - 17:17
fonte
5

A volte aumento la visibilità dei metodi privati o protetti per il pacchetto per consentire a un test di junit di accedere ad alcuni dettagli di implementazione che non dovrebbero essere accessibili dall'esterno.

dato che il test di junit 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 questo è un uso "buono" o meno, ma è pragmatico

    
risposta data 26.08.2015 - 17:36
fonte
2

Il tipo di coesione la tua descrizione non è in realtà l'esempio migliore di dove utilizzeresti l'accesso al pacchetto . Il pacchetto è utile per creare componenti, moduli intercambiabili su un'interfaccia.

Ecco un esempio approssimativo di uno scenario. Supponiamo che il tuo codice sia stato riorganizzato per essere più vicino alla coesione funzionale e alla componentizzazione. Forse 3 vasi 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, puoi essere sicuro che nessun altro pacchetto, o componente come il MissionManager, possa accoppiarsi strettamente con l'implementazione della missione. Questo è solo il componente di "terze parti" fornito da "M co" in realtà crea lì la propria implementazione speciale di una missione. Il Mission Manager deve chiamare MissionBuiler.createMission (...) e utilizzare l'interfaccia definita.

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

Quindi possiamo scambiare MCo-missionizer5000.jar con un prodotto concorrente. (O forse un Mock per unità testare il Mission Manager)

Al contrario, MCo può fare una certa quantità di nascondere i segreti commerciali dei propri missionari.

(Questo è relativo all'applicazione di Instability e alle idee di Abstractness anche nei componenti.)

    
risposta data 27.08.2015 - 01:27
fonte
1

Non è molto utile, soprattutto come predefinito, in un mondo in cui le persone tendono a usare i nomi puntati dei pacchetti come se fossero sottoprogetti. Poiché Java non ha nulla come un sotto-pacchetto, quindi x.y.internals non ha più accesso a x.y di quanto non faccia ad a.b. I nomi dei pacchetti sono uguali o diversi, una corrispondenza parziale non è diversa dall'avere niente in comune.

Immagino che gli sviluppatori originali non si aspettassero che questa convenzione fosse usata e volevano mantenere le cose semplici senza aggiungere la complessità dei pacchetti gerarchici di stile Ada.

Java 9 è una sorta di indirizzo di questo, in che puoi mettere diversi pacchetti in un modulo e esportarne solo alcuni. Ma ciò renderà la portata del pacchetto ancora più ridondante.

In un mondo ideale, cambierebbero l'ambito predefinito in "ambito modulo, se esiste un modulo contenente, altrimenti l'ambito del pacchetto". Quindi è possibile utilizzarlo per condividere l'implementazione all'interno di un modulo, consentendo il refactoring senza rompere le interfacce esportate. Ma forse c'è un po 'di sottigliezza perché ciò romperebbe la retrocompatibilità, o sarebbe altrimenti male.

    
risposta data 26.08.2015 - 21:30
fonte

Leggi altre domande sui tag