Come ridurre al minimo la superficie dell'interfaccia esposta di una libreria Java mentre si abbatte la libreria in pacchetti secondari?

5

Sto creando una libreria o due per l'app Android e voglio mantenere le interfacce esposte della libreria al minimo per evitare che l'astrazione si diffonda ovunque, evitando di rendere pubbliche tutte le classi. Sto anche seguendo la struttura del pacchetto di Maven .

Venendo da C #, ho usato il modificatore di accesso internal per ottenere ciò che voglio; se non ti è familiare, internal concede l'accesso a qualsiasi cosa all'interno dell'assieme, ma non al di fuori di esso. Quindi le poche interfacce pubbliche sono veramente pubbliche. L'analogia più vicina, secondo me, è package-private in Java, ma non funziona quando organizzo la mia libreria in pacchetti secondari.

Ad esempio, supponiamo di avere una classe Service1 in root e un'altra classe Service2 in un sottopackage sotto root. Mi piacerebbe che Service1 sia in grado di fare riferimento all'interfaccia di Service2 , ma preferibilmente non rendere Service2 public poiché esporrà l'interfaccia di Service2 al di fuori del pacchetto, che non è quello che Intendo.

Immagino che questo non sia terribilmente complicato, ma sono un po 'confuso su come consentire ai pacchetti parent / di padre di accedere alle classi del pacchetto secondario senza renderle public . L'unica soluzione che ho potuto veramente pensare è di 1) mettere tutto allo stesso livello di pacchetto che mi permetterebbe di usare package-private per nascondere tutto ciò che non dovrebbe essere pubblicamente disponibile ma è estremamente disordinato o 2) succhialo e rendi pubbliche le mie interfacce, il che ovviamente offende la mia sensibilità.

Come si fa normalmente? Tutte le classi sono nello stesso pacchetto, ma possibilmente organizzate da sottodirectory? Ho ancora un approccio che mi piace davvero.

    
posta JosephRT 09.05.2018 - 04:59
fonte

4 risposte

4

In Java, la superficie dell'API desiderata dovrebbe definire la struttura del pacchetto. Evita la tentazione di creare pacchetti secondari solo per "organizzare" i tuoi file in un modo che non riguarda direttamente la progettazione dell'API. La percezione della necessità di farlo potrebbe anche suggerire una scarsa progettazione dell'API.

Tenendo presente questo, potresti comunque voler utilizzare un sotto pacchetto per creare un'API interna. Ad esempio, un gruppo di classi potrebbe desiderare che determinati membri siano accessibili ad altre classi nel gruppo, ma non esporre tali membri al resto della libreria. In tal caso, inseriscili in un subpackage e approfitti dell'accesso privato al pacchetto. È necessario riflettere attentamente se l'API esposta del pacchetto secondario è qualcosa che si desidera progettare, documentare e supportare come prodotto separato. Fare ciò renderà il tuo prodotto più prezioso, ma ovviamente a un certo costo per te. Se non vuoi supportare l'API interna, o non sei ancora sicuro, la solita soluzione è mettere quell'API in un subpackage con un nome che lo identifica come interno, come com.myproduct.internal , o anche completamente diverso gerarchia dei pacchetti, come le classi com.sun esposte per necessità ma che non fanno ufficialmente parte dell'API Java standard.

    
risposta data 09.05.2018 - 21:43
fonte
4

Questo potrebbe non essere applicabile a te dal momento che non so se Android lo supporta, ma la caratteristica principale dell'aggiornamento Java 9 riguarda esattamente questo problema.

La funzionalità dei moduli di Java 9 (noto come Project Jigsaw) ti dà la possibilità di aggiungere un file di metadati a un file .jar che (tra le altre cose) elenca i pacchetti che sono pubblicamente visibili. Tutti gli altri pacchetti sono interni al modulo. In altre parole, le classi pubbliche all'interno di un modulo interno sono equivalenti alle classi interne in un assembly .Net.

    
risposta data 09.05.2018 - 10:59
fonte
1

"Forse organizzato da sottodirectory" non ha senso, dato che ogni sottodirectory è un nuovo pacchetto in Java.

In genere viene documentato quale sia l'interfaccia pubblica per pacchetto e le classi di implementazione del documento come aperte al cambiamento, utilizzate queste direttamente a proprio rischio.

Un modo in cui potresti anche usarlo, e ho visto questo usato in modo efficace, è dividere la libreria in 2 file jar. Un file contenente solo le interfacce per l'API pubblica e un secondo che contiene tutto il resto. Fornire un'ampia documentazione per le interfacce e accanto a nessuna per le classi di implementazione, nella versione rilasciata al pubblico.

E confida nei tuoi utenti. Fidati di loro per attenersi alla tua API pubblica, la maggior parte lo farà se è progettato abbastanza bene che non è necessario pasticciare direttamente con gli interni, semplicemente perché fare confusione con i tuoi interni renderebbe il loro lavoro molto più fragile.

    
risposta data 09.05.2018 - 10:51
fonte
1

Sono d'accordo con le altre risposte qui, che il modo standard di Java è di non creare pacchetti aggiuntivi o usare una chiamata di pacchetto interna. Volevo indicare un'altra opzione che hai con Android.

Android ha un'annotazione di supporto, RestrictTo . Chiunque usi il codice annotato al di fuori dell'ambito appropriato riceve un avviso di sfilacciamento.

link

Ciò consentirebbe di annotare classi, metodi o campi come limitati alla libreria stessa.

@RestrictTo(RestrictTo.Scope.LIBRARY)

Ci sono anche altre opzioni per l'ambito. Questo è lo schema seguito da Google con la libreria di supporto.

    
risposta data 22.05.2018 - 22:40
fonte

Leggi altre domande sui tag