SlideShare uma empresa Scribd logo
1 de 32
Dependency Injection:
The Good Parts
@massimogroppel1
groppedev
MASSIMO GROPPELLI
Senior Software Developer at
Zucchetti
TDD Milano (Test Driven Development)
Mikamai/LinkMe - Milano 16/04/2018
Di cosa parleremo?
1. Cos’è la Dependency Injection e quali sono i suoi vantaggi
2. Caso di studio “Correttore Ortografico” (Tutti gli esempi mostrati riguarderanno questo
caso di studio)
3. Inversion of Control e Dependency Injector
4. Injection points / Forme di Dependency Injection
5. Scope dei componenti e problematiche associate
6. Test Unitari
7. Test di Integrazione
8. Profili
Dependency Injection
La Dependency Injection (DI) è una naturale tecnica di composizione degli oggetti in
Object Oriented Programming (OOP).
La DI è un design pattern talmente semplice che molti sviluppatori lo utilizzano per anni
senza sapere come si chiama.
Ogni oggetto (dipendente) può avere la necessità di collaborare con altri oggetti
(dipendenze/collaboratori) per svolgere il suo unico compito (SRP Single Responsibility
Principle).
Nell’ambito della DI è molto importante considerare gli oggetti come dei servizi. In questo
modo si evidenzia la relazione di dipendenza (transitiva) tra un oggetto ed i servizi che
utilizza per svolgere la sua funzione (A sua volta un altro servizio).
Il sistema composto da tutte le dipendenze è comunemente chiamato Object Graph e
funzionalmente svolge il suo compito come se fosse un singolo componente.
ORIGINI D.I. (2004): https://martinfowler.com/articles/injection.html
Vantaggi della Dependency Injection
Incoraggia l’utilizzo della composizione di oggetti e scoraggia l’uso dell’ereditarietà.
Porta ad un basso accoppiamento tra i componenti, aumentando chiaramente la
flessibilità e favorendo il riuso del software.
Aumenta la testabilità dei componenti. I collaboratori che rappresentano delle dipendenze,
vengono sostituiti dai test doubles.
Favorisce l’applicazione di alcuni dei principi SOLIDi soprattutto se utilizzata in
combinazione con IOP (Interface-Oriented Programming).
● OCP (Open/Close Principle)
● DIP (Dependency Inversion Principle)
OCP Un'entità software dovrebbe essere aperta alle estensioni, ma chiusa alle modifiche
DIP I moduli di alto livello non devono dipendere da quelli di basso livello. Entrambi devono dipendere da astrazioni; Le
astrazioni non devono dipendere dai dettagli; sono i dettagli che dipendono dalle astrazioni. INFRASTRUTTURA usa DOMINIO e
non viceversa
Libro del 2009.
Ottimo riferimento sia per principianti che
per esperti di DI.
Mostra i design pattern e le best practices
per utilizzare al meglio la DI in ambito
professionale.
Non si concentra su uno specifico
framework per DI ma si focalizza sui
concetti importanti con il supporto di ottimi
esempi.
Gli esempi sono in Java e vengono utilizzati i
framework:
● Spring IoC
● Google Guice
● PicoContainer
Caso di studio “Correttore Ortografico”
Il nostro “correttore ortografico” è molto semplice, e fornisce due funzionalità:
● Data una parola ci dice se è VALIDA per il lessico impostato.
● Data una parola errata prova a darci dei SUGGERIMENTI per correggerla cercando
parole simili nel lessico impostato sfruttando una specifica strategia di correzione.
Lessici installati:
● SPORT ITALIANI: calcio, pallavolo, pallacanestro
● CIBI ITALIANI : pizza, lasagne, pasta
Strategie di correzione disponibili:
● INIZIA PER: Se si digita “p” ed è installato il dizionario CIBI ITALIANI le possibili
correzioni saranno “pizza” e “pasta” visto che entrambe iniziano per il typo “p”.
● FINISCE PER : Se si digita “o” ed è installato il dizionario SPORT ITALIANI le possibili
correzioni saranno “calcio” , “pallavolo” e “pallacanestro” visto che tutte terminano per
il typo.
Caso di studio “Correttore Ortografico”
Il caso di studio è sviluppato in Java (1.8) ed utilizza Spring IoC Container
(5.0.5.RELEASE) come Dependency Injector.
Il progetto utilizza Maven ed il plug-in di Eclipse Spring Tool Suite (STS 3.9.4.RELEASE)
I test utilizzano le librerie JUnit (4.12), AssertJ (3.9.1) e Mockito (1.10.19)
Nel caso di studio il container di Spring viene configurato solo con files XML.
URL Github: https://github.com/groppedev/di-thegoodparts-tdd-milano
Object Graph “Correttore Ortografico”
DI senza IoC
Dependency Injection manuale con l’aiuto del Factory Pattern
● Si deve scrivere molto codice che non fornisce alcun valore aggiunto (Factory)
● I componenti sono testabili ma con difficoltà. Si modificano le factory
appositamente per fare i test.
● Lo scope dei componenti è definito nel codice sorgente e non può essere
modificato dall’esterno. La flessibilità dell’applicazione è ridotta.
● I client devono chiedere le loro dipendenze mentre la DI con IoC enfatizza il
concetto che i client non devono avere conoscenza esplicita delle loro dipendenze.
DI senza IoC (Client)
public class SpellCheckerNoIoC
{
// Si utilizza il lessico dei cibi italiani.
private final Lexicon lexicon = LexiconFactory.getInstance().newItalianFoodLexicon();
public boolean check(Word wordToCheck)
{
return lexicon.hasWord(wordToCheck);
}
}
DI senza IoC (Factory)
public class LexiconFactory
{
// SCOPE Singleton nel sorgente e non determinato dal contesto.
private static final LexiconFactory INSTANCE;
static {
INSTANCE = new LexiconFactory();
INSTANCE.init();
}
private volatile LexiconRepository lexiconREPO = new LexiconFileRepository();
private volatile LexiconQueryExecutor lexiconQE = new LexiconQueryExecutor(lexiconREPO);
public static LexiconFactory getInstance() {
return INSTANCE;
}
private LexiconFactory() {}
// Factory method
public Lexicon newItalianFoodLexicon() {
// Composizione manuale del grafo, nessuna flessibilità
return new LexiconItalianFood(lexiconREPO, lexiconQE);
}
}
DI senza IoC (Test)
@RunWith(JUnit4.class)
public class SpellCheckerNoIoCTest {
@Before
public void setup() {
// Nei test si vuole utilizzare l'implementazione in memoria.
LexiconInMemoryRepository lexiconREPO = new LexiconInMemoryRepository();
LexiconFactory.getInstance().setLexiconREPO(lexiconREPO);
}
@After
public void teardown(){
// Al termine del test va impostata la vecchia implementazione.
LexiconRepository lexiconREPO = new LexiconFileRepository();
LexiconFactory.getInstance().setLexiconREPO(lexiconREPO);
}
@Test
public void test() {
// Run Test
SpellCheckerNoIoC spellChecker = new SpellCheckerNoIoC();
boolean isValid = spellChecker.check(Word.aWord("pizza"));
// Assertion
Assert.assertTrue(isValid);
}
}
IoC (Inversion of Control)
Hollywood Principle: Un oggetto dipendente, non deve
preoccuparsi di richiedere le sue dipendenze, gli verranno
fornite in automatico.
Dependency Injector: Framework o libreria che
implementa l’Hollywood Principle
Esempi di IoC:
● Un metodo di test invocato in automatico da un framework (vedi DI senza IoC (Test))
● Un event handler richiamato in seguito al click del mouse
● Una dipendenza impostata in un oggetto dipendente da un dependency injector
Dependency Injector
Un framework di Dependency Injection (Injector o Container) è uno strumento molto
potente che gestisce aspetti che vanno oltre la pura Dependency Injection:
● Gestione degli SCOPE dei componenti, garantendo a seconda dell’utilizzo la possibilità
di cambiare scope senza modificare il codice sorgente.
● Gestione dell’intero ciclo di vita degli oggetti, dalla creazione alla distruzione.
● Metodologie standardizzate per risolvere problemi comuni quando si utilizza la
Dependency Injection, su tutte la possibilità di impostare una dipendenza con scope più
limitato rispetto a quello dell’oggetto dipendente.
● Possibilità di gestire dei profili per rendere l’applicazione completamente flessibile
senza la necessità di modificare la codebase.
Nei successivi esempi verranno utilizzate le annotation JSR-330, anche se la
configurazione XML è da considerarsi migliore per applicazioni complesse.
Dependency Injector per quali oggetti?
Quando si inizia ad utilizzare un framework di DI si tende a gestire OGNI oggetto con
l’injector, ma è sbagliato!
Lo stesso sintomo si verifica quando si inizia ad utilizzare una libreria di mocking nei test,
si tende a sostituire ogni oggetto con un test doubles.
● I Value Object e le Entity non vanno gestite con l’injector così come non devono mai
essere sostituiti con dei mock nei test.
● Le classi di utilità che non hanno dipendenze, *Utils non devono essere gestite con
l’injector così come non devono mai essere sostituite con dei mock nei test.
Vanno create apposite classi statiche senza stato.
Dependency Injector per quali oggetti?
/**
* Value Object
*/
@Immutable
public final class Word {
private final String text;
private Word(String text) {
this.text = text;
}
public static Word aWord(String text) {
return new Word(text);
}
public boolean startsWith(Word word) {
return this.text.startsWith(word.text);
}
@Override
public boolean equals(Object obj) { ... }
}
Dependency Injector per quali oggetti?
/**
* Classe di utilità che non ha dipendenze esterne
*/
public class LexiconUtils {
public static Collection<Word> toWord(LexiconType type, Collection<LexiconWord> lexicondWords) {
Collection<LexiconWord> currentTypeWords = select(lexicondWords, new Predicate<LexiconWord>() {
public boolean evaluate(LexiconWord lexiconWord) {
return lexiconWord.matchesType(type);
}
});
return collect(currentTypeWords, new Transformer<LexiconWord, Word>() {
@Override
public Word transform(LexiconWord lexiconWord) {
return lexiconWord.toWord();
}
});
}
}
Dependency Injector - Best practices
Qualificare i componenti utilizzando i namespace
Soprattutto quando si devono gestire applicazioni complesse è buona prassi qualificare
sempre i componenti gestiti dall’injector con un identificativo univoco utilizzando i
namespace.
@Named // Non Qualificato
public class SuggestionService
@Named(value="suggestionService") // Senza Contesto
public class SuggestionService
@Named(value="spellcheck.suggestions.service") // OK
public class SuggestionService
Dependency Injector - Best practices
Non legare il codice di dominio al codice del framework di injection
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import groppedev.dithegoodparts.domain.Word;
import groppedev.dithegoodparts.domain.lexicon.LexiconItalianFood;
public class WordsRepository implements ApplicationContextAware
{
private ApplicationContext applicationContext;
public Collection<Word> words() {
return applicationContext.getBean(LexiconItalianFood.class).words();
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
}
Injection Point - Field Injection
// Annotations JSR-330.
@Named
public class FieldInjection implements InjectionPoint
{
@Inject private Provider<Example> provider;
public void execute() {
Objects.requireNonNull(provider);
System.out.println(provider.get());
}
public void executeBis() {
Objects.requireNonNull(provider);
System.out.println(provider.get());
}
}
Injection Point - Field Injection
● Forma di DI concisa.
● Si creano oggetti NON testabili.
● Si creano oggetti dipendenti dal framework di Dependency Injection.
● Va aggiunta la validazione in tutti i metodi.
● Tende a nascondere le dipendenze.
● Favorisce il crescere dei collaboratori senza accorgersi del problema fino a violare il
principio SRP
DA NON UTILIZZARE
La Field Injection è un ANTI-PATTERN
// Annotations JSR-330.
@Named
public class SetterInjection implements InjectionPoint
{
private Provider<Example> provider;
public void execute()
{
Objects.requireNonNull(provider);
System.out.println(provider.get());
}
public void executeBis() {
Objects.requireNonNull(provider);
System.out.println(provider.get());
}
// Setter non esposto sull'interfaccia
@Inject public void setProvider(Provider<Example> provider) {
this.provider = provider;
}
}
Injection Point - Setter/Method Injection
Injection Point - Setter/Method Injection
● Vanno bene per esplicitare dipendenze opzionali.
● Sono utili per gestire casistiche particolari e NON di dominio (ES:
ComponentProvider nel caso di studio)
● Ottima soluzione per risolvere le dipendenze circolari, che però normalmente sono
un segnale di cattivo design
● Funziona bene con l’ereditarietà, ma noi vorremmo lavorare con la
composizione.
Injection Point - Setter/Method Injection
● Si creano oggetti testabili ma fixture di test poco manutenibili.
● Va aggiunta la validazione in tutti i metodi.
● Tende a nascondere le dipendenze se utilizzato con dipendenze obbligatorie.
● Incoraggia a creare oggetti MUTABILI e potenzialmente INCOMPLETI.
● Favorisce il crescere dei collaboratori senza accorgersi del problema fino a violare il
principio SRP.
● Può causare più facilmente errori a RUNTIME
DA UTILIZZARE SOLO PER MODELLARE LE DIPENDENZE OPZIONALI
La Setter Injection si può utilizzare ma prestando molta attenzione!
I setter devono essere metodi delle implementazioni NON delle interfacce
Injection Point - Constructor Injection
// Annotations JSR-330.
@Immutable
@Named
public final class ConstructorInjection implements InjectionPoint {
private final Provider<Example> provider;
// Ogni componente dovrebbe avere uno solo costruttore!
@Inject
public ConstructorInjection(Provider<Example> provider){
// Validazione solo nel costruttore.
Objects.requireNonNull(provider);
this.provider = provider;
}
public void execute() {
System.out.println(provider.get());
}
public void executeBis() {
System.out.println(provider.get());
}
}
Injection Point - Constructor Injection
● Per le dipendenze opzionali utilizzare setter/method injection
● Va bene per esplicitare dipendenze obbligatorie.
● Si creano oggetti testabili e fixture di test manutenibili
● E’ richiesta la validazione solamente nell’unico costruttore (Più costruttori molte volte
indicano la violazione del principio SRP)
● Limita gli errori a RUNTIME
● Evidenzia in modo chiaro quando i collaboratori aumentano troppo e favorisce la
sistemazione del design prima di violare il principio SRP
● Incoraggia a creare oggetti IMMUTABILI e COMPLETI eliminando il problema della
concorrenza e favorendo notevolmente la diminuzione degli errori.
● Evidenzia le dipendenze al posto di nasconderle creando oggetti utilizzabili facilmente
anche senza il framework di dependency injection
Injection Point - Conclusioni
La FIELD INJECTION non va mai utilizzata!
Utilizzare la SETTER INJECTION solo per i collaboratori OPZIONALI e la CONSTRUCTOR
INJECTION per i collaboratori OBBLIGATORI.
I componenti devono avere un solo COSTRUTTORE e non nascondere mai le DIPENDENZE.
Anche i più grandi critici dei framework dei Dependency Injection, ammettono che la
CONSTRUCTOR INJECTION è un ottima tecnica per creare oggetti.
Vedi http://www.yegor256.com/2014/10/03/di-containers-are-evil.html
Scope
Lo scope di un oggetto indica la durata del ciclo di vita dell’oggetto stesso in un determinato
contesto che nel nostro ambito sarà una istanza del container di dependency injection.
Unscoped / Prototype : Viene creato un nuovo oggetto ad ogni richiesta.
Singleton: Viene creato un nuovo oggetto solo alla prima richiesta e poi viene sempre
restituita l’istanza creata la prima volta.
Lo scope è fornito dal contesto e non deve essere esplicitato nel codice sorgente!
Dovrebbe essere possibile cambiare scope senza modificare il codice
Esistono altri scope (Request, Session e Thread), ma ci concentreremo su Prototype e
Singleton che sono quelli di base ed i più importanti.
Scope - Qual è il migliore
Lo scope di default, incredibilmente è diverso a seconda del framework di dependency
injection utilizzato:
● Google Guice: Unscoped / Prototype
● Standard JSR-330: Unscoped / Prototype
● Spring IoC Container: Singleton
Se si parte dal presupposto che il container di dependency injection dovrebbe gestire
principalmente dei servizi per lo più immutabili, sicuramente l’approccio di Spring IoC
Container è il più corretto.
Scope - Problematiche
La principale problematica da risolvere quando si lavora con gli scope è la gestione di una
dipendenza di tipo Unscoped / Prototype in un oggetto dipendente di tipo Singleton.
In sostanza quando si ha una dipendenza di scope più limitato rispetto allo scope del
dipendente.
Quello sopra presentato, è un problema talmente comune, che anche lo standard JSR-330
ha proposto una soluzione:
javax.inject.Provider<T>
Questa interfaccia deve essere implementata da tutti i framework di dependency injection
che aderiscono allo standard JSR-330
Come fare i Test
TEST UNITARI: Rigorosamente senza l’injector, si istanziano gli oggetti con la new e si passano dei test
doubles al posto delle dipendenze.
@RunWith(JUnit4.class)
public class SpellCheckerTest
{
@Test
public void isValidTest()
{
// Fixture.
Lexicon lexicon = Mockito.mock(Lexicon.class);
SuggestionService suggestionService = Mockito.mock(SuggestionService.class);
// Stubbing
Mockito.when(lexicon.hasWord(Word.aWord("massimo"))).thenReturn(true);
// Run Test.
SpellChecker spellChecker = new SpellChecker(lexicon, suggestionService);
boolean isValid = spellChecker.check(Word.aWord("massimo"));
// Assertions.
Assert.assertTrue(isValid);
}
}
Come fare i Test
TEST DI INTEGRAZIONE: Test di accettazione sempre completi di tutto il ciclo di vita dell’injector, deve
essere caricata tutta la configurazione.
@RunWith(JUnit4.class)
public class ApplicationStaticTest {
private static ApplicationStaticAPI app;
@BeforeClass
public static void startApplication(){
System.setProperty(AbstractEnvironment.ACTIVE_PROFILES_PROPERTY_NAME, "TEST,STATIC_APP");
app = Application.startStaticApplication();
}
@AfterClass
public static void stopApplication(){
app.close();
System.setProperty(AbstractEnvironment.ACTIVE_PROFILES_PROPERTY_NAME, "");
}
@Test
public void validWordTest(){
// Run Test.
boolean validWord = app.checkWord(Word.aWord("pizza"));
// Assertion.
Assert.assertTrue(validWord);
}
}

Mais conteúdo relacionado

Mais procurados

Lezione 3: Sviluppo in Extreme Programming
Lezione 3: Sviluppo in Extreme ProgrammingLezione 3: Sviluppo in Extreme Programming
Lezione 3: Sviluppo in Extreme ProgrammingAndrea Della Corte
 
Android Test Driven Development
Android Test Driven DevelopmentAndroid Test Driven Development
Android Test Driven Developmentsazilla
 
Java Unit Testing - JUnit (1)
Java Unit Testing - JUnit (1)Java Unit Testing - JUnit (1)
Java Unit Testing - JUnit (1)fgianneschi
 
Java Unit Testing - JUnit (2)
Java Unit Testing - JUnit (2)Java Unit Testing - JUnit (2)
Java Unit Testing - JUnit (2)fgianneschi
 

Mais procurados (6)

Lezione 3: Sviluppo in Extreme Programming
Lezione 3: Sviluppo in Extreme ProgrammingLezione 3: Sviluppo in Extreme Programming
Lezione 3: Sviluppo in Extreme Programming
 
Unit Testing
Unit TestingUnit Testing
Unit Testing
 
Android Test Driven Development
Android Test Driven DevelopmentAndroid Test Driven Development
Android Test Driven Development
 
Java Unit Testing - JUnit (1)
Java Unit Testing - JUnit (1)Java Unit Testing - JUnit (1)
Java Unit Testing - JUnit (1)
 
Java Unit Testing - JUnit (2)
Java Unit Testing - JUnit (2)Java Unit Testing - JUnit (2)
Java Unit Testing - JUnit (2)
 
Unit Testing
Unit TestingUnit Testing
Unit Testing
 

Semelhante a Dependency injection: the good parts

Delphi & Dintorni Webinar - Diventa un mago del Testing
Delphi & Dintorni Webinar - Diventa un mago del TestingDelphi & Dintorni Webinar - Diventa un mago del Testing
Delphi & Dintorni Webinar - Diventa un mago del TestingMarco Breveglieri
 
Inversion of control e Dependency Injection (ITA)
Inversion of control e Dependency Injection (ITA)Inversion of control e Dependency Injection (ITA)
Inversion of control e Dependency Injection (ITA)Giancarlo Valente
 
Slide evento Code Refactoring JavaScript
Slide evento Code Refactoring JavaScriptSlide evento Code Refactoring JavaScript
Slide evento Code Refactoring JavaScriptLuca Pagliaro
 
Clean android code - Droidcon Italiy 2014
Clean android code - Droidcon Italiy 2014Clean android code - Droidcon Italiy 2014
Clean android code - Droidcon Italiy 2014Fabio Collini
 
Clean android code
Clean android codeClean android code
Clean android codefirenze-gtug
 
L'Occhio di Ra sul Testing
L'Occhio di Ra sul TestingL'Occhio di Ra sul Testing
L'Occhio di Ra sul TestingFelice Pescatore
 
Xe One Day - Adaptive Code
Xe One Day - Adaptive CodeXe One Day - Adaptive Code
Xe One Day - Adaptive CodeMirco Vanini
 
Progetto SOD Davide Sito
Progetto SOD Davide SitoProgetto SOD Davide Sito
Progetto SOD Davide SitoDavide Sito
 
Spring Framework
Spring FrameworkSpring Framework
Spring FrameworkNaLUG
 
Delphi & Dintorni Webinar - Padroneggiare i principi SOLID con Delphi
Delphi & Dintorni Webinar - Padroneggiare i principi SOLID con DelphiDelphi & Dintorni Webinar - Padroneggiare i principi SOLID con Delphi
Delphi & Dintorni Webinar - Padroneggiare i principi SOLID con DelphiMarco Breveglieri
 
Software Testing & Test Driven Development
Software Testing & Test Driven DevelopmentSoftware Testing & Test Driven Development
Software Testing & Test Driven DevelopmentSergio Santoro
 
Dependency injection questa sconosciuta
Dependency injection questa sconosciutaDependency injection questa sconosciuta
Dependency injection questa sconosciutaAndrea Dottor
 
Acadevmy - Angular Overview
Acadevmy - Angular OverviewAcadevmy - Angular Overview
Acadevmy - Angular OverviewFrancesco Sciuti
 
MuleSoft_Meetup__Official__8_.pdf
MuleSoft_Meetup__Official__8_.pdfMuleSoft_Meetup__Official__8_.pdf
MuleSoft_Meetup__Official__8_.pdfFlorence Consulting
 
Scala: come recuperare la programmazione funzionale e perché
Scala: come recuperare la programmazione funzionale e perchéScala: come recuperare la programmazione funzionale e perché
Scala: come recuperare la programmazione funzionale e perchéEdmondo Porcu
 

Semelhante a Dependency injection: the good parts (20)

Delphi & Dintorni Webinar - Diventa un mago del Testing
Delphi & Dintorni Webinar - Diventa un mago del TestingDelphi & Dintorni Webinar - Diventa un mago del Testing
Delphi & Dintorni Webinar - Diventa un mago del Testing
 
Inversion of control e Dependency Injection (ITA)
Inversion of control e Dependency Injection (ITA)Inversion of control e Dependency Injection (ITA)
Inversion of control e Dependency Injection (ITA)
 
Slide evento Code Refactoring JavaScript
Slide evento Code Refactoring JavaScriptSlide evento Code Refactoring JavaScript
Slide evento Code Refactoring JavaScript
 
Spring 2.5
Spring 2.5Spring 2.5
Spring 2.5
 
Clean android code - Droidcon Italiy 2014
Clean android code - Droidcon Italiy 2014Clean android code - Droidcon Italiy 2014
Clean android code - Droidcon Italiy 2014
 
Spring Intro
Spring IntroSpring Intro
Spring Intro
 
Java lezione1
Java lezione1Java lezione1
Java lezione1
 
Tesi di Laurea
Tesi di LaureaTesi di Laurea
Tesi di Laurea
 
Clean android code
Clean android codeClean android code
Clean android code
 
L'Occhio di Ra sul Testing
L'Occhio di Ra sul TestingL'Occhio di Ra sul Testing
L'Occhio di Ra sul Testing
 
Xe One Day - Adaptive Code
Xe One Day - Adaptive CodeXe One Day - Adaptive Code
Xe One Day - Adaptive Code
 
Progetto SOD Davide Sito
Progetto SOD Davide SitoProgetto SOD Davide Sito
Progetto SOD Davide Sito
 
Spring Framework
Spring FrameworkSpring Framework
Spring Framework
 
Delphi & Dintorni Webinar - Padroneggiare i principi SOLID con Delphi
Delphi & Dintorni Webinar - Padroneggiare i principi SOLID con DelphiDelphi & Dintorni Webinar - Padroneggiare i principi SOLID con Delphi
Delphi & Dintorni Webinar - Padroneggiare i principi SOLID con Delphi
 
Software Testing & Test Driven Development
Software Testing & Test Driven DevelopmentSoftware Testing & Test Driven Development
Software Testing & Test Driven Development
 
Dependency injection questa sconosciuta
Dependency injection questa sconosciutaDependency injection questa sconosciuta
Dependency injection questa sconosciuta
 
Acadevmy - Angular Overview
Acadevmy - Angular OverviewAcadevmy - Angular Overview
Acadevmy - Angular Overview
 
MuleSoft_Meetup__Official__8_.pdf
MuleSoft_Meetup__Official__8_.pdfMuleSoft_Meetup__Official__8_.pdf
MuleSoft_Meetup__Official__8_.pdf
 
Scala: come recuperare la programmazione funzionale e perché
Scala: come recuperare la programmazione funzionale e perchéScala: come recuperare la programmazione funzionale e perché
Scala: come recuperare la programmazione funzionale e perché
 
App Engine + Python
App Engine + PythonApp Engine + Python
App Engine + Python
 

Dependency injection: the good parts

  • 1. Dependency Injection: The Good Parts @massimogroppel1 groppedev MASSIMO GROPPELLI Senior Software Developer at Zucchetti TDD Milano (Test Driven Development) Mikamai/LinkMe - Milano 16/04/2018
  • 2. Di cosa parleremo? 1. Cos’è la Dependency Injection e quali sono i suoi vantaggi 2. Caso di studio “Correttore Ortografico” (Tutti gli esempi mostrati riguarderanno questo caso di studio) 3. Inversion of Control e Dependency Injector 4. Injection points / Forme di Dependency Injection 5. Scope dei componenti e problematiche associate 6. Test Unitari 7. Test di Integrazione 8. Profili
  • 3. Dependency Injection La Dependency Injection (DI) è una naturale tecnica di composizione degli oggetti in Object Oriented Programming (OOP). La DI è un design pattern talmente semplice che molti sviluppatori lo utilizzano per anni senza sapere come si chiama. Ogni oggetto (dipendente) può avere la necessità di collaborare con altri oggetti (dipendenze/collaboratori) per svolgere il suo unico compito (SRP Single Responsibility Principle). Nell’ambito della DI è molto importante considerare gli oggetti come dei servizi. In questo modo si evidenzia la relazione di dipendenza (transitiva) tra un oggetto ed i servizi che utilizza per svolgere la sua funzione (A sua volta un altro servizio). Il sistema composto da tutte le dipendenze è comunemente chiamato Object Graph e funzionalmente svolge il suo compito come se fosse un singolo componente. ORIGINI D.I. (2004): https://martinfowler.com/articles/injection.html
  • 4. Vantaggi della Dependency Injection Incoraggia l’utilizzo della composizione di oggetti e scoraggia l’uso dell’ereditarietà. Porta ad un basso accoppiamento tra i componenti, aumentando chiaramente la flessibilità e favorendo il riuso del software. Aumenta la testabilità dei componenti. I collaboratori che rappresentano delle dipendenze, vengono sostituiti dai test doubles. Favorisce l’applicazione di alcuni dei principi SOLIDi soprattutto se utilizzata in combinazione con IOP (Interface-Oriented Programming). ● OCP (Open/Close Principle) ● DIP (Dependency Inversion Principle) OCP Un'entità software dovrebbe essere aperta alle estensioni, ma chiusa alle modifiche DIP I moduli di alto livello non devono dipendere da quelli di basso livello. Entrambi devono dipendere da astrazioni; Le astrazioni non devono dipendere dai dettagli; sono i dettagli che dipendono dalle astrazioni. INFRASTRUTTURA usa DOMINIO e non viceversa
  • 5. Libro del 2009. Ottimo riferimento sia per principianti che per esperti di DI. Mostra i design pattern e le best practices per utilizzare al meglio la DI in ambito professionale. Non si concentra su uno specifico framework per DI ma si focalizza sui concetti importanti con il supporto di ottimi esempi. Gli esempi sono in Java e vengono utilizzati i framework: ● Spring IoC ● Google Guice ● PicoContainer
  • 6. Caso di studio “Correttore Ortografico” Il nostro “correttore ortografico” è molto semplice, e fornisce due funzionalità: ● Data una parola ci dice se è VALIDA per il lessico impostato. ● Data una parola errata prova a darci dei SUGGERIMENTI per correggerla cercando parole simili nel lessico impostato sfruttando una specifica strategia di correzione. Lessici installati: ● SPORT ITALIANI: calcio, pallavolo, pallacanestro ● CIBI ITALIANI : pizza, lasagne, pasta Strategie di correzione disponibili: ● INIZIA PER: Se si digita “p” ed è installato il dizionario CIBI ITALIANI le possibili correzioni saranno “pizza” e “pasta” visto che entrambe iniziano per il typo “p”. ● FINISCE PER : Se si digita “o” ed è installato il dizionario SPORT ITALIANI le possibili correzioni saranno “calcio” , “pallavolo” e “pallacanestro” visto che tutte terminano per il typo.
  • 7. Caso di studio “Correttore Ortografico” Il caso di studio è sviluppato in Java (1.8) ed utilizza Spring IoC Container (5.0.5.RELEASE) come Dependency Injector. Il progetto utilizza Maven ed il plug-in di Eclipse Spring Tool Suite (STS 3.9.4.RELEASE) I test utilizzano le librerie JUnit (4.12), AssertJ (3.9.1) e Mockito (1.10.19) Nel caso di studio il container di Spring viene configurato solo con files XML. URL Github: https://github.com/groppedev/di-thegoodparts-tdd-milano
  • 8. Object Graph “Correttore Ortografico”
  • 9. DI senza IoC Dependency Injection manuale con l’aiuto del Factory Pattern ● Si deve scrivere molto codice che non fornisce alcun valore aggiunto (Factory) ● I componenti sono testabili ma con difficoltà. Si modificano le factory appositamente per fare i test. ● Lo scope dei componenti è definito nel codice sorgente e non può essere modificato dall’esterno. La flessibilità dell’applicazione è ridotta. ● I client devono chiedere le loro dipendenze mentre la DI con IoC enfatizza il concetto che i client non devono avere conoscenza esplicita delle loro dipendenze.
  • 10. DI senza IoC (Client) public class SpellCheckerNoIoC { // Si utilizza il lessico dei cibi italiani. private final Lexicon lexicon = LexiconFactory.getInstance().newItalianFoodLexicon(); public boolean check(Word wordToCheck) { return lexicon.hasWord(wordToCheck); } }
  • 11. DI senza IoC (Factory) public class LexiconFactory { // SCOPE Singleton nel sorgente e non determinato dal contesto. private static final LexiconFactory INSTANCE; static { INSTANCE = new LexiconFactory(); INSTANCE.init(); } private volatile LexiconRepository lexiconREPO = new LexiconFileRepository(); private volatile LexiconQueryExecutor lexiconQE = new LexiconQueryExecutor(lexiconREPO); public static LexiconFactory getInstance() { return INSTANCE; } private LexiconFactory() {} // Factory method public Lexicon newItalianFoodLexicon() { // Composizione manuale del grafo, nessuna flessibilità return new LexiconItalianFood(lexiconREPO, lexiconQE); } }
  • 12. DI senza IoC (Test) @RunWith(JUnit4.class) public class SpellCheckerNoIoCTest { @Before public void setup() { // Nei test si vuole utilizzare l'implementazione in memoria. LexiconInMemoryRepository lexiconREPO = new LexiconInMemoryRepository(); LexiconFactory.getInstance().setLexiconREPO(lexiconREPO); } @After public void teardown(){ // Al termine del test va impostata la vecchia implementazione. LexiconRepository lexiconREPO = new LexiconFileRepository(); LexiconFactory.getInstance().setLexiconREPO(lexiconREPO); } @Test public void test() { // Run Test SpellCheckerNoIoC spellChecker = new SpellCheckerNoIoC(); boolean isValid = spellChecker.check(Word.aWord("pizza")); // Assertion Assert.assertTrue(isValid); } }
  • 13. IoC (Inversion of Control) Hollywood Principle: Un oggetto dipendente, non deve preoccuparsi di richiedere le sue dipendenze, gli verranno fornite in automatico. Dependency Injector: Framework o libreria che implementa l’Hollywood Principle Esempi di IoC: ● Un metodo di test invocato in automatico da un framework (vedi DI senza IoC (Test)) ● Un event handler richiamato in seguito al click del mouse ● Una dipendenza impostata in un oggetto dipendente da un dependency injector
  • 14. Dependency Injector Un framework di Dependency Injection (Injector o Container) è uno strumento molto potente che gestisce aspetti che vanno oltre la pura Dependency Injection: ● Gestione degli SCOPE dei componenti, garantendo a seconda dell’utilizzo la possibilità di cambiare scope senza modificare il codice sorgente. ● Gestione dell’intero ciclo di vita degli oggetti, dalla creazione alla distruzione. ● Metodologie standardizzate per risolvere problemi comuni quando si utilizza la Dependency Injection, su tutte la possibilità di impostare una dipendenza con scope più limitato rispetto a quello dell’oggetto dipendente. ● Possibilità di gestire dei profili per rendere l’applicazione completamente flessibile senza la necessità di modificare la codebase. Nei successivi esempi verranno utilizzate le annotation JSR-330, anche se la configurazione XML è da considerarsi migliore per applicazioni complesse.
  • 15. Dependency Injector per quali oggetti? Quando si inizia ad utilizzare un framework di DI si tende a gestire OGNI oggetto con l’injector, ma è sbagliato! Lo stesso sintomo si verifica quando si inizia ad utilizzare una libreria di mocking nei test, si tende a sostituire ogni oggetto con un test doubles. ● I Value Object e le Entity non vanno gestite con l’injector così come non devono mai essere sostituiti con dei mock nei test. ● Le classi di utilità che non hanno dipendenze, *Utils non devono essere gestite con l’injector così come non devono mai essere sostituite con dei mock nei test. Vanno create apposite classi statiche senza stato.
  • 16. Dependency Injector per quali oggetti? /** * Value Object */ @Immutable public final class Word { private final String text; private Word(String text) { this.text = text; } public static Word aWord(String text) { return new Word(text); } public boolean startsWith(Word word) { return this.text.startsWith(word.text); } @Override public boolean equals(Object obj) { ... } }
  • 17. Dependency Injector per quali oggetti? /** * Classe di utilità che non ha dipendenze esterne */ public class LexiconUtils { public static Collection<Word> toWord(LexiconType type, Collection<LexiconWord> lexicondWords) { Collection<LexiconWord> currentTypeWords = select(lexicondWords, new Predicate<LexiconWord>() { public boolean evaluate(LexiconWord lexiconWord) { return lexiconWord.matchesType(type); } }); return collect(currentTypeWords, new Transformer<LexiconWord, Word>() { @Override public Word transform(LexiconWord lexiconWord) { return lexiconWord.toWord(); } }); } }
  • 18. Dependency Injector - Best practices Qualificare i componenti utilizzando i namespace Soprattutto quando si devono gestire applicazioni complesse è buona prassi qualificare sempre i componenti gestiti dall’injector con un identificativo univoco utilizzando i namespace. @Named // Non Qualificato public class SuggestionService @Named(value="suggestionService") // Senza Contesto public class SuggestionService @Named(value="spellcheck.suggestions.service") // OK public class SuggestionService
  • 19. Dependency Injector - Best practices Non legare il codice di dominio al codice del framework di injection import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import groppedev.dithegoodparts.domain.Word; import groppedev.dithegoodparts.domain.lexicon.LexiconItalianFood; public class WordsRepository implements ApplicationContextAware { private ApplicationContext applicationContext; public Collection<Word> words() { return applicationContext.getBean(LexiconItalianFood.class).words(); } @Override public void setApplicationContext(ApplicationContext applicationContext) { this.applicationContext = applicationContext; } }
  • 20. Injection Point - Field Injection // Annotations JSR-330. @Named public class FieldInjection implements InjectionPoint { @Inject private Provider<Example> provider; public void execute() { Objects.requireNonNull(provider); System.out.println(provider.get()); } public void executeBis() { Objects.requireNonNull(provider); System.out.println(provider.get()); } }
  • 21. Injection Point - Field Injection ● Forma di DI concisa. ● Si creano oggetti NON testabili. ● Si creano oggetti dipendenti dal framework di Dependency Injection. ● Va aggiunta la validazione in tutti i metodi. ● Tende a nascondere le dipendenze. ● Favorisce il crescere dei collaboratori senza accorgersi del problema fino a violare il principio SRP DA NON UTILIZZARE La Field Injection è un ANTI-PATTERN
  • 22. // Annotations JSR-330. @Named public class SetterInjection implements InjectionPoint { private Provider<Example> provider; public void execute() { Objects.requireNonNull(provider); System.out.println(provider.get()); } public void executeBis() { Objects.requireNonNull(provider); System.out.println(provider.get()); } // Setter non esposto sull'interfaccia @Inject public void setProvider(Provider<Example> provider) { this.provider = provider; } } Injection Point - Setter/Method Injection
  • 23. Injection Point - Setter/Method Injection ● Vanno bene per esplicitare dipendenze opzionali. ● Sono utili per gestire casistiche particolari e NON di dominio (ES: ComponentProvider nel caso di studio) ● Ottima soluzione per risolvere le dipendenze circolari, che però normalmente sono un segnale di cattivo design ● Funziona bene con l’ereditarietà, ma noi vorremmo lavorare con la composizione.
  • 24. Injection Point - Setter/Method Injection ● Si creano oggetti testabili ma fixture di test poco manutenibili. ● Va aggiunta la validazione in tutti i metodi. ● Tende a nascondere le dipendenze se utilizzato con dipendenze obbligatorie. ● Incoraggia a creare oggetti MUTABILI e potenzialmente INCOMPLETI. ● Favorisce il crescere dei collaboratori senza accorgersi del problema fino a violare il principio SRP. ● Può causare più facilmente errori a RUNTIME DA UTILIZZARE SOLO PER MODELLARE LE DIPENDENZE OPZIONALI La Setter Injection si può utilizzare ma prestando molta attenzione! I setter devono essere metodi delle implementazioni NON delle interfacce
  • 25. Injection Point - Constructor Injection // Annotations JSR-330. @Immutable @Named public final class ConstructorInjection implements InjectionPoint { private final Provider<Example> provider; // Ogni componente dovrebbe avere uno solo costruttore! @Inject public ConstructorInjection(Provider<Example> provider){ // Validazione solo nel costruttore. Objects.requireNonNull(provider); this.provider = provider; } public void execute() { System.out.println(provider.get()); } public void executeBis() { System.out.println(provider.get()); } }
  • 26. Injection Point - Constructor Injection ● Per le dipendenze opzionali utilizzare setter/method injection ● Va bene per esplicitare dipendenze obbligatorie. ● Si creano oggetti testabili e fixture di test manutenibili ● E’ richiesta la validazione solamente nell’unico costruttore (Più costruttori molte volte indicano la violazione del principio SRP) ● Limita gli errori a RUNTIME ● Evidenzia in modo chiaro quando i collaboratori aumentano troppo e favorisce la sistemazione del design prima di violare il principio SRP ● Incoraggia a creare oggetti IMMUTABILI e COMPLETI eliminando il problema della concorrenza e favorendo notevolmente la diminuzione degli errori. ● Evidenzia le dipendenze al posto di nasconderle creando oggetti utilizzabili facilmente anche senza il framework di dependency injection
  • 27. Injection Point - Conclusioni La FIELD INJECTION non va mai utilizzata! Utilizzare la SETTER INJECTION solo per i collaboratori OPZIONALI e la CONSTRUCTOR INJECTION per i collaboratori OBBLIGATORI. I componenti devono avere un solo COSTRUTTORE e non nascondere mai le DIPENDENZE. Anche i più grandi critici dei framework dei Dependency Injection, ammettono che la CONSTRUCTOR INJECTION è un ottima tecnica per creare oggetti. Vedi http://www.yegor256.com/2014/10/03/di-containers-are-evil.html
  • 28. Scope Lo scope di un oggetto indica la durata del ciclo di vita dell’oggetto stesso in un determinato contesto che nel nostro ambito sarà una istanza del container di dependency injection. Unscoped / Prototype : Viene creato un nuovo oggetto ad ogni richiesta. Singleton: Viene creato un nuovo oggetto solo alla prima richiesta e poi viene sempre restituita l’istanza creata la prima volta. Lo scope è fornito dal contesto e non deve essere esplicitato nel codice sorgente! Dovrebbe essere possibile cambiare scope senza modificare il codice Esistono altri scope (Request, Session e Thread), ma ci concentreremo su Prototype e Singleton che sono quelli di base ed i più importanti.
  • 29. Scope - Qual è il migliore Lo scope di default, incredibilmente è diverso a seconda del framework di dependency injection utilizzato: ● Google Guice: Unscoped / Prototype ● Standard JSR-330: Unscoped / Prototype ● Spring IoC Container: Singleton Se si parte dal presupposto che il container di dependency injection dovrebbe gestire principalmente dei servizi per lo più immutabili, sicuramente l’approccio di Spring IoC Container è il più corretto.
  • 30. Scope - Problematiche La principale problematica da risolvere quando si lavora con gli scope è la gestione di una dipendenza di tipo Unscoped / Prototype in un oggetto dipendente di tipo Singleton. In sostanza quando si ha una dipendenza di scope più limitato rispetto allo scope del dipendente. Quello sopra presentato, è un problema talmente comune, che anche lo standard JSR-330 ha proposto una soluzione: javax.inject.Provider<T> Questa interfaccia deve essere implementata da tutti i framework di dependency injection che aderiscono allo standard JSR-330
  • 31. Come fare i Test TEST UNITARI: Rigorosamente senza l’injector, si istanziano gli oggetti con la new e si passano dei test doubles al posto delle dipendenze. @RunWith(JUnit4.class) public class SpellCheckerTest { @Test public void isValidTest() { // Fixture. Lexicon lexicon = Mockito.mock(Lexicon.class); SuggestionService suggestionService = Mockito.mock(SuggestionService.class); // Stubbing Mockito.when(lexicon.hasWord(Word.aWord("massimo"))).thenReturn(true); // Run Test. SpellChecker spellChecker = new SpellChecker(lexicon, suggestionService); boolean isValid = spellChecker.check(Word.aWord("massimo")); // Assertions. Assert.assertTrue(isValid); } }
  • 32. Come fare i Test TEST DI INTEGRAZIONE: Test di accettazione sempre completi di tutto il ciclo di vita dell’injector, deve essere caricata tutta la configurazione. @RunWith(JUnit4.class) public class ApplicationStaticTest { private static ApplicationStaticAPI app; @BeforeClass public static void startApplication(){ System.setProperty(AbstractEnvironment.ACTIVE_PROFILES_PROPERTY_NAME, "TEST,STATIC_APP"); app = Application.startStaticApplication(); } @AfterClass public static void stopApplication(){ app.close(); System.setProperty(AbstractEnvironment.ACTIVE_PROFILES_PROPERTY_NAME, ""); } @Test public void validWordTest(){ // Run Test. boolean validWord = app.checkWord(Word.aWord("pizza")); // Assertion. Assert.assertTrue(validWord); } }