Il concetto di controllo di accesso ci permette di limitare come tipi, funzioni e altre dichiarazioni possono essere accessibili da altro codice. Swift offre cinque diversi livelli di controllo degli accessi, e fare pieno uso di loro può essere fondamentale al fine di scrivere programmi che hanno chiaramente separati preoccupazioni e una struttura robusta.

Quando definiamo qualsiasi nuovo tipo, proprietà o funzione in Swift, avrà il livello di accesso internal per impostazione predefinita. Ciò significa che sarà visibile a tutti gli altri codici che vivono all’interno dello stesso modulo, come un’app, un’estensione di sistema, un framework o un pacchetto Swift.

Ad esempio, diciamo che stiamo costruendo un’app per lo shopping e che abbiamo definito una classe chiamata PriceCalculator che ci consente di calcolare il prezzo totale per una serie di prodotti:

Poiché al momento non stiamo specificando alcun livello di accesso esplicito, la nostra classe PriceCalculator (e il suo metodo calculatePrice) sarà accessibile da qualsiasi luogo all’interno della nostra app. Tuttavia, se stiamo cercando di condividere la nostra nuova classe con altri moduli (si potrebbe, per esempio, di implementare all’interno di un quadro che condividiamo tra la nostra app principale e un’estensione, o un compagno di Apple Watch app), quindi abbiamo bisogno di rendere public in modo che sia visibile all’interno di tali contesti esterni:

Tuttavia, il cambiamento non è abbastanza. Mentre ora siamo in grado di trovare la nostra classe al di fuori del modulo in cui è definita, non possiamo crearne alcuna istanza, poiché il suo inizializzatore (implicito) è, proprio come qualsiasi altro codice, internal per impostazione predefinita. Per risolvere questo problema, definiamo un inizializzatore public, che lasceremo vuoto poiché non c’è lavoro effettivo da fare al suo interno:

public class PriceCalculator { public init() {} ...}

Ora siamo in grado di trovare, inizializzare e chiamare il nostro PriceCalculator sia all’interno che all’esterno del suo modulo — fantastico. Ma diciamo ora che stiamo anche cercando di sottoclasse al fine di modificarlo, o per aggiungere nuove funzionalità ad esso. Mentre questo è attualmente possibile all’interno del proprio modulo, è di nuovo qualcosa che è impedito al di fuori di esso.

Per cambiarlo, dovremo utilizzare il livello di controllo degli accessi attualmente più aperto di Swift, che è denominato in modo appropriato open:

open class PriceCalculator { ...}

Con la modifica di cui sopra, ora possiamo creare sottoclassi personalizzate di PriceCalculator ovunque, che possono avere nuovi inizializzatori, nuove proprietà e nuovi metodi. Ecco come potremmo usarlo per implementare un DiscountedPriceCalculator, che ci consente di applicare un dato discount a tutti i calcoli dei prezzi:

Sopra stiamo definendo un nuovo metodo di calcolo del prezzo, ma sarebbe probabilmente molto più appropriato sovrascrivere e modificare il metodo calculatePrice esistente che abbiamo ereditato dalla nostra classe base. In questo modo, non ci sarebbe confusione su quale metodo chiamare, e potremmo mantenere coerenti le nostre due classi.

Per poterlo fare, dobbiamo nuovamente contrassegnare la dichiarazione originale — questa volta la nostra dichiarazione del metodo calculatePrice — come open:

open class PriceCalculator { public init() {} open func calculatePrice(for products: ) -> Int { ... }}

Con quanto sopra, ora possiamo sovrascrivere liberamente calculatePrice, piuttosto che dover creare un metodo separato:

Quindi è internal, public e open — che vengono utilizzati per aprire gradualmente una dichiarazione per uso pubblico e modifica. Ma ovviamente possiamo anche andare dall’altra parte e nascondere che parti del nostro codice vengano scoperte e utilizzate. All’inizio, può sembrare discutibile quale sia il valore nel farlo, ma può davvero aiutarci a rendere la nostra API molto più ristretta e focalizzata, il che a sua volta può rendere più facile la comprensione, il test e l’uso.

Quindi andiamo fino all’altro lato dello spettro del livello di accesso e diamo un’occhiata al livello più restrittivo — private. Qualsiasi tipo, proprietà o metodo contrassegnato come private sarà visibile solo all’interno del proprio tipo (che include anche estensioni su quel tipo definito nello stesso file).

Tutto ciò che dovrebbe essere considerato un dettaglio di implementazione privata di un determinato tipo dovrebbe probabilmente essere contrassegnato come private. Ad esempio, la proprietà discount del nostro calcolatore dei prezzi di prima era pensata solo per essere utilizzata all’interno della propria classe, quindi andiamo avanti e rendiamo privata quella proprietà:

class DiscountedPriceCalculator: PriceCalculator { private let discount: Int ...}

La nostra precedente implementazione continuerà a funzionare esattamente come prima, poiché discount rimarrà interamente visibile all’interno della nostra classe DiscountedPriceCalculator. Tuttavia, se volessimo estendere leggermente tale visibilità per includere anche altri tipi definiti all’interno dello stesso file, dovremmo usare

class DiscountedPriceCalculator: PriceCalculator { fileprivate let discount: Int ...}

Con la modifica di cui sopra, ora possiamo accedere alla nostra proprietà discount dal codice correlato definito nello stesso file — come questa estensione su UIAlertController che ci consente di mostrare facilmente una descrizione del prezzo per una serie di prodotti all’interno di un avviso:

Quando si tratta di funzioni, tipi ed estensioni libere, private e fileprivate agiscono esattamente allo stesso modo. Sono diversi solo se applicati a dichiarazioni definite all’interno di un tipo.

Quindi, per riassumere, questi sono i cinque livelli di controllo degli accessi che Swift offre attualmente:

  • private mantiene una proprietà o una funzione privata all’interno del suo tipo di inclusione, incluse le estensioni di quel tipo definite all’interno dello stesso file. Se applicato a un tipo, una funzione o un’estensione di livello superiore, agisce allo stesso modo di fileprivate.
  • fileprivate rende visibile una dichiarazione all’interno dell’intero file in cui è definita, nascondendola da tutti gli altri codici.
  • internal è il livello di accesso predefinito e rende visibile una dichiarazione all’interno dell’intero modulo in cui è definita.
  • public rivela una funzione, un tipo, un’estensione o una proprietà al di fuori del suo modulo.
  • open consente di sottoclassare una classe e di sovrascrivere una funzione o una proprietà al di fuori del suo modulo.

In generale, è spesso meglio iniziare con il livello di accesso più restrittivo che una determinata dichiarazione può praticamente avere, e quindi aprire le cose in seguito se necessario. In questo modo stiamo limitando le strade per l’interazione tra i nostri vari tipi e funzioni, che possono a prima vista sembrare una brutta cosa, ma è spesso veramente essenziale per costruire sistemi gestibili e ben strutturati.

Grazie per la lettura! Note

(Si noti che questo articolo non è andato in modificatori di accesso specifici della mutazione, come private(set). Quelli saranno coperti da un altro articolo di base in futuro.)

Supporto Swift da Sundell controllando questo sponsor:

Instabug: Risolvi bug, crash e altri problemi molto più velocemente utilizzando le tracce dettagliate dello stack, i registri di rete e gli eventi dell’interfaccia utente che Instabug attribuisce automaticamente a ogni segnalazione di bug. Utilizzato sia da me che da migliaia di team di sviluppo iOS in tutto il mondo. Provalo gratuitamente e integralo con una sola riga di codice.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato.