Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

O - Open/Close Principle

Entità che non necessitino di essere modificate a fronte dei cambiamenti delle applicazioni, ma che possano essere estese con facilità
Entità che non necessitino di essere modificate a fronte dei cambiamenti delle applicazioni, ma che possano essere estese con facilità
Link copiato negli appunti

In questo articolo scopriremo come organizzare il nostro codice in modo da renderlo il più possibile flessibile e resistente alle comuni evoluzioni del software. Tratteremo infatti del secondo dei cinque principi del SOLID: “Software entities should be open for extension, but closed for modification.”.

L'obiettivo dell'Open/Close Principle è:

“ Progettare entità che non necessitino di essere modificate a fronte dei naturali cambiamenti delle nostre applicazioni, ma che possano essere estese con estrema facilità.”

Per capire meglio facciamo subito un esempio: Supponiamo di essere nel pieno dello sviluppo di un software di CMS che potremmo chiamare YACS (Yet Another CMS Solution): per distinguerci un po' dalla massa di prodotti simili abbiamo deciso di creare una dashboard di amministrazione che presenti secondo un particolare ordine tutti i contenuti creati indipendentemente dalla loro tipologia: Pagine, Post, Commenti, Documenti ed Immagini.

Ognuno degli oggetti di cui sopra dovrà essere visualizzato nella dashboard secondo le proprie peculiarità, ad esempio:

Figura 5. La dashboard del nostro CMS
La dashboard del nostro CMS

Supponendo di avere un oggetto che sia responsabile della gestione della dashboard (DashboardController), una prima soluzione potrebbe essere quella di creare un metodo con un ciclo che comprenda tutti gli oggetti da stampare e che per ogni iterazione stampi il contenuto di Data, Tipologia, Titolo e Descrizione in accordo con il tipo di oggetto. Ecco lo pseudocodice:

// metodo index
per ogni oggetto_da_stampare
  se è una pagina
    data = pagina.data_di_creazione
    tipologia = 'Pagina'
    titolo = pagina.titolo
    descrizione = i primi 200 caratteri di pagina.testo
    ...
  se è una immagine
    data = immagine.data_di_upload
    tipologia = 'Immagine'
    titolo = immagine.didascalia
    descrizione = una versione ridimensionata di immagine.src
    ...

Ora ipotizziamo di voler estendere la dashboard aggiungendo la possibilità selezionare e disabilitare contenuti:

Figura 6. Abilitare e disabilitare contenuti sulla dashboard
Abilitare e disabilitare contenuti sulla dashboard

Anche in questo caso il codice che gestisce questa porzione del CMS potrebbe basarsi sulla stessa
logica del precedente:

// metodo disabilitazione_di_massa
per ogni oggetto_selezionato
  se è una pagina
    pagina.disabilitata = true
    per ogni documento_legato_alla_pagina
    documento.disabilitato = true
  se è un commento
    commento.disabilitato = true
    manda una mail a commento.autore informandolo della disabilitazione
  se è un documento
    documento.disabilitato = true
    ...

Perfetto... dove stiamo sbagliando ? Semplice, proviamo a pensare di aggiungere al CMS la
gestione di un nuovo tipo di contenuto, ad esempio Prodotto, in quanti punti dovremmo
intervenire
per incorporare questo nuovo oggetto ?

  • nella nuova classe Prodotto;
  • nei metodi index e disabilitazione_di_massa per spiegare al DashboardController come trattare questi casi con oggetti di tipo Prodotto.

La classe DashBoardController è decisamente troppo invischiata nella logica degli oggetti che
gestisce!

Ogni cambiamento in uno di questi oggetti o ogni aggiunta di un nuovo oggetto si ripercuote anche su questa classe. Alla lunga una architettura come questa genera un prodotto i cui componenti sono così interdipendenti da non poter più essere riutilizzati per altro, e c'è di più: mantenere un software costruito in questo modo è una vera propria mission impossible, come possiamo sapere in quanti punti una modifica ad uno specifico oggetto debba essere propagata?

Astrazione, comportamenti ed interfacce

La soluzione suggerita dal secondo dei principi SOLID è quella di creare un oggetto che sappia fare
da cuscinetto tra chi deve orchestrare l'azione (DashBoardController) e chi invece possiede le
informazioni per eseguirla (Pagina, Post, ...). Ecco uno schema:

Figura 7. Separazione tra azione e contenuto
Separazione tra azione e contnuto

Come si intuisce facilmente stiamo parlando di astrazione: la classe Contenuto non è altro che il padre di Pagina, Post, Commento, Documento e Immagine.

Questa strategia ci consente di definire due metodi astratti sommario e disabilita in Contenuto che poi ognuna delle classi figlie dovrà implementare in accordo con le azioni richieste dallo specifico tipo di contenuto che rappresenta. In questo modo DashBoardController potrà limitarsi ad invocare il metodo sommario e disabilita per ognuno degli oggetti della collezione, senza preoccuparsi della reale operatività di questi metodi:

// metodo index
per ogni oggetto_da_stampare
  dati_per_la_tabella = oggetto_da_stampare.sommario()

// metodo disabilitazione_di_massa
per ogni oggetto_selezionato
  oggetto_selezionato.disabilita()

Ottimo diremmo, ma ora stiamo aderendo al secondo principio SOLID ma stiamo violando il primo: supponiamo infatti di voler fare in modo che nell'elenco di oggetti della dashboard compaiano anche gli utenti: sarebbe fantastico se anche la classe Utente potesse beneficiare dei metodi sommario e disabilita ma ovviamente non possiamo farla discendere da Contenuto!

In realtà quello che abbiamo scoperto è che sommario e disabilita rappresentano due comportamenti (o behaviors) che possono appartenere anche a classi non necessariamente legate da altri aspetti logici. Possiamo dire che gli oggetti che posseggono queste proprietà siano Riassumibili e Disabilitabili.

Da questo deriva che è la sola appartenenza ad uno dei due gruppi a definire se l'oggetto può essere stampato nella dashboard e/o può essere disabilitato.

Esistono modi diversi per raggiungere questo risultato, si possono ad esempio utilizzare delle interfacce che descrivano i metodi che poi dovranno essere implementati dalle classi che vogliono attivarsi sul quel particolare comportamento.

Conclusioni

È difficile ottenere una struttura aderente all'Open Close Principle al 100%, esisteranno sempre aspetti che sfuggiranno al nostro controllo; l'importante è che tali aspetti siano quelli con la minor probabilità di cambiare nel corso del tempo. Anche per il secondo dei 5 principi del SOLID l'esperienza riveste quindi un ruolo decisamente importante.


Ti consigliamo anche