Advanced Programming Dependencies Among Objects

Consider the below set of dependent objects The design pattern decouples dependent objects so that they may be configured and tested independently. BookInventory BookStore CreditCardService remove(Book) purchase(List, CreditCard) : double debit(CreditCard, dollars : int, cents : int) Guice manages dependencies among objects add(Book) refund(List CreditCard) : double credit(CreditCard, dolars : int, cents : int) and handles the complexity of wiring together inStock(Book) : int complex object relationships. REST FirstBankOfPSU BookDatabase FirstBankOfPortlandStateServlet serverHost : String directory : File serverPort : int Dependency Injection fileName : String

The Dependency Injection design pattern • A BookStore requires a BookInventory and a Testing with mock objects using Mockito CreditCardService to get its work done • Managing dependencies with Google Guice We’ve already extracted interfaces to allow us to • • abstract out the behavior that we need

We want to be able to replace the implementations of • BookInventory and CreditCardService without affecting the code in BookStore

Copyright ©2010-2021 by David M. Whitlock. Permission to make digital or hard copies of part or all of this work for personal or classroom use is granted without fee provided that copies are not made or distributed for profit or commercial advantage and that copies bear this notice and full citation on the first page. To copy otherwise, to republish, to post on servers, or to redistribute to lists, requires prior specific permission and/or fee. Request permission to publish from [email protected]. Last updated March 13, 2021.

1 2

How can we implement BookStore? How can we implement BookStore? package edu.pdx.cs410J.di; public double purchase( List books, CreditCard card) { import java.io.File; double total = 0.0d; import java.util.List; for (Book book : books) { inventory.remove(book); public class BookStore total += book.getPrice(); { } private final BookInventory inventory; CreditTransactionCode code = private final CreditCardService cardService; cardService.debit(card, total); if (code == CreditTransactionCode.SUCCESS ) { public BookStore() { return total; String tmpdir = } else { System.getProperty( "java.io.tmpdir" ); throw new CreditCardTransactionException(code); File directory = new File( tmpdir ); } this.inventory = } new BookDatabase( directory, "books.txt" ); this.cardService = The purchase method uses the dependencies new FirstBankOfPSU( "localhost", 8080 ); }

When the BookStore is created, it also creates its dependencies

Dependencies are stored in final fields because • they only need to be created once

3 4 What is wrong with this solution? “Don’t call us, we’ll call you”

Sure, this code works, but.. The “Hollywood Principle” states that dependent code should require that its dependencies are provided to it You can’t test this code! • Practically speaking, it means pass dependencies – Tests would have to execute against the actual • into the constructor (slow) and credit card service ($$$) public BookStore( BookInventory inventory, – Lots of data setup: create database file and credit CreditCardService cardService ) { card account this.inventory = inventory; this.cardService = cardService; The BookStore has to know how to configure its } • dependencies Now the BookStore class doesn’t have to know about the – Does the BookStore really care what port the concrete types of its dependencies credit card service runs on? Let the main program create and configure • dependencies If another class wants to use the BookDatabase, it • would have to create another instance public class BookStoreApp { public static void main(String... args) { – Two BookDatabases can’t work with the same file String tmpdir = System.getProperty( "java.io.tmpdir" ); File directory = new File( tmpdir ); If you want to switch out another implementation of BookInventory inventory = • the dependencies, you have to change the new BookDatabase( directory, "books.txt" ); BookStore CreditCardService cardService = new FirstBankOfPSU( "localhost", 8080 ); – Again, does the BookStore really care what kind BookStore store = of BookInventory is uses? new BookStore(inventory, cardService); 5 6

Testing with Mock Objects Testing with Mock Objects

Now that a BookStore gets passed its dependencies, you Test that BookStore invokes the expected methods of its can test the class without using the “production” dependencies BookInventory and CreditCardService Override methods to keep track of how it • Instead of using real dependencies, test with “mock” was invoked objects that provide an implementation of the interface package edu.pdx.cs410J.di; that is useful for testing import org.junit.Test; package edu.pdx.cs410J.di; import java.util.Collections; import static org.junit.Assert.assertEquals; public abstract class MockObject { import static org.junit.Assert.assertNotNull; protected void shouldNotInvoke() { throw new UnsupportedOperationException( public class BookStoreTest { "Did not expect this method to be invoked"); @Test } public void testBookIsPurchased() { } final Book[] removedBook = new Book[1]; BookInventory inventory = new MockBookInventory() { public void remove( Book book ) { package edu.pdx.cs410J.di; removedBook[0] = book; } public class MockBookInventory extends MockObject }; implements BookInventory { CreditCardService cardService = new MockCreditCardService() { public CreditTransactionCode debit( CreditCard card, double amount ) { public void remove( Book book ) { return CreditTransactionCode.SUCCESS; shouldNotInvoke(); } }; } }

7 8 Testing with Mock Objects Mock Objects are Awkward

Once the mock dependencies are set up Mock Objects get the job done, but...

BookStore Create the Lots of verbose boilerplate code • • Invoke the method you want to test • When interface changes (new method, e.g.), mock • Verify that the dependency was invoked as you has to change • expected Subclassing mock objects is hokey BookStore store = • new BookStore(inventory, cardService); – Lots of anonymous inner classes CreditCard card = new CreditCard( "123" ); final Book testBook = – Have to keep track of state that was passed to new Book("title", "author", 1.00d); dependency double total = store.purchase( Collections.singletonList(testBook), card ); assertEquals( testBook.getPrice(), total, 0.0d ); There’s got to be a better way, right?

final Book removed = removedBook[0]; assertNotNull( removed ); assertEquals(testBook.getTitle(), removed.getTitle()); assertEquals(testBook.getAuthor(), removed.getAuthor()); assertEquals(testBook.getPrice(), removed.getPrice(), 0.0d); } }

9 10

Mock Objects with Mockito Mock Objects with Mockito

There are several testing libraries for Java that allow you @Test to easily create mock objects public void testCreditCardIsCharged() { Book book = mock(Book.class); double price = 1.00d; We’re going to learn about Mockito (http://mockito.org/) when( book.getPrice() ).thenReturn( price ); A Java API that creates mock objects for interfaces • BookInventory inventory = and classes mock(BookInventory.class); Contains a domain specific language (DSL) for • CreditCardService cardService = constructing mock objects mock(CreditCardService.class); when(cardService.debit(any( CreditCard.class ), You can specify what behavior you expect out of the • anyDouble() )) mock objects .thenReturn( CreditTransactionCode.SUCCESS );

Add the dependency in your pom.xml BookStore store = new BookStore(inventory, cardService); org.mockito CreditCard card = mock(CreditCard.class); mockito-core 1.8.5 double total = store.purchase( test Collections.singletonList(book), card ); assertEquals( book.getPrice(), total, 0.0d ); verify( cardService ).debit( card, price ); }

Let’s take a closer look...

11 12 Creating mock objects Specifying behavior of mock objects

The most interesting methods are all in the The when method specifies how a method of the mock org.mockito.Mockito class object should behave

static OngoingStubbing when(T The mock method creates a for an interface • or class methodCall)

static T mock(Class classToMock) Because they cannot be overridden by the magical • • class loader, final methods cannot be sent to when Unless instructed otherwise, invoking a method of • the mock object will noop or return null The OngoingStubbing class specifies what the behavior of the mock method Mockito uses crazy class loading tricks to accomplish thenReturn(T value) will cause the mock method • this • to return a given value thenThrow(Throwable t) We mocked all of our objects in the previous example will throw an exception • when the mock method is invoked No need to invoke constructors • thenCallRealMethod() will invoked the real Only had to specify the state we needed • implementation of the mock object to be invoked • – The test doesn’t care about the title or author In general, you want to chain the when and then calls of the Book together We can add a method to an interface without having when( book.getPrice() ).thenReturn( price ); • to update a mock implementation

13 14

Method Argument Matching Method Argument Matching

The same mock method can be configured with different If at least one arguments to a mock method is one of the argument values “matchers”, then all arguments must be matchers

Use the eq() methods for matching on a single value when( cardService.debit(card, price) • .thenReturn(SUCCESS)) when( cardService.debit( any(CreditCard.class), when( cardService.debit(card, -1.0) eq(-1.0) ) .thenReturn(INVALID_AMOUNT)) .thenReturn(INVALID_AMOUNT);

Sometimes the behavior of the mock method doesn’t The AdditionalMatchers class has even more matchers depend on the value of its arguments Boolean operations on matchers: and, or, not, etc. • Comparison operations: gt, lt, geq Always return SUCCESS no matter which card is • • passed in

Matchers, the superclass of Mockito, has methods for filtering which values apply to the when Basically, there are bunch of type safe any methods • any() object (or null) or any(Class) object of a • given type anyInt(), anyDouble(), anyString(), etc. • isNull, isNotNull, and same(T), matches(String • regex)

15 16 Verifying Mock Objects Managing Dependencies

After configuring your mock objects and sending them to As your application scales, wiring together dependencies the API you want to test, the mock objects are verified can become very cumbersome

Mock objects keep a history of what methods were Large initialization code that references tons of • invoked with which arguments • classes

The verify method tests whether or not a mock Difficult to change out old implementations for new • method was invoked with the expected arguments • implementations

To verify that cardService was invoked with the expected End up relying on to create instances card and price: • – Factory Pattern verify( cardService ).debit( card, price ); Create instances of a class using a static ∗ method You can also use matchers with verify – verify(cardService).debit( same(card), eq(price) ); Access the one-and-only instance of a class via ∗ a static method

Lots of boilerplate code that is tricky to get right • – Concurrent access to singletons – Life cycle of pooled/cached objects

17 18

Google Guice Managing Dependencies without Guice

Google Guice (pronounced “juice”) is a framework for From BookStoreApp.java configuring and injecting dependencies into Java classes public static void main(String... args) throws JAXBException, IOException { Leverages annotations to provide type-safety • String tmpdir = Well-known objects are registered in one place (a • System.getProperty("java.io.tmpdir"); “module”) File directory = new File(tmpdir); BookInventory inventory = Objects that depend on well-known objects are • new BookDatabase(directory); created by Guice via reflection addBooks(inventory); – Dependencies are injected into constructors and CreditCardService cardService = fields new FirstBankOfPSU("localhost", 8080);

Maven dependency: Logger logger = Logger.getLogger("edu.pdx.cs410J.Logger"); logger.setLevel(Level.INFO); com.google.code.guice guice CheckoutPanel panel = 2.0 new CheckoutPanel(inventory,cardService,logger); BookStoreGUI gui = new BookStoreGUI(panel); gui.pack(); gui.setVisible( true ); }

If we want to introduce another panel that is for inventory control, we’d have to pass the well-known BookInventory instance to that code, too.

19 20 Requesting Objects from Guice Guice Modules

Annotating one of a class’s constructors with @Inject A Guice “module” tells the Guice infrastructure about instructs Guice to invoke that constructor when an well-known objects instance is requested Your modules subclass Guice will provide the well-known instances of the • com.google.inject.AbstractModule • constructors arguments* Objects are “bound” into Guice using the bind method @Inject public CheckoutPanel(BookInventory inventory, Linking a type to its implementation (like a factory CreditCardService cardService, • method) Logger logger ) { bind(BookInventory.class).to(BookDatabase.class); If Guice is asked to create an instance of CheckoutPanel, Whenever an object depends on a BookInventory, it will invoke this constructor and provide an instance of Guice will provide it with an instance of BookDatabase BookInventory and CreditCardService By default, Guice will create a new instance of a bound Your code doesn’t need to create the dependencies class when it is requested • any more! Singleton objects in Guice can be configured in two ways @Inject can also be applied to methods and non-final Annotating the object’s class with @Singleton instance fields methods that are invoked by Guice • Binding the type in the singleton scope @Inject methods are invoked by Guice after the • • instance is created bind(CreditCardService.class) .to(FirstBankOfPSU.class) *Guice automatically injects a Logger whose name is the name of the .in(Singleton.class); class into which it is injected. And you can’t change it.

21 22

Annotations for Binding Annotations for Binding

The @Named annotation can be used to name well-known You can also create your own annotation types for objects well-known objects

From FirstBankOfPSU.java DataDirectory is an annotation for the directory in which application data is stored @Inject public FirstBankOfPSU( package edu.pdx.cs410J.di; @Named("ServerHost") String serverHost, @Named("ServerPort") int serverPort ) import com.google.inject.BindingAnnotation;

import java.lang.annotation.*; In your module, you can bind the values of the named objects: @BindingAnnotation @Retention(RetentionPolicy.RUNTIME) bind(String.class) @Target({ElementType.PARAMETER, ElementType.FIELD}) .annotatedWith(Names.named("ServerHost")) @Documented .toInstance("localhost"); public @interface DataDirectory { bind(Integer.class) .annotatedWith(Names.named("ServerPort")) } .toInstance( 8080 ); @BindingAnnotation means that the annotation being When Guice instantiates FirstBankOfPSU it will provide defined can be used with Guice the values of the server host and port.

23 24 Annotations for Binding Creating an Injector

You can annotate constructor parameters A Guice module is used to create an Injector which provides access to well-known objects @Inject public BookDatabase(@DataDirectory File directory) public class GuicyBookStoreApp extends BookStoreApp { And bind the value in your module: public static void main(String... args) { Injector injector = String tmpdir = Guice.createInjector(new BookStoreModule()); System.getProperty("java.io.tmpdir"); BookInventory inventory = File directory = new File(tmpdir); injector.getInstance(BookInventory.class); bind(File.class) addBooks(inventory); .annotatedWith(DataDirectory.class) .toInstance(directory); BookStoreGUI gui = injector.getInstance(BookStoreGUI.class); gui.pack(); gui.setVisible(true); } }

Guice takes care of creating and configuring all of the dependencies of BookStoreGUI and wiring them together

Ultimately, your code is more decoupled and can be reconfigured more easily

25 26

Provider Methods Provider classes

Sometimes, objects need more than a constructor call in A Provider class is used to create very expensive order to be initialized objects or objects that you want lazily initialize

Or the constructor needs dependencies that are not • public class EncyrptionKeyProvider provided by Guice implements Provider {

A module method that is annotated with @Provides private final EncryptionAlgorithm algorithm; constructs a Guice-managed instance of its return type @Inject This is probably a better way to configure the public EncyrptionKeyProvider(EncryptionAlgorithm algorithm) { DataDirectory File: this.algorithm = alrogithm; @Provides } @DataDirectory protected File provideDataDirectory() { @Override String tmpdir = public EncyrptionKey get() { System.getProperty("java.io.tmpdir"); // Perform expensive operation on demand return new File(tmpdir); return new EncryptionKey(algorithm); } } } It would probably be better to construct the FirstBankOfPSU using a provider method, also. Bind with: You wouldn’t need the @Named constructor • parameters that reused anywhere bind(EncyrptionKey.class) .toProvider(EncyrptionKeyProvider.class);

27 28 Provider classes Summary

Guice can inject a provider into an object Dependency Injection is the ultimate expression of “programming to the interface” @Singleton The “Hollywood Principle” states that objects should public class ConnectionManager { • @Inject receive their dependencies upon construction private Provider keyProvider; When dependencies are decoupled, finer-grained • public ConnectionManager() { testing is possible // Should inject Provider as parameter instead } Mockito provides facilities for mocking objects and • specifying mocked behavior public Connection createConnection() { return new Connection(keyProvider.get()); Google Guice manages dependencies by separating } • configuration code from application code }

Even though the ConnectionManager is a singleton, it can get multiple EncyrptionKeys by injecting the Provider*

*Okay, ConnectionManager is a factory and we could probably doing this Guicier, but you get the point

29 30