In questo articolo parleremo di:
- Build Systems
- Stile e formattazione del codice
- Testing
- MVC - Model-View-Controller
- VCS - Version Control Systems
- AGILE SOFTWARE DEVELOPMENT
Build Systems
La costruzione del software include:
- Manipolazione e pre-processing del sorgente
- Verifica della qualità del codice
- Gestione delle dipendenze
- Compilazione
- Testing e misurazione della copertura
- Generazione della documentazione
Build automation
-
Imperativo/Personalizzato:
- Realizzato con script di programmazione
- Flessibile ma difficile da adattare/riusare
-
Dichiarativo/Standardizzato:
- Usa file di configurazione dedicati
- Portabile e comprensibile, ma limitato dalle opzioni disponibili
Convention over Configuration
- Standard di fatto
- Riduce le ripetizioni
- Aumenta la portabilità
Automatori ibridi
Sono sistemi che cercano di combinare il meglio dei sistemi dichiarativi e imperativi.
Esempi:
- Sbt, che si appoggia su Scala
- Gradle, che si appoggia su Groovy
Domain-Specific languages
Essere flessibili da permettere dicostruire un linguaggio dentro al linguaggio.
Gradle
- Usa Kotlin o Groovy
- Build system Ibrido
- Supporta Java, C/C++, Scala, Kotlin
- Usato nell'ecosistema Android
Concetti fondamentali:
- Progetto, contiene build.gradle.kts (logica di costruzione) e/o settings.gradle.kts (nome al progetto), detti build file.
- Task, Operazione atomica del processo di build
- Plugin, task pronti all'uso
Dipendeza del software:
- Le applicazioni hanno un albero di dipendenze!
Dipendendeze transitive:
- Sono le dipendenze indirette (dipendenze delle dipendenze).
Maven
Java non definisce alcuno standard per i nomi di libreria, per sopperire a questo problema è nato Maven.
che è poi divento lo standard de facto per la gestione delle dipendenze in Java nei build system.
- Maven Central Repository (Sonatype OSSRH)
Maven naming convention: È il repository di default di Maven, contiene milioni di librerie.
- groupId: identifica progetto/gruppo (es: com.google)
- artifactId: identifica libreria specifica
- version: versione della libreria
Gestione delle dipendenze in gradle
Grande consente di gestirle, potremmo avere anche delle librerie da utilizzare solo per i test, che poi non useremo in produzione.
plugins { java } // Carica il necessario per Java
repositories { mavenCentral() } // Configura Gradle per cercare e scaricare da Maven Central
Trovare su Maven Central la libreria che intendiamo usare e annotare:
- groupId
- artifactId
- version
Gradle consente di (costringe a) dire chiaramente “a cosa serve” una certa libreria:
- implementation: sia per compilare che per eseguire il codice
- testImplementation: compilare e eseguire i test
- testRuntimeOnly: solo eseguzione test
Lanciare app java da Gradle
plugins {
java // Carica il necessario per la compilazione di Java
application // Carica il necessario per l'esecuzione di classi con la JVM
}
repositories { mavenCentral() } // Configura Gradle per cercare e scaricare da Maven Central
dependencies { // Elenco di librerie che ci servono
implementation("com.omertron:API-OMDB:1.5") //libreria
}
application { // Configurazione dell'esecuzione, la main class è sufficiente
mainClass.set("it.unibo.sampleapp.SimplerRateAMovie")
}
GRADLE WRAPPER
Caratteristiche:
- Garantisce consistenza della versione di Gradle
- Permette coesistenza di diverse versioni
- Automatizza download e installazione
Componenti:
- gradlew (script Unix)
- gradlew.bat (script Windows)
- gradle/wrapper/gradle-wrapper.properties
- gradle/wrapper/gradle-wrapper.jar
Vantaggi:
- Non richiede installazione manuale di Gradle
- Garantisce compatibilità tra sviluppatori
- Semplifica la gestione di progetti multipli
Comandi principali:
- ./gradlew build - compila il progetto
- ./gradlew test - esegue i test
- ./gradlew clean - pulisce la build
- ./gradlew tasks - mostra i task disponibili
Stile e formattazione del codice
Importanza di uno stile uniforme, consistente, Standard.
- Indentazione 4 caratteri
- Lunghezza linee non più di 90 caratteri
Commenti:
- // ... su una linea
- /_ ... _/ su più linee per commentare sezioni
- /*_ ... _/ su più linee per commenti che generano documentazione
Utili:
- Definizione di variabile: una per linea, meglio se a inizio del metodo
- Meglio inizializzare sempre le variabili!
- Una sola istruzione per riga
- Blocchi e parentesi graffe, anche per blocchi ad uno statement
Nomi:
- Classi (e interface): PascalCase
- Metodi, campi, variabili: camelCase
- Package: lowercase
- Campi costanti: UPPERCASE
Ordine degli elementi della classe:
- Campi statici (pubblici, poi privati – ovvero, dal più aperto al più chiuso)
- Campi istanza (pubblici, poi privati)
- Costruttori (pubblici, poi privati)
- Metodi (raggruppati per ruolo, NON per Ûsibilità, NON alfabeticamente)
Metodi getter/setter:
- Un metodo getter è un metodo che senza input restituisce un valore, una proprietà dell’oggetto
- Un metodo setter è un metodo che restituisce void e accetta un valore che modifica una proprietà dell’oggetto
Convenzioni nel nome:
- Getter non booleano: T getXYZ(){...}
- Getter booleano: boolean getXYZ(){...} o anche boolean isXYZ() {...}
- Setter: void setXYZ() {...}
Esempio lamp
public class Lamp {
...
// Setter per proprieta' Intensity di tipo double
public void setIntensity(double value) {
...
} /
/ Getter per proprieta' Intensity di tipo double
public double getIntensity() {
...
} /
/ Getter per proprieta' SwitchedOn di tipo boolean
public boolean isSwitchedOn() {
...
}
}
lampUtilities
public class LampUtilities {
/* Costruisco una fila di lampadine */
public static Lamp[] buildLampRow(final int size) {
final Lamp[] array = new Lamp[size];
for (int index = 0; index < size; index++) {
array[index] = new Lamp();
}
return array;
} /
* Accendo/spengo una fila di lampadine */
public static void switchAll(final boolean switchedOn, final Lamp[] array) {
for (Lamp l : array) {
if (switchedOn) {
l.switchOn();
} else {
l.switchOff();
}
}
}
}
Checkstyle
- Si occupa di trovare errori di stile e formattazione del codice.
- Si dichiara lo stile desiderato in un file checkstyle.xml e poi si esegue il comando.
Si integra anche con Gradle e poi di defualt fallisce la build se non si rispettano le regole.
Testing
Il testing del software è quell’attività di ricerca di anomalie al fine di localizzare e rimuovere i difetti nel software
Definizioni:
- Errore: l’azione che ha causato una falla
- Falla (bug): causa di un’anomalia
- Fallimento (anomalia): differenza tra risultato atteso e attuale
- Testing: attività di ricerca anomalie per localizzare/rimuovere difetti
Errore -> Difetto/Bug -> Fallimento
- Verifica: controllo correttezza rispetto a specifiche
- Validazione: controllo adeguatezza rispetto aspettative stakeholder
JUNIT 5: FRAMEWORK DI TESTING
UUT: unit under test
Architettura JUnit 5:
- JUnit Platform: piattaforma per esecuzione test
- JUnit Jupiter: API per scrivere test
- JUnit Vintage: supporto per test JUnit 4
Pattern AAA:
- Arrange: setup del contesto del test
- Act: esecuzione della funzionalità da testare
- Assert: verifica del risultato
Esempio di asserizone:
per confrontare il risultato atteso con quello effettivo:
assertEquals(expected, actual)
assertTrue(condition)
assertFalse(condition)
assertNull(object)
assertNotNull(object)
assertArrayEquals(expectedArray, actualArray)
Annotazioni principali:
Le annotazione sono informazioni aggiuntive ai costrutti del programma
- @Test: marca un metodo come test ( può espiremere uno più test case)
- @BeforeEach: eseguito prima di ogni test
- @AfterEach: eseguito dopo ogni test
- @BeforeAll: eseguito una volta prima di tutti i test
- @AfterAll: eseguito una volta dopo tutti i test
Unit testing: la pratica di testare una unità funzionale in isolamento (funzioni, metodi, classi).
- Esempio: testare una funzione che calcola lo sconto su un prezzo
Integration testing: verificare la correttezza dell’integrazione di diverse unità
- Esempio: testare l'integrazione tra il modulo ordini e il modulo pagamenti
TDD
- RED: si scrive un test per catturare la funzionalità che si vuole realizzare (visto che è ancora da implementare, questa fallirà)
- GREEN: si implementa la funzionalità fino a che il test passa
- REFACTOR: eventualmente, si migliora l’implementazione rieseguendo i test, sempre sicuri che questi interventi non causano regressioni
Terminata un’iterazione RED-GREEN-REFACTOR, si può procedere a un’altra iterazione, ovvero alla realizzazione di un incremento di funzionalità
Principi fondamentali:
- Test isolati e indipendenti
- Un assert per test (quando possibile)
- Test deterministici
- No logica nei test
MVC - Model-View-Controller
Modello architetturale che divide un'applicazione in tre componenti principali per separare le responsabilità.
Per esempio, in un'app di note:
- Model: gestisce il salvataggio e modifica delle note
- View: mostra la lista delle note e il form per crearne di nuove
- Controller: gestisce le azioni come "crea nota" o "elimina nota"
Promuovere il riuso, la modularità, l’estensibilità, la manutenibilità del software
Model: metodi di dominio chiamati da Controller
incapsula dati e logica relativi al dominio della applicazione
- Modello del dominio applicativo
- Incapsula dati e logica di business
- Indipendente da visualizzazione e controllo
View: metodi void chiamati da View (azioni utente)
incapsula la GUI, le sue sottoparti, e la logica di notifica
- Gestisce interazioni con l'utente
- Si occupa di input e output
- Presenta i dati del Model
- Notifica il Controller delle azioni utente
Controller: metodi void chiamati da Controller (richieste visualizzazione)
intercetta gli eventi della View, comanda le modi×che al modello, cambia di conseguenza la View
- Coordina Model e View
- Intercetta eventi dalla View
- Comanda modifiche al Model
- Aggiorna la View in base ai cambiamenti
ESEMPIO PRATICO (DrawNumber):
// Model Interface
public interface DrawNumber {
void reset();
DrawResult attempt(int n);
}
// View Interface
public interface DrawNumberView {
void setObserver(DrawNumberViewObserver observer);
void start();
void numberIncorrect();
void result(DrawResult res);
}
// Controller (observer)
public interface DrawNumberViewObserver {
void newAttempt(int n);
void resetGame();
void quit();
}
VANTAGGI DEL PATTERN
- Separazione delle responsabilità
- Facilità di testing (componenti testabili separatamente)
- Possibilità di modificare un componente senza impattare gli altri
- Supporto al lavoro in team
- Codice più organizzato e mantenibile
VCS - Version Control Systems
Evoluzione del controllo versione:
- Centralizzato
- Distribuito
Centralizzato:
- Concurrent Versioning System (CVS) - 1986
- Apache Subversion (SVN)
Pain:
- Single Point of Failure
- Connettività, necessaria la connessione per quasi tutte le operazioni
- Difficile collaborazione
- Performance
Distribuito DVCS(Distributed Version Control System):
- Git - 2005
- Mercurial
Scopo:
- Gestione delle modifiche
- Collaborazione
- Tracciabilità
Tracking delle modifiche:
- Evitare perdita di progressi
- Documentare lo sviluppo
- Possibilità di rollback
Collaborazione:
- Lavoro concorrente
- Integrazione modifiche
- Risoluzione conflitti
Git
Repository: Contiene intero progetto/storia e metadata
Commit: Snapshot dello stato del progetto
Best Praticies:
- Commit piccoli e frequenti
- Un branch per feature
- Main branch sempre stabile
- Pull prima del lavoro
Configurazione Inizale:
# Setup identità
git config --global user.name "Nome"
git config --global user.email "email"
git config --global init.defaultbranch main
Operazioni Base Repository
# Inizializzazione e setup
git init
git remote add origin URL
git clone URL
Gestione Modifiche
# Stage e commit
git add . # Stage tutte le modifiche
git add <file> # Stage file specifico
git commit -m "messaggio"
git reset HEAD <file>
Sincronizzazione
# Comandi remote
git fetch # Scarica modifiche
git pull # Scarica e merge
git push # Carica modifiche
Branch Management
# Gestione branch
git branch nome # Crea branch
git checkout nome # Switch branch
git checkout -b new # Crea e switch
git merge branch # Unisce branch
git rebase main # Rebase su main
Monitoraggio
# Stato e storia
git status # Stato corrente
git log # Storia commit
git diff # Mostra modifiche
GitHub
- Servizio di hosting per repository Git
Gitignore: File .gitignore per escludere files
AGILE SOFTWARE DEVELOPMENT
- Insieme principi/pratiche per rispondere al cambiamento
- Approcci iterativi/incrementali
- Cicli brevi per feedback frequente
- Focus sulla qualità
Manifesto Agile
Valorizzare:
- Individui e interazioni più che processi e strumenti
- Software funzionante più che documentazione esaustiva
- Collaborazione col cliente più che negoziazione contratti
- Rispondere al cambiamento più che seguire un piano"