Progettazione del software – parte II



I tre princìpi della OOP

La programmazione orientata agli oggetti si basa su tre princìpi fondamentali:

Incapsulamento

L’incapsulamento è un evoluzione della relazione “has a” della programmazione strutturata, esso prevede che la classe abbia sì gli attributi e i metodi, ma aggiunge il concetto di modificatore d’accesso ovvero prevede che gli attributi di un oggetto possano essere accessibili agli altri oggetti solo se chi ha progettato quella classe ha previsto che ciò sia possibile. Non è escluso infatti che determinati attributi o determinati metodi servano solo per uso interno della classe e oggetti esterni non debbano richiamare quei campi (termine generico per indicare attributi e metodi).

Oppure possono esserci casi dove l’attributo può essere letto e/o scritto ma in modo controllato e quindi si blocca l’accesso diretto al campo e si crea un metodo apposito che oltre far modificare o disporre all’oggetto esterno il valore del campo, esegue anche le operazioni di controllo sulla validità del valore.

Ereditarietà

L’ereditarietà invece è quel principio che aggiunge alle strutture dati create dal programmatore la relazione “is a” ovvero “è un”.

Questo permette di rendere altamente astratte determinate parti del progetto e di poterle quindi riutilizzare in altri progetti. Facendo un esempio, se si creasse la classe Persona per utilizzare vari oggetti rappresentanti diversi individui con il loro nome, cognome e con un insieme di funzioni tipiche di una persona come saluta, parla e simili, per creare una nuova classe Dipendente basterà indicare la relazione “is a” fra dipendente e persona, perché un dipendente è pur sempre una persona ma con qualche attributo e funzione in più come stipendio, lavora e simili.

La grande potenza dell’ereditarietà si ha nel fatto che tutta quella parte del sistema che lavora sulla struttura dati Persona, può lavorare tranquillamente su qualsiasi altra classe che deriva da Persona, quindi posso far usare la classe Dipendente o una sua derivata più specifica come Operaio, Impiegato, Dirigente che sarebbero ugualmente accettati come dati in quanto qualsiasi nuova classe che eredita da persona è una persona.

Polimorfismo

Questa caratteristica è strettamente legata all’ereditarietà; infatti prevede la possibilità di dare a determinate funzioni con lo stesso nome a due o più classi che ereditano da una stessa classe base, e di inserire un comportamento diverso in ogni classe derivata.

Proseguendo con l’esempio delle persone e dei dipendenti posso creare dentro la classe Dipendente la funzionalità lavora e definire un comportamento diverso in Operaio, Impiegato, Dirigente fino a poter definire la funzionalità in modo molto specifico in classi particolarmente concrete come Cassiere, Manager ecc…

Il polimorfismo scatta nel momento in cui quella parte del sistema che si aspetta un dato di tipo Dipendente e deve richiamare la funzione per farlo lavorare, capisce se il dato passato è in realtà un oggetto di tipo Operaio, Autista o in genere di che tipo di definizione e chiamerà la versione della funzionalità lavora dell’oggetto passato in realtà, e non quello generico della classe Dipendente.

Per sfruttare al massimo questa caratteristica è nato il concetto di classe astratta ovvero una classe dalla quale non si possono creare oggetti concreti visto il concetto troppo astratto che esprime. Basti pensare proprio alla classe Dipendente citata fino ad ora: potrebbe rappresentare qualsiasi mestiere e sarebbe impossibile descrivere nella pratica in un linguaggio di programmazione o in un progetto la generica funzione lavora; quindi la potremmo definire astratta in quanto fa solo da base per la creazione di classi sempre più concrete da usare in futuro, e al tempo stesso però dire al resto del sistema di lavorare genericamente sul dato Dipendente in modo che sfruttando la relazione “è un” il software potrà essere mantenibile facilmente ed esteso senza dover andare a modificare a monte il resto del progetto.

La relazione di dipendenza: usa

Si è detto fino ad ora che una volta delineate le classi di oggetti da usare (o riusare) e una volta creati concretamente le istanze necessarie al funzionamento del software da realizzare, bisogna far interagire questi oggetti. E’ qui che sorge la relazione di dipendenza.

Fino ad ora abbiamo visto che una classe può avere attributi (che possono essere tipi base o oggetti), può essere un’estensione di un’altra classe, ma può ovviamente usare altre classi. La relazione di dipendenza naturalmente ingloba anche le relazione di aggregazione (has a) e ereditarietà (is a), e si applica in generale a quelle classi che sono a conoscenza dell’esistenza di un’altra classe, senza la quale non potrebbero portare a termine i loro compiti. È evidente che più dipendenze ci sono meno flessibile è il sistema in quanto se le dipendenze sono troppo intrecciate una modifica anche minima potrebbe portare alla necessità di modificare anche altre classi.

Fasi per lo sviluppo del software

Le fasi necessarie per ottenere un software funzionante partendo da un problema sono:

Analisi

In questa fase si stende la così detta specifica funzionale, dove si specificano i compiti che devono essere eseguiti e lavorano assieme sia gli analisti-programmatori sia gli esperti del problema da implementare ed è per questo che questa fase non deve essere caratterizzata da troppe indicazioni di carattere tecnico in quanto sullo stesso lavoro collaborano anche quella persone non addette ai lavori di sviluppo software.

Ad esempio in un software gestionale per aziende la fase di analisi prevede una stretta collaborazione fra coloro che poi progetteranno e realizzeranno il software e coloro che invece sono gli esperti del problema economico da implementare; più in generale questa fase è l’intervista al cliente per capire esattamente cosa vuole che venga implementato nel software.

Progettazione

Questa è la fase più importante visto che getta le basi per una realizzazione veloce e con meno complicazioni possibili. Arrivati a questo punti i progettisti, partendo dall’analisi fatta precedentemente, individuano le classi necessarie, le loro responsabilità e caratteristiche e le relazioni fra le altre classi. Ovviamente questa è anche la fase dove si deve cercare di riutilizzare classi progettate in passato per altri progetti in modo da non dover fare più volte lo stesso lavoro.

In questa fase gioca un ruolo importante la forma con cui viene studiato il sistema, e viene moltissimo usato un linguaggio apposito: UML: Unified Model Language; esso tramite rappresentazioni grafiche permette di descrivere dettagliatamente le informazioni sopracitate.

Realizzazione.

Questa è la fase più complicata ovviamente: consiste nel tradurre il progetto fatto fino a quel punto in un linguaggio di programmazione. La OOP aiuta a realizzare determinate classi o gruppi di classi dipendenti fra loro e farli interagire man mano da soli potendo effettuare il collaudo su porzioni più piccole del programma e mano a mano si integra con nuove unità collaudate fino a raggiungere il lavoro finale.

E’ ricorrente in progetti complessi che durante la realizzazione si creino dei prototipi da mostrare al cliente o agli esperti del problema reale per poter eventualmente riaprire la fasi di analisi e quindi progettazione per fare correzioni o altre modifiche di fondo delle quali ci si accorge solo più avanti o dovute a incomprensioni iniziali.

Annunci sponsorizzati:
Condividi su Facebook Condividi su Twitter!
Pinterest