2010-04-30 9 views
7

Cosa succede se alcuni metodi di fabbrica restituiscono tipi di metodi non di tipo pubblico e pairing che forniscono variabili di questo tipo non pubblico? Ciò si verifica con il messaggio di avviso denominato in NetBeans.Esportazione di tipo non pubblico tramite API pubblica

Nel risultato API pubblica conterrà solo due insiemi di metodi. La ragione è di rendere il mio tipo gerarchico sigillato (come le classi seald in Scala) e consentire agli utenti di creare istanze di questo tipo solo attraverso metodi di fabbrica. Quindi otteniamo DSL in un certo senso.

Ad esempio, la classe di pianificazione rappresentata dai contrappunti dei campi del calendario. Esistono alcuni tipi di contrappunti: Range, Singleton, List, FullSet - con l'interfaccia NumberSet come root. Non vogliamo esporre questi tipi e il modo in cui Schedule interagisce con loro. Vogliamo solo le specifiche dell'utente. Quindi rendiamo NumberSet pacchetto-privato. In orario delle lezioni creiamo qualche fabbrica-metodi per vincoli:

NumberSet singleton(int value); 
NumberSet range(int form, int to); 
NumberSet list(NumberSet ... components); 

e alcuni metodi per la creazione di oggetto Pianificazione:

Schedule everyHour(NumberSet minutes); 
Schedule everyDay(NumberSet minutes, NumberSet hours); 

utente le può usare solo nei modi:

Schedule s = Schedule.everyDay(singleton(0), list(range(10-15), singleton(8))); 

È una cattiva idea?

risposta

9

L'idea stessa è valida. Ma non funzionerà, se si rende il pacchetto root root (qui: NumberSet) privato. Esporre questo tipo o utilizzare invece un'interfaccia pubblica. I veri tipi di implementazione dovrebbero (e possono) essere nascosti.

public abstract class NumberSet { 

    // Constructor is package private, so no new classes can be derived from 
    // this guy outside of its package. 
    NumberSet() { 
    } 
} 

public class Factories { 

    public NumberSet range(int start, int length) { 
     return new RangeNumberSet(start, length); 
    } 

    // ... 
} 

class RangeNumberSet extends NumberSet { 
    // ... must be defined in the same package as NumberSet 
    // Is "invisible" to client code 
} 

Modifica Per esporre un tipo di nascosto/privato radice da un'API pubblica è un errore. Si consideri il seguente scenario:

package example; 

class Bar { 
    public void doSomething() { 
      // ... 
    } 
} 

public class Foo { 

    public Bar newBar() { 
     return new Bar(); 
    } 
} 

e considerare un'applicazione client che utilizza questa API. Cosa può fare il cliente? Non può dichiarare correttamente una variabile per avere il tipo Bar poiché quel tipo è invisibile a qualsiasi classe esterna al pacchetto example. Non può nemmeno chiamare i metodi public su un'istanza Bar che ha ricevuto da qualche parte, poiché non sa, che un tale metodo pubblico esiste (non può vedere la classe, per non parlare dei membri che espone). Quindi, il meglio che un cliente può fare è qualcosa del tipo:

Object bar = foo.newBar(); 

che è essenzialmente inutile. Una cosa diversa sarebbe avere un'interfaccia pubblica (o una classe astratta) al posto del pacchetto privato, come nel codice sopra definito. In questo caso, il cliente in realtà può dichiarare una variabile di tipo NumberSet. Non può creare le proprie istanze o derivare sottoclassi, poiché il costruttore è nascosto da esso, ma può accedere all'API pubblica definita.

modificare nuovamente Anche se si desidera un valore di "informe" (dal punto di vista del cliente), vale a dire, un valore, che non definisce alcun API interessanti il ​​cliente può decidere di chiamare, è ancora una buona idea esporre un tipo di base pubblica nel modo descritto sopra. E sia per il solo scopo che il compilatore sia in grado di eseguire il controllo di tipo e che consenta al codice client di memorizzare temporaneamente tale valore in una variabile (dichiarata correttamente).

Se non si desidera che il client chiami alcun metodo API sul tipo: va bene. Non c'è nulla che ti impedisce di non fornire un'API pubblica sul tuo (altrimenti) tipo di base pubblico. Basta non dichiararne nessuno. Utilizzare una classe di base astratta "vuota" (vuota dal punto di vista del client, poiché tutti i metodi interessanti sarebbero pacchetti privati ​​e quindi nascosti). Tuttavia, è necessario fornire un tipo di base pubblico, oppure è necessario utilizzare il valore Object come valore di ritorno, ma in tal caso si perde il controllo degli errori in fase di compilazione.

Regola generale: se il cliente deve chiamare un metodo in modo da ottenere un valore e passare a qualche altro API, il client in realtà sa, che ci sono alcuni valori speciali magici. E deve essere in grado di gestirlo in qualche modo ("passarlo avanti", almeno). Non fornire un tipo corretto per il client per gestire i valori non ti compra nulla tranne per gli avvisi del compilatore (del tutto appropriato).

public abstract class Specification { 

    Specification() { 
     // Package private, thus not accessible to the client 
     // No subclassing possible 
    } 

    Stuff getInternalValue1() { 
     // Package private, thus not accessible to the client 
     // Client cannot call this 
    } 
} 

La classe precedente è "vuota" per quanto riguarda il codice client; non offre un'API utilizzabile eccetto cose, che lo Object offre già. Il vantaggio principale di averlo: il client può dichiarare variabili di questo tipo e il compilatore è in grado di digitare check. Il tuo framework, tuttavia, rimane l'unico posto dove possono essere create istanze concrete di questo tipo e, quindi, il tuo framework ha il controllo totale su tutti i valori.

+0

Perché non funziona, se rendiamo il tipo root pacchetto-privato? –

+0

Sì, ma è il client indesiderato a vedere questo tipo. Abbiamo solo bisogno di specificare attraverso metodi di fabbrica e di passarlo attraverso i metodi di fabbrica per gli oggetti di pianificazione. –

0

Mi vengono in mente due modi per fare questo, ma nessuno realmente funzionare:

  1. Dichiarare NumberSet come pacchetto privato, ma modificare i metodi API pubbliche che attualmente utilizzano il tipo NumberSet da utilizzare Object invece, e fare in modo che le implementazioni trasmettano gli argomenti da Object a NumberSet come richiesto. Questo dipende dal fatto che i chiamanti passino sempre il giusto tipo di oggetto e saranno soggetti a ClassCastException s.

  2. implementare una pubblica NumberSet interfaccia con alcun metodo ed il pacchetto privato InternalNumberSet che implementa NumberSet e definisce i metodi richiesti internamente. Digitare il cast viene utilizzato ancora una volta per trasmettere gli argomenti NumberSet a InternalNumberSet. Questo è meglio l'approccio precedente, ma se qualcuno crea una classe che implementa NumberSet senza implementare anche InternalNumberSet, il risultato sarà ClassCastException s ancora una volta.

Personalmente ritengo che si debba dichiarare l'interfaccia NumberSet come pubblica. Non vi è alcun danno reale nell'esporre i getter nell'interfaccia e, se si desidera che le classi di implementazione siano immutabili, dichiararle come definitive e non esporre i setter.

Problemi correlati