Progettazione

Proge&azione Proge&azione ad ogge- •  Gli ogge- sono astrazioni di en2tà reali o di sistema –  In alcuni casi, ci potrebbe essere una corrispondenza ovvia tra en2tà del mondo reale e ogge- •  Gli ogge- sono indipenden2 e incapsulano il loro stato –  Le funzionalità del sistema sono espresse a&raverso le operazioni offerte dagli ogge- –  Le aree da2 condivise sono limitate e gli ogge- comunicano a&raverso messaggi –  Gli ogge- possono essere distribui2 e possono essere esegui2 in sequenza o in parallelo •  Gli ogge- sono potenzialmente componen2 riusabili Come iden2ficare gli ogge- •  L’iden2ficazione degli ogge- (classi) è la parte più difficile della proge&azione a ogge- •  Non esiste una “formula magica” –  Ci si basa su euris2che, abilità, esperienza e conoscenza del dominio applica2vo •  L’iden2ficazione degli ogge-/classi è un processo itera2vo –  Solitamente non si finisce con una sola iterazione –  Dis2nzione tra ogge-/classi del dominio e della soluzione Approcci possibili •  Approccio gramma2cale basato su una descrizione del sistema in linguaggio naturale •  Iden2ficazione degli “elemen2 tangibili” del dominio applica2vo •  Approccio “comportamentale” per iden2ficare gli ogge- in funzione di chi partecipa a quale comportamento •  Approccio basato su scenari per iden2ficare gli ogge-, a&ribu2 e metodi in ogni scenario •  Modellazione dei da2 (schema ER) completata con le operazioni •  Altri metodi … Analisi e proge&azione •  Metodi e notazioni simili •  Analisi ojbect-­‐oriented –  Modellazione del problema •  Proge&azione object-­‐oriented –  Modellazione della soluzione •  Spesso gli elemen2 iden2fica2 in fase di analisi migrano (rivis2) nella soluzione •  Il confine tra analisi e proge&azione non è ne&o Analisi e proge&azione OO Design Proge&azione •  La “corre&ezza” del proge&o è fondamentale –  ma possiamo anche valutarlo in funzione della sua “bontà”, efficienza, modificabilità, stabilità, ecc. •  Possiamo valutare un proge&o usando –  Accoppiamento, coesione e principio open-­‐closed Accoppiamento (coupling) •  Ca&ura il grado di interconnessione tra classi •  Un alto grado di accoppiamento significa –  Alta interdipendenza tra le classi –  Difficoltà di modifica della classe singola •  Un basso accoppiamento è requisito fondamentale per creare un sistema comprensibile e modificabile •  Nel mondo ad ogge- abbiamo tre 2pi di coupling: interac2on, component e inheritance Interac2on coupling •  Si ha quando metodi di una classe chiamano metodi di altre classi •  Le forme peggiori si hanno quando un metodo –  Manipola dire&amente le variabili di stato di altre classi –  Scambia informazioni a&raverso variabili temporanee •  Altre forme (minori) si hanno quando i metodi comunicano dire&amente via parametri –  I metodi dovrebbero passare la minor quan2tà di informazione possibile a&raverso il minor numero di parametri Component coupling •  Quando una classe A ha –  A&ribu2 di un’altra classe C –  Parametri di 2po C –  Metodi con variabili locali di 2po C •  Quando A è accoppiata con C, è accoppiata anche con tu&e le sue so&oclassi •  Component coupling solitamente implica anche la presenza di interac2on coupling Inheritance coupling •  Quando una classe è so&oclasse di un’altra •  La forma peggiore si ha quando una so&oclasse cambia la segnatura di un metodo ereditato –  Meglio se la segnatura resta la stessa, ma cambia l’implementazione •  Ancor meglio se la so&oclasse aggiunge solo metodi e a&ribu2, ma non modifica nulla Esempio Coesione •  Conce&o intra-­‐modulo •  Si concentra sul perché gli elemen2 stanno nello stesso modulo –  Solo gli elemen2 fortemente correla2 dovrebbero stare nello stesso modulo –  Il modulo rappresenterebbe una chiara astrazione e sarebbe più semplice da capire •  Alta coesione solitamente porta a basso accoppiamento •  Tre 2pi di coesione: method, class e inheritance Method cohesion •  Come la coesione all’interno di una funzione C –  La forma migliore si ha quando un metodo implementa una funzione singola e chiaramente definita –  Si dovrebbe poter descrivere il compito del metodo con una sola (semplice) frase Class cohesion •  Una classe dovrebbe rappresentare un solo conce&o e tu&e le proprietà dovrebbero contribuire alla sua rappresentazione •  Se si incapsulano conce- diversi (nella stessa classe), la coesione diminuisce •  Gruppi di metodi diversi che accedono a gruppi di a&ribu2 disgiun2 rappresentano un chiaro sintomo di bassa coesione Inheritance cohesion •  L’ereditarietà potrebbe essere indo&a dalla necessità di –  Generalizzazione/specializzazione –  Riuso •  La coesione è maggiore se la gerarchia viene usata per ges2re la generalizzazione/
specializzazione Metriche •  Weighted Methods per Class (WMC) –  La complessità di una classe dipende dal numero di classi e dalla loro complessità •  Supponiamo che la classe C abbia i metodi M1, M2, …, Mn •  Supponiamo che la complessità dei metodi sia c1, c2 •  WMC = Σ ci •  Chiaramente se la complessità di ogni metodo fosse 1, WMC sarebbe pari al numero di metodi della classe –  Un WMC alto potrebbe significare propensione ai dife- più elevata Metriche •  Una gerarchia di classi profonda significa –  Più metodi potenzialmente riusabili –  Alta coesione e quindi difficoltà di cambiamento •  Depth of Inheritance Tree (DIT) –  Il DIT di una classe è la distanza (shortest path) della classe dal nodo radice –  Il valore di DIT aiuta a predire la predisposizione ai dife- di una classe Metriche •  Number of Children (NOC) –  Numero di so&oclassi immediate della classe –  Valuta il grado di riuso –  Un NOC alto indica riuso delle definizioni da parte di un elevato numero di so&oclassi –  Indica anche l’influenza di una classe su altri elemen2 (le use so&oclassi) •  Alta influenza impone una proge&azione e una implementazione accurata –  Classi con NOC elevato non dovrebbero essere causa d’errore Metriche •  Coupling Between Classes (CBC) –  Riduce la modularità e rende i cambiamen2 più complessi –  CBC = Numero di classi con cui la classe C è accoppiata •  Due classi sono accoppiate se i metodi dell’una usano metodi o a&ribu2 dell’altra –  Può essere calcolato sul codice •  Ci sono forme dire&e di accoppiamento che non possono essere calcolate sta2camente –  Può fornire una previsione della dife&osità della classe Metriche •  Response for a Class (RFC) –  Il numero totale di metodi che possono essere invoca2 da un ogge&o della classe –  RFC è la cardinalità del response set della classe •  Insieme di tu- i metodi che possono essere invoca2 quando si manda un messaggio a un ogge&o della classe –  Tu- i metodi della classe e tu- i metodi invoca2 da ques2 •  Anche se il CBC di una classe è 1, il suo RBC potrebbe essere alto –  Ca&ura il “peso” delle connessioni –  Il test di classi con RFC elevato è più complesso Metriche •  Lack of Cohesion in Methods (LCOM) –  La coesione ca&ura la “vicinanza” tra i metodi di una classe •  Due metodi formano una coppia coesa se accedono a variabili/a&ribu2 comuni •  Formano una copia non coesa se non ci sono variabili in comune •  L’alta coesione è auspicabile –  LCOM è il numero di coppie di metodi che sono non coesi meno il numero di coppie coese –  Non significa2vo per predire la possibile dife&osità della classe Gli studi dicono che (I) •  Weighted Methods per Class (WMC) –  La maggior parte delle classi ha un numero limitato di metodi •  Le classi sono semplici e forniscono astrazioni e operazioni specifiche •  Solo un numero limitato di classi ha mol2 metodi –  Ha una correlazione significa2va con la propensione agli errori •  Depth of Inheritance (DIT) –  Le classi sono solitamente “vicine” alla radice •  DIT massimo vicino a 10 •  La maggior parte delle classi ha DIT uguale a 0 (sono la radice) –  I proge-s2 tendono a tenere il numero di livelli d’astrazione basso, quindi sacrificano la riusabilità a favore della comprensibilità •  Number of Children (NOC) –  Le classi di solito hanno un numero limitato di figli, e in mol2 casi NOC vale 0 –  L’ereditarietà non è spesso sfru&ata a pieno Gli studi dicono che (II) •  Coupling Between Classes (CBC) –  La maggior parte delle classi sono auto-­‐contenute (CBC = 0), ovvero non sono accoppiate con nessuno –  Gli ogge- di interfaccia tendo ad avere un CBC più grande •  Response for a Class (RFC) –  La maggior parte delle classi usa/invoca pochi metodi di altre classi –  Le classi degli ogge- di interfaccia hanno un RFC maggiore •  Lack of Cohesion in Methods (LCOM) –  Non molto u2le per predire la propensione alla dife&osità Principi di proge&azione Sintomi di un proge&o “mal fa&o” •  Rigidità è la tendenza del sokware ad essere difficile da cambiare, anche in modo semplice –  Ogni cambiamento provoca una cascata di cambiamen2 in sequenza nei moduli dipenden2 •  Fragilità è la tendenza del sokware a “rompersi” in mol2 pun2 ogni volta che viene cambiato –  Spesso il problema si verifica in par2 del programma non logicamente correlate al cambiamento •  Immobilità è l’incapacità a riusare sokware da altri proge- o da altre par2 dello stesso proge&o •  Viscosità si verifica quando l’uso di scorciatoie (hack) è più facile dell’uso dei metodi che rispe&ano il proge&o –  È facile fare la cosa sbagliata, difficile fare quella giusta –  La viscosità dell’ambiente è data da un ambiente di sviluppo lento e inefficiente Ges2one delle dipendenze •  I qua&ro sintomi elenca2 nel lucido precedente sono causa2, dire&amente o indire&amente, da dipendenze improprie tra i moduli del sokware –  Quando si ha un degrado nella stru&ura (archite&ura) del sokware, si ha anche un degrado della sua manutenibilità •  Le dipendenze tra moduli vanno ges2te a&raverso apposi2 firewall –  Le dipendenze non devono propagarsi oltre i firewall Alcuni principi di proge&azione Principio Open-­‐Closed •  Un modulo dovrebbe essere aperto alle estensioni, ma chiuso alle modifiche •  Derivato dal lavoro di Bertrand Meyer, in sostanza, dice –  Dovremmo sempre scrivere le nostre classi in modo che siano estendibili, senza che debbano essere modificate •  Vogliamo essere capaci di cambiare il comportamento delle classi senza cambiarne il codice sorgente •  Il vantaggio in manutenzione è ovvio: se non modifico, non faccio errori •  Principi base: ereditarietà, overriding, polimorfismo e binding dinamico Esempio ben noto typedef struct …Figura; Figura figure[100]; Triangolo Trapezio Re&angolo Cerchio figure[1] = “re&angolo”; figure[2] = “triangolo”; figure[3] = “cerchio”; void disegnaTu&o(Figura figure[]) { figure[4] = “trapezio”; for (i= 0; i<100; i++) { if (figure[i] è “re&angolo”) “disegna re&angolo” if (figure[i] è “triangolo”) “disegna triangolo” if (figure[i] è “cerchio”) “disegna cerchio” if (figure[i] è “trapezio”) “disegna trapezio” } } Il codice già definito per disegnaTu&o deve cambiare per Figura potere supportare il nuovo 2po!!! Open/closed? •  Quindi la proge&azione tradizionale non verifica open-­‐closed –  Il modulo disegnaTu&o è aperto a estensioni, ma solo tramite modifica: non è chiuso rispe&o alle modifiche •  Come si può fare a rispe&are open/closed? Aggiungiamo la classe Trapezio Figura Cerchio Re&angolo Triangolo Trapezio Figura[] figure = new Figura[100]; figure[1] = new Re&angolo(); figure[2] = new Triangolo(); figure[3] = new Cerchio(); figure[4] = new Trapezio(); disegnaTu&o(figure); public sta2c void disegnaTu&o(Figura[] figure) { for (i= 0; i<100;i++) figure[i].disegna(); } Il Codice definito per Figura non cambia!!! Estendibile se…. •  Estendibilità tramite ereditarietà è garan2ta solo se possiamo usare ogge- di una classe che sono “sos2tuibili” a quelli della sopraclasse •  Infa- gli ogge- della classe devono rispe&are il contra&o della superclasse –  Ad esempio, la disegna() deve contenere codice che disegna l’ogge&o this sullo schermo –  Ogni so&oclasse di Figura deve definire una disegna() che abbia lo stesso effe&o! –  Ma se si definisse disegna() in modo diverso? •  Ad esempio, se disegna() di Trapezi fosse definita in modo che disegna this solo se l’area è maggiore di 20? Va ancora bene? 33
Liskov Subs2tu2on Principle •  Gli ogge- della so&oclasse devono rispe&are il contra&o della superclasse –  Significa che il comportamento della so&oclasse deve essere “compa2bile” con la specifica della sopraclasse –  Ma il metodo può essere esteso per coprire ulteriori casi •  Moduli che usano ogge- di un 2po devono potere usare ogge- di un 2po derivato senza accorgersi della differenza –  Il contra&o della superclasse deve essere onorato da tu&e le so&oclassi Esempi •  Quadrato e Re&angolo •  Ortaggio e OrtaggioStagionale •  Persona e Studente Dependency Inversion Principle •  Dependere dalle astrazioni, non dagli elemen2 concre2 –  Dipendere da interfacce e classi astra&e, non da metodi e classi concrete –  Principio fondante del conce&o di proge&azione per componen2 (component design) •  CORBA, EJB, etc. … in altre parole •  Ogni classe C che si ri2ene possa essere estesa in futuro, dovrebbe essere definite come so&o2po di un'interfaccia o di una classe astra&a A •  Tu&e le volte che non è stre&amente indispensabile riferirsi ad ogge- della classe concreta C, è meglio riferirsi invece a ogge- il cui 2po sta2co è A, non C •  In questo modo, sarà più facile in seguito modificare il codice client per u2lizzare invece di C altre classi concrete so&o2pi di A Minimizzazione dell’interfaccia •  Se non esiste una ragione forte per dire che un metodo è pubblico, lo dichiariamo privato •  Definiamo solo metodo ge&er (e non se&er) per i campi della classe se possibile •  Non dobbiamo replicare completamente la stru&ura da2 contenuta in una classe nella sua interfaccia Ereditarietà e delega List
List
+ add()
+ remove()
+ add()
+ remove()
Stack
Stack
+ push()
+ pop()
+ top()
+ push()
+ pop()
+ top()
Ulteriori principi •  The Interface Segrega2on Principle: Many client specific interfaces are be&er than one general purpose interface •  The Reuse/Release Equivalency Principle: The granule of reuse is the same as the granule of release. Only components that are released through a tracking system can be effec2vely reused •  The Acyclic Dependencies Principle: The dependency structure for released components must be a directed acyclic graph. There can be no cycles •  The Stable Dependencies Principle: Dependencies between released categories must run in the direc2on of stability. The dependee must be more stable than the depender •  The Stable Abstrac2ons Principle: The more stable a class category is, the more it must consist of abstract classes. A completely stable category should consist of nothing but abstract classes Metodi con pochi parametri •  Oltre i tre o qua&ro si rischia che i programmatori non se li ricordino –  Nonostante l’assistenza fornita dagli IDE, i metodi con troppi parametri rimangono incomprensibili •  Par2colarmente pericolose sono le liste di parametri dello stesso 2po –  Un’inversione non genera errori di compilazione ma provoca malfunzionamen2 difficilmente diagnos2cabili Metodi con pochi parametri •  Un metodo che richiede mol2 parametri può generalmente essere spezzato in metodi che richiedono ciascuno meno parametri •  Possiamo creare classi ausiliarie che contengano gli aggrega2 dei parametri –  Questa tecnica è raccomandabile quando si hanno sequenze di parametri ricorren2 che rappresentano sempre la stessa en2tà An2-­‐pa&ern •  Un an2-­‐pa&ern è una soluzione usata spesso, ma che dovrebbe essere evitata –  È un errore comune –  Dovrebbe fornire suggerimen2 su come migliorare il codice o evitare errori no2 •  Gli an2-­‐pa&ern non si applicano solo alla proge&azione a ogge- Esempio •  Classe Blob –  Una sola classe enorme che con2ene la maggior parte della logica applica2va –  Deriva spesso dall’adozione di una proge&azione procedurale Refactoring •  È abbastanza difficile fare la cosa giusta al primo colpo –  Spesso le soluzioni trovate richiedono refactoring –  Ovvero il miglioramento del proge&o del codice esistente senza cambiarne il comportamento •  Miglioramen2 al codice/archite&ura –  Piccoli miglioramen2 –  Test di regressione con2nuo •  Framework Junit Refactoring •  Il tempo e la manutenzione potrebbero rendere disordinato il codice •  Codice disordinato riduce le possibilità di manutenzione •  Il refactoring rinfresca/pulisce il codice senza cambiarne le funzionalità –  Migliora la coesione e riduce l’accoppiamento –  Applica i pa&ern –  Rimuove gli an2-­‐pa&ern –  O-mizza le prestazioni (velocità e memoria) –  Aggiunge commen2 (magari a&raverso JavaDocs) Quando fare refactoring •  Non esiste regola precisa •  Quando si intravedono problemi –  Par2 di codice par2colarmente difficili –  Par2 troppo complesse –  Par2 con an2-­‐pa&ern eviden2 •  Meglio fare refactoring prima di aggiungere nuove funzionalità o cambiare le esisten2 Problemi no2 (I) •  Codice duplicato –  Estrarlo, parametrizzarlo e farlo diventare un metodo di servizio –  Codice simile in so&oclassi correlate •  Spostare il codice comune come metodo della superclasse •  Metodi lunghi –  Se il codice di un metodo diventa troppo lungo per poterlo capire facilmente, bisogna estrarne delle par2 come metodi di servizio Problemi no2 (II) •  Funzionalità e da2 –  Se un metodo usa principalmente i da2 di un’altra classe, bisogna spostare il metodo nella classe in cui sono defini2 i da2 •  Uso di Switch –  Spesso indicano un 2po –  L’introduzione di nuovi 2pi richiede la modifica degli switch rela2vi –  Bisognerebbe usare ereditarità, enum, pa&ern (State) Problemi no2 (III) •  Blocchi di da2 –  Da2 che sono solitamente usa2 insieme –  Potrebbero portare a una lunga lista di parametri (altro problema) –  Bisognerebbe introdurre una classe per contenere i da2 correla2 •  Commen2 –  Par2 di codice con commen2 significa2vi potrebbero diventare metodi con un nome auto-­‐
esplica2vo Programmazione Codice riusabile, manutenibile ed estendibile Metodi di piccole dimensioni •  Metodi di dimensioni limitate possono essere le- e compresi in modo abbastanza agevole –  Metodi brevi e chiari richiedono pochi commen2 o possono non richiederne affa&o –  Di solito si riesce a mantenere la maggior parte dei metodi al di so&o delle 20 righe di codice –  Raramente è necessario scrivere metodi più lunghi di 40 righe Pa&ern s2lis2ci •  Sono pra2che riguardan2 la scri&ura del codice, non la proge&azione •  Spesso, sono supporta2 dall’IDE •  Spesso, sono verificabili •  È bene prendere le buone abitudini subito •  È giusto concordare alcune pra2che nel gruppo di lavoro Commen2 •  Il codice è troppo di basso livello •  I commen2 possono essere usa2 per chiarire il significato del codice o per renderlo ancora più oscuro! –  I commen2 che ripetono il codice sono inu2li –  I commen2 che contraddicono il codice indicano che probabilmente sia codice che commen2 sono scorre- –  I commen2 possono indicare il significato del codice in modo parzialmente indipendente da cambiamen2 –  I commen2 devono indicare cosa il codice si propone di fare Esempi •  Commento inu2le: i++; // Increment i •  Commento u2le: i++; // Increment the card counter. •  Qui non c’è bisogno di commento: cardCounter++; •  Una buona scelta dei nomi delle variabili, dei metodi, ecc. rende il codice largamente “autocommentato” Dove/cosa commentare •  Cosa commentare dipende da molteplici fa&ori, in par2colare dall’espressività del linguaggio –  In Java generalmente non è il caso di avere un commento per ogni riga di codice •  Nei linguaggi OO è generalmente una buona idea fornire un commento all’inizio di ogni metodo, che de&agli il significato del metodo, eventuali vincoli, requisi2 ecc. –  Può essere u2le riportare anche riferimen2 a documentazione esterna –  Al solito, dare un nome significa2vo ai metodi può semplificare il commento Quando commentare •  Occorre commentare quando è necessario dire qualcosa con maggiore chiarezza di quanto non possa fare il codice •  Se si può rendere il codice abbastanza chiaro da poterlo considerare autocommentato, questo è ciò che va fa&o! –  Valutare sempre la conoscenza di chi leggerà il codice –  Se l’informazione per comprendere il codice si trova “vicino” allora il codice può essere considerato comprensibile Nomi e maiuscole •  Si può fare in modo che il nome di un elemento indichi la natura dell’elemento •  Questo effe&o si o-ene mediante delle “convenzioni di programmazione”, solitamente stabilite a livello aziendale •  In modo più ar2gianale, si possono usare le maiuscole in modo convenzionale per comunicare informazioni Maiuscole e Java •  Nomi di classi e interfacce hanno l’iniziale maiuscola: Stack •  Nomi di variabili e metodi iniziano sempre con una minuscola: push(...). •  I nomi delle costanto sono completamente maiuscoli: java.lang.Math.PI; •  I nomi di package sono scri- in minuscolo: java.lang •  Se il nome è composto da più parole, ciascuna di queste è scri&a con l’iniziale maiuscola: mustCopyToRunstack(...) –  L’uso di “underscore” (must_copy_to_runstack) non è consigliato Uso consistente dei nomi •  I nomi delle classi dovrebbero essere singolari: Stack •  I metodi void dovrebbero avere per nome dei predica2 verbali che descrivono cosa fanno: openFiles() •  I metodi e le variabili boolean dovrebbero avere nomi che iniziano con una declinazione di “essere”: isFinished() •  Gli altri metodi non void dovrebbero avere nomi che suggeriscono cosa res2tuiscono: sizeOfFigure() •  Variabili non boolean dovrebbero avere nomi cos2tui2 da sostan2vi (eventualmente con agge-vo): age Codice comprensibile •  Codice semplice, ma cosa fa? public List<int[]> getThem() {
List<int[]> list1 = new ArrayList<int[]>();
for (int[] x : theList)
if (x[0] == 4)
list1.add(x);
return list1;
}
Meglio? public List<int[]> getFlaggedCells() {
List<int[]> flaggedCells = new ArrayList<int[]>();
for (int[] cell : gameBoard)
if (cell[STATUS_VALUE] == FLAGGED)
flaggedCells.add(cell);
return flaggedCells;
}
Così? public List<Cell> getFlaggedCells() {
List<Cell> flaggedCells = new ArrayList<Cell>();
for (Cell cell : gameBoard)
if (cell.isFlagged())
flaggedCells.add(cell);
return flaggedCells;
}
Abbiamo •  Usato nomi significa2vi per spiegare le intenzioni –  flaggedCells invece di list1 •  Sos2tuito numeri magici con costan2 –  cell[STATUS_VALUE] invece di x[0] •  Creato un ADT opportuno –  Cell cell invece di int[] cell Ogni metodo una cosa sola public bool isEdible() {
if (this.ExpirationDate > Date.Now &&
this.ApprovedForConsumption == true &&
this.InspectorId != null) {
return true;
} else {
return false;
}
}
1. 
2. 
3. 
4. 
Controlla la scadenza Controlla l’approvazione Controlla l’ispezione Risponde alla richiesta Meglio così public bool isEdible() {
return isFresh() && isApproved() && isInspected();
}
•  Ora il metodo fa una cosa sola •  Un cambiamento nella specifica richiederebbe un solo cambiamento nel codice Non mischiare livelli d’astrazione public void doTheDomesticThings() {
takeOutTheTrash();
walkTheDog();
for (Dish dish : dirtyDishStack) {
sink.washDish(dish);
teaTowel.dryDish(dish);
}
}
public void doTheDomesticThings() {
takeOutTheTrash();
walkTheDog();
doTheDishes();
}
Usare le eccezioni •  Gli errori devono essere codifica2 •  I controlli richiedono codice •  Codice difficile da estendere public int foo(){
...
}
public void bar(){
if(foo() == OK)
...
else
// error handling
}
Con le eccezioni •  Non si mischiano valori res2tui2 e di controllo •  Sintassi più chiara •  Codice facile da estendere public void foo() throws FooException{
...
}
public void bar(){
try{
foo();
...
} catch(FooException){
// error handling
}
}
Alcuni errori comuni •  Crescita del codice –  Metodi lunghi e classi grandi •  Violazione del principio della responsabilità singola –  Ossessione per i 2pi primi2vi e troppi parametri •  Sinonimo di ca-va proge&azione •  I principi dell’OO non sono usa2 al meglio –  Statement switch su ogge- •  Meglio usare il polimorfismo –  Rifiuto dell’ereditarietà –  Classi alterna2ve con interfacce diverse •  Sinonimo di ca-va proge&azione della gerarchia delle classi Tipi primi2vi public Class Car{
private int red, green, blue;
public void paint(int red, int green, int blue){
this.red
= red;
this.green = green;
this.blue = blue;
}
}
public Class Car{
private Color color;
public void paint(Color color){
this.color = color;
}
}
Switch e polimorfismo public Money calculatePay(Employee e) throws InvalidEmployeeType{
switch(e.type){
case COMMISSIONED:
return calculateCommissionedPay(e);
case HOURLY:
return calculateHourlyPay(e);
case SALARIED:
return calculateSalariedPay(e);
default:
throw new InvalidEmployeeType(e.type);
}
}
public abstract class Employee {
public abstract Money calculatePay();
}
Ca-vo uso dell’ereditarietà •  Le so&oclassi non usano i metodi e gli a&ribu2 della superclasse •  Quota non riguarda Engineer –  Dovrebbe essere solo parte di Salesman public abstract class Employee{
private int quota;
public int getQuota();
...
}
public class Salesman extends Employee{ ... }
public class Engineer extends Employee{
...
public int getQuota(){
throw new NotSupportedException();
}
}
Alcuni errori comuni •  Elemen2 che complicano la manutenzione del codice –  Codice divergente •  Una classe deve essere cambiata in diversi pun2 –  Intervento urgente •  Un singolo cambiamento richiede modifiche in diverse classi •  Il codice con2ene elemen2 non necessari –  Una classe non fa a sufficienza •  Una classe non fornisce logica –  Codice non u2lizzato o ridondante •  Non è nulla di u2le Alcuni errori comuni •  Alcune classi sono troppo accoppiate –  Abuso di proprietà altrui •  Responsabilità mal distribuite –  In2mità non appropriata •  Una classe dovrebbe conoscere il meno possibile delle altre classi –  Catene di invocazioni •  Accesso ai da2 troppo complesso Abuso di cara&eris2che altrui public class Customer{
private PhoneNumber mobilePhone;
...
public String getMobilePhoneNumber(){
return “(” +
mobilePhone.getAreaCode() + “)” +
mobilePhone.getPrefix() + “-” +
mobilePhone.getNumber();
}
}
public String getMobilePhoneNumber(){
return mobilePhone.toFormattedString();
}
Catene di invocazioni •  a.getB().getC().getD().getTheNeededData() •  a.getTheNeededData() •  Legge di Demeter –  Parla solo con i tuoi amici più stre-