La presentazione è in caricamento. Aspetta per favore

La presentazione è in caricamento. Aspetta per favore

1 Dependency Injection Sandro Pedrazzini Approfondimento Dependency Injection.

Presentazioni simili


Presentazione sul tema: "1 Dependency Injection Sandro Pedrazzini Approfondimento Dependency Injection."— Transcript della presentazione:

1 1 Dependency Injection Sandro Pedrazzini Approfondimento Dependency Injection

2 2 Dependency Injection Sandro Pedrazzini Dependency Injection (DI) Modalità di configurazione di un oggetto, in cui le dipendenze delloggetto vengono specificate da entità esterne Lalternativa è che loggetto stesso si definisca da solo, al suo interno, le dipendenze Anche chiamata Inversion of Control (IoC), che però ha un significato più esteso (principio del framework in generale)

3 3 Dependency Injection Sandro Pedrazzini Esempio Classe che gestisce lesecuzione del pagamento di un certo ordine (BillingService) Sia la classe responsabile dellelaborazione dei dati della carta di credito (CreditCardProcessor), sia la classe responsabile di mantenere le informazioni di log (TransactionLog) del pagamento devono poter essere modificate

4 4 Dependency Injection Sandro Pedrazzini Esempio (2)

5 5 Dependency Injection Sandro Pedrazzini Esempio (2) BillingService carica lordine sulla carta di credito. La transazione verrà registrata (log) sia in caso di riuscita, sia in caso di insuccesso public interface IBillingService { Receipt chargeOrder(Order order, CreditCard creditCard); }

6 6 Dependency Injection Sandro Pedrazzini Esempio (3) Schema di elaborazione public Receipt chargeOrder(Order order, CreditCard creditCard) { … ChargeResult result = processor.process(order.getAmount(), creditCard); transactionLog.logChargeResult(result); … }

7 7 Dependency Injection Sandro Pedrazzini Esempio (4) public class BillingService implements IBillingService { public Receipt chargeOrder(Order order, CreditCard creditCard) { ICreditCardProcessor processor = new PaypalCreditCardProcessor(); ITransactionLog transactionLog = new DatabaseTransactionLog(); try { ChargeResult result = processor. process(order.getAmount(), creditCard); transactionLog.logChargeResult(result); return... } catch (UnreachableException e) {... }

8 8 Dependency Injection Sandro Pedrazzini Esempio (4) Situazione attuale

9 9 Dependency Injection Sandro Pedrazzini Commenti Il codice precedente pone problemi di modularità e di test La dipendenza diretta, in compilazione, allelaboratore di carta di credito significa che ogni chiamata al codice, anche durante il test, esegue una transazione reale sulla carta di credito ICreditCardProcessor processor = new PaypalCreditCardProcessor();

10 10 Dependency Injection Sandro Pedrazzini Obiettivo

11 11 Dependency Injection Sandro Pedrazzini Factory Una factory permette di separare la classe client (BillingService) dalla classe che implementa un servizio (classi concrete di CreditCardProcessor e TransactionLog) Una factory semplice utilizza alcuni metodi static per stabilire quale implementazione collegare a una data interface

12 12 Dependency Injection Sandro Pedrazzini Factory (2) public class CreditCardProcessorFactory { private static ICreditCardProcessor instance; public static void setInstance(ICreditCardProcessor processor) { instance = processor; } public static ICreditCardProcessor getInstance() { if (instance == null) { throw new IllegalStateException("Factory not initialized); } return instance; }

13 13 Dependency Injection Sandro Pedrazzini Factory (3) public class RealBillingService implements IBillingService { public Receipt chargeOrder(Order order, CreditCard creditCard) { ICreditCardProcessor processor = CreditCardProcessorFactory.getInstance(); ITransactionLog transactionLog = TransactionLogFactory.getInstance(); try { ChargeResult result = processor.process(order.getAmount(), creditCard); transactionLog.logChargeResult(result); return... } catch (UnreachableException e) {... }

14 14 Dependency Injection Sandro Pedrazzini Factory (4) CreditCardFactory TransactionLogFactory

15 15 Dependency Injection Sandro Pedrazzini Factory (5) La scelta dellimplementazione di ICreditCardProcessor e ITransactionLog viene fatta attraverso le factory In questo modo si diminuisce la dipendenza esistente tra BillingService e queste classi Limplementazione dei test diventa più semplice

16 16 Dependency Injection Sandro Pedrazzini Unit Test public class BillingServiceTest extends TestCase { private ITransactionLog transactionLog = new InMemoryTransactionLog(); private ICreditCardProcessor processor = new FakeCreditCardProcessor(); public void setUp() { TransactionLogFactory.setInstance(transactionLog); CreditCardProcessorFactory.setInstance(processor); public void testBilling() {... } public void tearDown() { TransactionLogFactory.setInstance(null); CreditCardProcessorFactory.setInstance(null); }

17 17 Dependency Injection Sandro Pedrazzini Unit Test public void testBilling() { IBillingService billingService = new BillingService(); Order order = new PizzaOrder(100); CreditCard creditCard = new CreditCard("1234", 11, 2010); Receipt receipt = billingService.chargeOrder(order, creditCard); assertTrue(receipt.hasSuccessfulCharge()); assertEquals(100, receipt.getAmountOfCharge()); assertTrue(transactionLog.successfullyLogged()); }

18 18 Dependency Injection Sandro Pedrazzini Commenti Codice problematico: le implementazioni vengono praticamente gestite in variabili globali (static) Se il tearDown() dovesse venir interrotto per qualche motivo, avremmo limplementazione di test che rimane nella factory e potrebbe creare problemi ad altri test Non è possibile eseguire più test in parallelo (variabili static)

19 19 Dependency Injection Sandro Pedrazzini Commenti (2) La dipendenza è nascosta nel codice Se per un motivo qualsiasi la factory venisse inizializzata in modo sbagliato, bisognerebbe attendere il primo pagamento per accorgersi del problema La cosa si complicherebbe se il numero di factory dovesse crescere

20 20 Dependency Injection Sandro Pedrazzini Dependency Injection Con questo pattern si fa un passo ulteriore verso la separazione tra comportamento e risoluzione della dipendenza BillingService non è più responsabile della risoluzione, perché gli oggetti ICreditCardProcessor e ITransactionLog vengono passati come parametri

21 21 Dependency Injection Sandro Pedrazzini Dependency Injection (2) Gli oggetti dipendenti vengono forniti a BillingService dallesterno, attraverso il costruttore (o attraverso un metodo set()) public BillingService(ICreditCardProcessor creditCardProcessor, ITransactionLog transactionLog) { fCreditCardProcessor = creditCardProcessor; fTransactionLog = transactionLog; }

22 22 Dependency Injection Sandro Pedrazzini Dependency Injection (3) public class BillingService implements IBillingService { private ICreditCardProcessor fCreditCardProcessor; private ITransactionLog fTransactionLog; public BillingService(ICreditCardProcessor creditCardProcessor, ITransactionLog transactionLog) { fCreditCardProcessor = creditCardProcessor; fTransactionLog = transactionLog; } public Receipt chargeOrder(Order order, CreditCard creditCard) { try { ChargeResult result= fCreditCardProcessor.process(order.getAmount(), creditCard); fTransactionLog.logChargeResult(result); return... } catch (Exception e) {... }

23 23 Dependency Injection Sandro Pedrazzini Dependency Injection (4) Injector

24 24 Dependency Injection Sandro Pedrazzini Commenti Non vengono più usate factory, quindi eliminata la dipendenza tra BilingService e factory Si possono modificare i test, eliminando setUp() e tearDown()

25 25 Dependency Injection Sandro Pedrazzini Unit test public class BillingServiceTest { private ICreditCardProcessor processor = new FakeCreditCardProcessor(); private ITransactionLog transactionLog = new InMemoryTransactionLog(); public void testSuccessfulCharge() { Order order = new PizzaOrder(100); CreditCard creditCard = new CreditCard("1234", 11, 2010); IBillingService billingService = new BillingService(processor, transactionLog); Receipt receipt = billingService.chargeOrder(order, creditCard); assertTrue(receipt.hasSuccessfulCharge()); assertEquals(100, receipt.getAmountOfCharge()); assertTrue(transactionLog.successfullyLogged()); }

26 26 Dependency Injection Sandro Pedrazzini Commenti Con il passaggio delle dipendenze al costruttore, per ogni nuova dipendenza si deve aggiungere un parametro. In questo modo il compilatore ci può avvertire se nel test ci sono dipendenze da inserire: le dipendenze vengono esposte via API Ora le classi client di IBillingService devono gestire loro le dipendenze

27 27 Dependency Injection Sandro Pedrazzini Generalizzazione Si può andare indietro nella catena di dipendenze (classi che dipendono da unimplementazione di IBillingService) Queste classi dovranno accettare IBillingService come parametro Il punto di fermata saranno le classi top-level

28 28 Dependency Injection Sandro Pedrazzini Generalizzazione (2) Per gestire le dipendenze nelle classi top-level è utile usare un framework, che aiuti a ricostruire la gerarchia di dipendenze Ne esistono diversi –Bean Container di Spring –Guice –PicoContainer –…

29 29 Dependency Injection Sandro Pedrazzini DI con Guice Il pattern DI permette di scrivere codice più modulare e più facilmente testabile Framework come Guice ne facilitano limplementazione Permettono di associare le interface alle implementazioni necessarie nei vari contesti

30 30 Dependency Injection Sandro Pedrazzini DI con Guice (2) La configurazione è specificata in un modulo Guice, una classe che implementa linterfaccia Module public class BillingModule extends AbstractModule { protected void configure() { bind(ITransactionLog.class).to(DatabaseTransactionLog.class); bind(ICreditCardProcessor.class).to(PaypalCreditCardProcessor.class); bind(IBillingService.class).to(BillingService.class); }

31 31 Dependency Injection Sandro Pedrazzini DI con Guice (3) nel costruttore di BillingService, si indica a Guice di passare le dipendenze come specificate nel modulo Nota: non è necessario che le dipendenze vengano specificate con interface. La relazione classe-sottoclasse basta

32 32 Dependency Injection Sandro Pedrazzini DI con Guice (4) public class BillingService implements IBillingService { private ICreditCardProcessor fCreditCardProcessor; private ITransactionLog public BillingService(ICreditCardProcessor creditCardProcessor, ITransactionLog transactionLog) { fCreditCardProcessor = creditCardProcessor; fTransactionLog = transactionLog; } public Receipt chargeOrder(Order order, CreditCard creditCard) {... }

33 33 Dependency Injection Sandro Pedrazzini Injectable properties: non è necessario prevedere il costruttore per linizializzazione DI con Guice (5) public class BillingService implements IBillingService private ICreditCardProcessor private ITransactionLog fTransactionLog; public Receipt chargeOrder(Order order, CreditCard creditCard) {... }

34 34 Dependency Injection Sandro Pedrazzini Utilizzo public static void main(String[] args) { Injector injector = Guice.createInjector(new BillingModule()); //riceveloggetto top level IBillingService billingService = injector.getInstance(IBillingService.class); Receipt receipt = billingService.chargeOrder( new PizzaOrder(100), new CreditCard("1234", 6, 2012)); if (receipt.hasSuccessfulCharge()) { System.out.println("Receipt value: " + receipt.getAmountOfCharge()); } else { System.out.println("Receipt value: " + receipt.getErrorMessage()); }

35 35 Dependency Injection Sandro Pedrazzini Altri punti di injection (2) Anche un metodo può essere segnalato Guice chiama questo metodo con loggetto associato subito dopo aver chiamato il costruttore Non devessere necessariamente un metodo set(), può avere più parametri e non devessere per forza public void setTransactionLog(ITransactionLog transactionLog){ fTransactionLog = transactionLog; }

36 36 Dependency Injection Sandro Pedrazzini Utilizzo di Guice per test (1) public class TestModule extends AbstractModule { protected void configure() { bind(ITransactionLog.class).to(InMemoryTransactionLog.class); bind(ICreditCardProcessor.class).to(FakeCreditCardProcessor.class); bind(IBillingService.class).to(BillingService.class); }

37 37 Dependency Injection Sandro Pedrazzini Utilizzo di Guice per test (2) public class BillingServiceWithGuiceTest { private Injector injector = Guice.createInjector(new public void testSuccessfulCharge() { Order order = new PizzaOrder(100); CreditCard creditCard = new CreditCard("1234", 11, 2010); IBillingService billingService = injector.getInstance(IBillingService.class); Receipt receipt = billingService.chargeOrder(order, creditCard); assertTrue(receipt.hasSuccessfulCharge()); assertEquals(100, receipt.getAmountOfCharge()); assertTrue(billingService.getTransactionLog().successfullyLogged()); }

38 38 Dependency Injection Sandro Pedrazzini Provider Capita che per la creazione di un oggetto sia necessario più codice che la semplice chiamata al costruttore Normalmente in questi casi scriveremmo un metodo factory, che contenga la chiamata al costruttore, più il codice necessario In Guice il metodo factory viene specificato nel modulo di binding e viene method, dal nome dellannotation da applicare Il binding in configure() va tolto

39 39 Dependency Injection Sandro Pedrazzini public class BillingModule extends AbstractModule { protected void configure() { // bind(ITransactionLog.class).to(DatabaseTransactionLog.class);... ITransactionLog provideTransactionLog() { DatabaseTransactionLog transactionLog = new DatabaseTransactionLog(); transactionLog.setJdbcUrl("jdbc:mysql://localhost/trans"); transactionLog.setThreadPoolSize(30); return transactionLog; }

40 40 Dependency Injection Sandro Pedrazzini Classe Provider Quando un inizia ad essere troppo complesso, si può pensare di creare una classe Provider dedicata Guice prevede a questo scopo uninterfaccia Provider che va implementata public interface Provider { T get(); }

41 41 Dependency Injection Sandro Pedrazzini Classe Provider (2) public class DBTransactionProvider implements Provider { public ITransactionLog get() { DatabaseTransactionLog transactionLog = new DatabaseTransactionLog(); transactionLog.setJdbcUrl("jdbc:mysql://localhost/trans"); transactionLog.setThreadPoolSize(30); return transactionLog; } Classe responsabile della creazione delloggetto ITransactionLog

42 42 Dependency Injection Sandro Pedrazzini Classe Provider (3) Binding nella classe di modulo public class BillingModule extends AbstractModule { protected void configure() { bind(ITransactionLog.class). toProvider(DBTransactionProvider.class);... }... }

43 43 Dependency Injection Sandro Pedrazzini Request Injector Necessario quando allinterno di configure si deve instanziare un oggetto da usare in modo particolare: si crea e si forza il passaggio delle dipendenze ai public class BillingModule extends AbstractModule { protected void configure() { bind(ITransactionLog.class).to(DatabaseTransactionLog.class); bind(ICreditCardProcessor.class).to(PaypalCreditCardProcessor.class); IBillingService billingService = new BillingService(); //necessario forzare injection requestInjection(billingService); … }

44 44 Dependency Injection Sandro Pedrazzini Utilizzo contemporaneo di più moduli Più moduli possono essere usati contemporaneamente, se è necessario usare oggetti della stessa classe, ma inizializzati diversamente Lutilizzo di più moduli può essere utile anche a cascata: se un oggetto di un modulo devessere creato manualmente allinterno di un provider, se ha dipendenze, queste potrebbero essere specificate allinterno di un secondo modulo.

45 45 Dependency Injection Sandro Pedrazzini Utilizzo contemporaneo di più moduli (2) public class DBTransactionProvider implements Provider { public ITransactionLog get() { Injector injector = Guice.createInjector(new DBProductionModule()); ITransactionLog transactionLog = injector.getInstance(ITransactionLog.class); … return transactionLog; }

46 46 Dependency Injection Sandro Pedrazzini Scope Per default, Guice restituisce una nuova istanza ogni volta che un oggetto viene richiesto Esempio –Ogni colta che si richiede un oggetto ITransactionLog a questo modulo, viene creata una nuova istanza di InMemoryTransactionLog bind(ITransactionLog.class). to(InMemoryTransactionLog.class);

47 47 Dependency Injection Sandro Pedrazzini Scope (2) Esistono altri scope, a scelta: –per lintero ciclo di vita dellapplicazione –per la durata di una sessione –per una singola richiesta Esempio –Scope configurato durante il binding: bind(ITransactionLog.class). to(InMemoryTransactionLog.class). in(Singleton.class);

48 48 Dependency Injection Sandro Pedrazzini Scope (3) Eager singleton –Guice prevede la possibilità di specificare oggetti singleton da costruire subito, in anticipo (eagerly) bind(ITransactionLog.class). to(InMemoryTransactionLog.class). asEagerSingleton();

49 49 Dependency Injection Sandro Pedrazzini DI con Spring Il Bean Container di Spring risolve le dipendenze durante il caricamento dei singoli oggetti Le dipendenze vengono specificate in un file di configurazione Le dipendenze possono essere specificate, con Le dipendenze possono essere passate via costruttore o via metodi set

50 50 Dependency Injection Sandro Pedrazzini Definizione di Bean Le definizioni dei Bean allinterno di una variante di BeanFactory sono rappresentate con oggetti BeanDefinition, contenenti almeno (anche solo implicitamente): –nome o id –nome della classe –elementi di configurazione del comportamento (prototype o singleton, inizializzazione, etc.) –argomenti di costruttore e property values da assegnare al bean creato –altri bean necessari al bean considerato per eseguire il suo lavoro (dipendenze)

51 51 Dependency Injection Sandro Pedrazzini Singleton I Bean sono definiti per essere installati e attivati in due modalità: singleton o non-singleton (prototype) Altre modalità sono disponibili per ApplicationContext di applicazioni Web (request, session, global session (portlet)) Quando un bean è un singleton, ununica instanza (shared) del bean viene gestita e creata (per bean e container) Per default i bean sono attivati in modalità singleton

52 52 Dependency Injection Sandro Pedrazzini Singleton vs. Prototype Singleton (per bean e contesto)

53 53 Dependency Injection Sandro Pedrazzini Singleton vs. Prototype Prototype (istanza)

54 54 Dependency Injection Sandro Pedrazzini Proprietà e Dipendenze Le dipendenze possono essere specificate con costruttore o con metodi set 1

55 55 Dependency Injection Sandro Pedrazzini Spring DI (3) public class ExampleBean { private AnotherBean fBeanOne; private YetAnotherBean fBeanTwo; private int fValue; public void setBeanOne(AnotherBean beanOne) { fBeanOne = beanOne; } public void setBeanTwo(YetAnotherBean beanTwo) { fBeanTwo = beanTwo; } public void setIntegerProperty(int value) { fValue = value; } 1 Esempio

56 56 Dependency Injection Sandro Pedrazzini BillingService con Spring Specificare metodi set per RealBillingService public class BillingService implements IBillingService { private ICreditCardProcessor fCreditCardProcessor; private ITransactionLog fTransactionLog;... public void setCreditCardProcessor(ICreditCardProcessor processor) { fCreditCardProcessor = processor; } public void setTransactionLog(ITransactionLog transactionLog) { fTransactionLog = transactionLog; }... }

57 57 Dependency Injection Sandro Pedrazzini BillingService con Spring (2)

58 58 Dependency Injection Sandro Pedrazzini Confronto: Guice e Spring Spring rappresenta un intero stack di elementi per applicazioni enterprise, di cui DI è un tassello Si può comunque usare Spring anche solo per DI (Spring non è un framework all or nothing) Guice si concentra invece puramente sugli aspetti di DI (e AOP)

59 59 Dependency Injection Sandro Pedrazzini Confronto: Guice e Spring (2) La configurazione Spring (esplicita) avviene in XML, quindi scomoda e poco concisa (tool di sviluppo che offrono una buona integrazione tra Java e XML aiutano comunque a rendere mantenibili le applicazioni) Esiste in Spring una modalità di configurazione autowired Guice utilizza annotations, una buona via di mezzo: una modalità concisa, esplicita, mantenibile e supportata dal linguaggio di programmazione


Scaricare ppt "1 Dependency Injection Sandro Pedrazzini Approfondimento Dependency Injection."

Presentazioni simili


Annunci Google