sabato 27 marzo 2021

Refactoring: improving encapsulation

 

Incapsulamento

L'incapsulamento è la proprietà che hanno le classi di nascondere i dettagli di una certa funzionalità o servizio ed esporre un'interfaccia che consenta al client di poterla utilizzare ad un maggior livello di astrazione.

L'incapsulamento attua il principio dell'information-hiding, che è uno dei principali cardini della programmazione. Affinché questo sia ben progettato, occorre quindi che siano nascosti al client i dettagli implementativi, e che modifiche dell'implementazione non rendano necessarie variazioni dei client.
Esamineremo alcuni problemi che si possono presentare sull'incapsulamento e vedremo come risolverli.


Insufficiente incapsulamento

Avviene quando una classe espone dei dettagli implementativi esponendo uno o più membri, creando quindi un accoppiamento indesiderato che esporrà i client ad un impatto qualora l'implementazione cambi. 
Il peggiore dei casi è esporre variabili globali che possono dunque essere modificate da più client.
A volte può essere dovuto all'applicazione di soluzioni quick-fix, o a volte le informazioni sono rese pubbliche per poterle verificare in unit test, oppure semplicemente si è progettata male la classe pensandola in termini procedurali anziché OOP.

Soluzioni possibili

  • evitare l'esposizione di variabili o membri di classe e al loro posto usare degli accessors di lettura o scrittura o dei metodi appositi, oppure esplicitarne la dipendenza in un costruttore
  • utilizzare l'uso di parametri espliciti per le funzioni invece di configurazioni tramite variabili globali, considerare l'uso di uno strategy pattern o di un builder pattern.
  • non esporre classi o strutture utilizzate nell'implementazione, anche qui potrebbe essere indicato un oggetto builder.

Incapsulamento parziale

Avviene quando una classe espone dei dettagli implementativi  attraverso la sua interfaccia pubblica.
Anche in questo caso è molto difficile in seguito cambiare l'implementazione della classe senza cambiare il codice dei client.
Quando si progetta una classe, si deve avere ben chiara la sua funzionalità (principio della single-responsibility) e definirne un'interfaccia che non sia sensibile ai cambiamenti dell'implementazione. Stabilire "cosa nascondere" (implementazione) e "cosa mostrare" (interfaccia) è probabilmente l'aspetto più importante in fase di progettazione e richiede esperienza e riflessione.

Soluzioni possibili

  • non esporre metodi collegati all'implementazione bensì focalizzarsi su un'interfaccia il più possibile generica, seppur completa di tutte le funzionalità indispensabili.
  • non fare proliferare il numero dei metodi di un'interfaccia, semmai considerare uno strategy pattern o la creazione di sottoclassi se si intendono fornire comportamenti simili ma diversi nella stessa classe. Questo serve a mantenere la classe semplice e ridurre l'accoppiamento degli utilizzatori. Infatti il solo fatto di sapere che una classe ha dieci metodi per eseguire un calcolo e sceglierne uno, rappresenta un accoppiamento: cosa dovrà fare quando ne sarà aggiunto l'undicesimo?


Incapsulamento violato

Si ha quando un client utilizza dei type check espliciti al fine di effettuare dei downcasting invece di  avvalersi in pieno delle interfacce.
Questo fa si che il client debba essere a conoscenza dei diversi tipi concreti che potrebbero implementare l'interfaccia, rendendo l'information hiding che era alla base della creazione dell'interfaccia del tutto violato.

Soluzioni possibili

  • non usare type-checking nell'implementazione di metodi di oggetti che espongono un'interfaccia, ossia se un parametro è astratto, non tentare di cambiare il comportamento di un metodo in base al tipo specifico del parametro. L'uso del type checking di solito è indice di un'interfaccia mal progettata.
  • ove necessario creare interfacce aggiuntive se si vuole verificare la presenza di determinate funzionalità in un oggetto, invece di verificarne il tipo esatto
  • individuare i motivi di violazione dell'incapsulamento e valutare l'introduzione di un pattern facade



Nessun commento:

Posta un commento

Refactoring: improving modularization

  Modularità In generale, per modularità si intende la misura in cui un sistema può essere decomposto e ricombinato. La modularizzazione è a...