design patternshaase/lehre/patterns/... · constructor vs. setter injection constructor injection...

54
Oliver Haase Design Patterns 1 Dependency Injection

Upload: others

Post on 17-Aug-2020

2 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Design Patternshaase/lehre/patterns/... · Constructor vs. Setter Injection Constructor Injection ‣only valid and complete objects are created ‣better chances for immutability

Oliver Haase

Design Patterns

1

Dependency Injection

Page 2: Design Patternshaase/lehre/patterns/... · Constructor vs. Setter Injection Constructor Injection ‣only valid and complete objects are created ‣better chances for immutability

Motivation

2

A simple, motivating example (by Martin Fowler):public interface MovieFinder { /** * returns all movies of this finder’s source * @return all movies */ List<Movie> findAll();}

public class ColonDelimitedMovieFinder implements MovieFinder { private final String fileName;

public ColonDelimitedMovieFinder(String fileName) { this.fileName = fileName;

/** * returns all movies listed in file <code>fileName</code> */ @Override List<Movie> findAll() { … }}

Page 3: Design Patternshaase/lehre/patterns/... · Constructor vs. Setter Injection Constructor Injection ‣only valid and complete objects are created ‣better chances for immutability

Motivation

3

A simple, motivating example (by Martin Fowler):@ThreadSafe //assuming that ColonDelimitedMovieFinder is threadsafe public class MovieLister { private final MovieFinder finder;

public MovieLister() { finder = new ColonDelimitedMovieFinder(“movies.txt”); }

public List<Movie> moviesDirectedBy(String arg) { List<Movie> movies = finder.findAll(); for ( Iterator<Movie> it = movies.iterator(); it.hasNext(); ) { Movie movie = it.next(); if ( !movie.getDirector().equals(arg) ) { it.remove(); } } return movies; }}

Page 4: Design Patternshaase/lehre/patterns/... · Constructor vs. Setter Injection Constructor Injection ‣only valid and complete objects are created ‣better chances for immutability

ProblemHow to remove MovieLister’s dependency on ColonDelimitedMovieFinder?

4

Difference to DocManager example:

‣ MovieLister needs only one MovieFinder instance (service)

‣ DocManager must be able to create many Document objects at will

Page 5: Design Patternshaase/lehre/patterns/... · Constructor vs. Setter Injection Constructor Injection ‣only valid and complete objects are created ‣better chances for immutability

Idea

Have the dependency (service) be injected by the client

⇒ Inversion of Control

⇒ Hollywood Principle (“don’t call us, we’ll call you”)

5

Please note: DI is first and foremost a design pattern that can be implemented by hand. DI is, however, often equated with DI frameworks, e.g. Spring DI, Google Guice.

Page 6: Design Patternshaase/lehre/patterns/... · Constructor vs. Setter Injection Constructor Injection ‣only valid and complete objects are created ‣better chances for immutability

Types of Dependency Injection

There are 3 types of dependency injection:

1. constructor injection

2. setter injection

3. interface injection

6

Page 7: Design Patternshaase/lehre/patterns/... · Constructor vs. Setter Injection Constructor Injection ‣only valid and complete objects are created ‣better chances for immutability

Constructor Injection - Example

7

@ThreadSafe //assuming that finder instance is threadsafe public class MovieLister { private final MovieFinder finder;

public MovieLister(MovieFinder finder) { this.finder = finder; }

public List<Movie> moviesDirectedBy(String arg) { List<Movie> movies = finder.findAll(); for ( Iterator<Movie> it = movies.iterator(); it.hasNext(); ) { Movie movie = it.next(); if ( !movie.getDirector().equals(arg) ) { it.remove(); } } return movies; }}

Usage:MovieLister movieLister = new MovieLister(new ColonDelimitedMovieFinder(“movies.txt”)); List<Movie> movies = movieLister.moviesDirectedBy(“Quentin Tarantino”);

Page 8: Design Patternshaase/lehre/patterns/... · Constructor vs. Setter Injection Constructor Injection ‣only valid and complete objects are created ‣better chances for immutability

Setter Injection - Example

8

@ThreadSafe //assuming that finder instance is threadsafe public class MovieLister { private MovieFinder finder;

public MovieLister() {}

public void setFinder(MovieFinder finder) { this.finder = finder; }

public List<Movie> moviesDirectedBy(String arg) { List<Movie> movies = finder.findAll(); for ( Iterator<Movie> it = movies.iterator(); it.hasNext(); ) { Movie movie = it.next(); if ( !movie.getDirector().equals(arg) ) { it.remove(); } } return movies; }}

Usage:MovieLister movieLister = new MovieLister();movieLister.setFinder(new ColonDelimitedMovieFinder(“movies.txt”)); List<Movie> movies = movieLister.moviesDirectedBy(“Quentin Tarantino”);

Page 9: Design Patternshaase/lehre/patterns/... · Constructor vs. Setter Injection Constructor Injection ‣only valid and complete objects are created ‣better chances for immutability

Interface Injection - ExampleProvider of MovieFinder interface also defines injection interface, e.g. :

9

public interface MovieFinderInjector { void injectMovieFinder(MovieFinder finder);}

Each class that needs to get a MovieFinder injected has to implement injector interface:@ThreadSafe //assuming that finder instance is threadsafe public class MovieLister implements MovieFinderInjector { private MovieFinder finder;

public MovieLister() {}

@Override public void injectMovieFinder(MovieFinder finder) { this.finder = finder; }

public List<Movie> moviesDirectedBy(String arg) { … }}

Page 10: Design Patternshaase/lehre/patterns/... · Constructor vs. Setter Injection Constructor Injection ‣only valid and complete objects are created ‣better chances for immutability

Setter vs. Interface Injection

Only difference between setter and interface injection:

⇒ Whether interface provider defines companion injection

interface that implementing class must use for injection, or not.

10

Page 11: Design Patternshaase/lehre/patterns/... · Constructor vs. Setter Injection Constructor Injection ‣only valid and complete objects are created ‣better chances for immutability

Interface Injection - ExampleUsage:

11

MovieLister movieLister = new MovieLister();movieLister.injectMovieFinder(new ColonDelimitedMovieFinder(“movies.txt”)); List<Movie> movies = movieLister.moviesDirectedBy(“Quentin Tarantino”);

Page 12: Design Patternshaase/lehre/patterns/... · Constructor vs. Setter Injection Constructor Injection ‣only valid and complete objects are created ‣better chances for immutability

Dependency Injection Frameworks

‣ DI Frameworks separate out instantiation configuration, i.e. bindings from abstract interfaces to concrete types.

‣ Configuration usually either in XML or in Java with annotations

‣Wide-spread DI Frameworks: • Apache Spring DI

• Google Guice

12

Page 13: Design Patternshaase/lehre/patterns/... · Constructor vs. Setter Injection Constructor Injection ‣only valid and complete objects are created ‣better chances for immutability

Guice: Constructor Injection

13

@ThreadSafe //assuming that finder instance is threadsafe public class MovieLister { private final MovieFinder finder;

@Inject public MovieLister(MovieFinder finder) { this.finder = finder; }

public List<Movie> moviesDirectedBy(String arg) { List<Movie> movies = finder.findAll(); for ( Iterator<Movie> it = movies.iterator(); it.hasNext(); ) { Movie movie = it.next(); if ( !movie.getDirector().equals(arg) ) { it.remove(); } } return movies; }}

Page 14: Design Patternshaase/lehre/patterns/... · Constructor vs. Setter Injection Constructor Injection ‣only valid and complete objects are created ‣better chances for immutability

Guice: Constructor Injection

‣ @Inject annotation tells Guice to create and fill in appropriate MovieFinder instance when creating MovieLister instance.

‣Works only if bound type (e.g. ColonDelimitedMovieFinder)

• has zero-args non-private constructor, or

• uses itself constructor injection

14

Page 15: Design Patternshaase/lehre/patterns/... · Constructor vs. Setter Injection Constructor Injection ‣only valid and complete objects are created ‣better chances for immutability

Guice: Constructor Injection

15

public class ColonDelimitedMovieFinder implements MovieFinder { private final String fileName; @Inject public ColonDelimitedMovieFinder(@Named("FILE NAME") String fileName) { this.fileName = fileName; }

...}

@Named annotation needed for instance binding, .i.e. binding of a type to an instance of that type.

Page 16: Design Patternshaase/lehre/patterns/... · Constructor vs. Setter Injection Constructor Injection ‣only valid and complete objects are created ‣better chances for immutability

Guice: ModulesBindings are defined in modules, i.e. Java classes that inherit from com.google.inject.AbstractModule and whose configure method contains the bindings:

16

public class MovieListerModule extends AbstractModule { @Override protected void configure() { bind(MovieFinder.class).to(ColonDelimitedMovieFinder.class); bind(String.class).annotatedWith(Names.named("FILE NAME")) .toInstance("movies.txt"); }}

Page 17: Design Patternshaase/lehre/patterns/... · Constructor vs. Setter Injection Constructor Injection ‣only valid and complete objects are created ‣better chances for immutability

Guice: InstantiationInstances are created by

‣creating a Guice injector that uses a previously defined module

‣having the injector create the application object(s)

17

Injector injector = Guice.createInjector(new MovieListerModule());MovieLister lister = injector.getInstance(MovieLister.class);

Page 18: Design Patternshaase/lehre/patterns/... · Constructor vs. Setter Injection Constructor Injection ‣only valid and complete objects are created ‣better chances for immutability

Guice: Setter Injection

18

@ThreadSafe //assuming that finder instance is threadsafe public class MovieLister { private MovieFinder finder; @Inject public void setFinder(MovieFinder finder) { this.finder = finder; }

public List<Movie> moviesDirectedBy(String arg) { List<Movie> movies = finder.findAll(); for ( Iterator<Movie> it = movies.iterator(); it.hasNext(); ) { Movie movie = it.next(); if ( !movie.getDirector().equals(arg) ) { it.remove(); } } return movies; }}

Page 19: Design Patternshaase/lehre/patterns/... · Constructor vs. Setter Injection Constructor Injection ‣only valid and complete objects are created ‣better chances for immutability

Guice: Setter Injection

19

public class ColonDelimitedMovieFinder implements MovieFinder { private String fileName; @Inject public void setFileName(@Named("FILE NAME") String fileName) { this.fileName = fileName; }

@Override public List<Movie> findAll() { ... }}

MovieListerModule remains the same as before, because mappings also remain the same.

Page 20: Design Patternshaase/lehre/patterns/... · Constructor vs. Setter Injection Constructor Injection ‣only valid and complete objects are created ‣better chances for immutability

Guice: Setter Injection

20

Object instantiation can also remain the same

⇒ Guice automatically calls setter methods to inject

necessary dependencies.Injector injector = Guice.createInjector(new MovieListerModule());MovieLister lister = injector.getInstance(MovieLister.class);

Or, objects can be instantiated as usually, and then Guice can fill in dependencies using setter methods: Injector injector = Guice.createInjector(new MovieListerModule());MovieLister lister = new MovieLister();injector.injectMembers(lister);

Page 21: Design Patternshaase/lehre/patterns/... · Constructor vs. Setter Injection Constructor Injection ‣only valid and complete objects are created ‣better chances for immutability

Constructor vs. Setter InjectionConstructor Injection

‣ only valid and complete objects are created

‣ better chances for immutabilitySetter Injection

‣ can lead to unnecessarily mutable objects

‣ injection through easy-to-read methods

‣ necessary if dependencies are not available at creation time, e.g. cyclic dependencies

21

Recommendation: Use setter injection only if necessary.

Page 22: Design Patternshaase/lehre/patterns/... · Constructor vs. Setter Injection Constructor Injection ‣only valid and complete objects are created ‣better chances for immutability

DocManager ReloadedHow to apply DI pattern to DocManager example?

22

⇒ Inject concrete DocumentFactory as a service into

DocManager

So ...

… if client knows when to create objects, but doesn’t know (neither care) how, then ...

… inject client with factory that can be used to get instances as needed.

Page 23: Design Patternshaase/lehre/patterns/... · Constructor vs. Setter Injection Constructor Injection ‣only valid and complete objects are created ‣better chances for immutability

DocManager Reloaded

23

@ThreadSafe // assuming that concrete Document is threadsafepublic class DocManager { private final Collection<Document> docs; private final DocumentFactory docFactory; @Inject public DocManager(DocumentFactory docFactory) { this.docFactory = docFactory; docs = new ConcurrentLinkedQueue<Document>(); }

public void createDoc() { Document doc = docFactory.newDocument(); docs.add(doc); doc.open(); } public void openDocs() { for ( Document doc : docs ) doc.open(); } }

Page 24: Design Patternshaase/lehre/patterns/... · Constructor vs. Setter Injection Constructor Injection ‣only valid and complete objects are created ‣better chances for immutability

DocManager Reloaded

24

public class DocManagerModule extends AbstractModule { @Override protected void configure() { bind(DocumentFactory.class).to(LatexDocFactory.class); }}

Injector injector = Guice.createInjector(new DocManagerModule());DocManager docManager = injector.getInstance(DocManager.class);

Usage:

Sample Bindings:

Page 25: Design Patternshaase/lehre/patterns/... · Constructor vs. Setter Injection Constructor Injection ‣only valid and complete objects are created ‣better chances for immutability

You Want More?

Read more about DI in Martin Fowler’s seminal online article http://martinfowler.com/articles/injection.html

25

Page 26: Design Patternshaase/lehre/patterns/... · Constructor vs. Setter Injection Constructor Injection ‣only valid and complete objects are created ‣better chances for immutability

Service Locator

26

Page 27: Design Patternshaase/lehre/patterns/... · Constructor vs. Setter Injection Constructor Injection ‣only valid and complete objects are created ‣better chances for immutability

Motivation

27

Back to Martin Fowler’s DI example:@ThreadSafe //assuming that ColonDelimitedMovieFinder is threadsafe public class MovieLister { private final MovieFinder finder;

public MovieLister() { finder = new ColonDelimitedMovieFinder(“movies.txt”); }

public List<Movie> moviesDirectedBy(String arg) { List<Movie> movies = finder.findAll(); for ( Iterator<Movie> it = movies.iterator(); it.hasNext(); ) { Movie movie = it.next(); if ( !movie.getDirector().equals(arg) ) { it.remove(); } } return movies; }}

Page 28: Design Patternshaase/lehre/patterns/... · Constructor vs. Setter Injection Constructor Injection ‣only valid and complete objects are created ‣better chances for immutability

Problem & IdeaHow to remove MovieLister’s dependency on ColonDelimitedMovieFinder?

28

⇒ Pass a service locator into MovieLister that can be queried

for all kinds of services.

Page 29: Design Patternshaase/lehre/patterns/... · Constructor vs. Setter Injection Constructor Injection ‣only valid and complete objects are created ‣better chances for immutability

Example

29

public interface ServiceLocator { MovieFinder getMovieFinder(String fileName); ... }

@ThreadSafe //assuming that ColonDelimitedMovieFinder is threadsafe public class MovieLister { private final MovieFinder finder; private final ServiceLocator serviceLocator;

public MovieLister(ServiceLocator serviceLocator) { finder = serviceLocator.getMovieFinder(“movies.txt”); }

public List<Movie> moviesDirectedBy(String arg) { ... }}

Page 30: Design Patternshaase/lehre/patterns/... · Constructor vs. Setter Injection Constructor Injection ‣only valid and complete objects are created ‣better chances for immutability

But...

… (a) there’s now a dependency on the service locator...

⇒ yes, but only on one object for all services.

30

…(b) how does service locator get into MovieLister?

⇒ e.g. with dependency injection.

Page 31: Design Patternshaase/lehre/patterns/... · Constructor vs. Setter Injection Constructor Injection ‣only valid and complete objects are created ‣better chances for immutability

Example

31

@ThreadSafe //assuming that ColonDelimitedMovieFinder is threadsafe public class MovieLister { private final MovieFinder finder; private final ServiceLocator serviceLocator;

@Inject public MovieLister(ServiceLocator serviceLocator) { finder = serviceLocator.getMovieFinder(“movies.txt”); }

public List<Movie> moviesDirectedBy(String arg) { ... }}

Using Guice dependency injection:

Page 32: Design Patternshaase/lehre/patterns/... · Constructor vs. Setter Injection Constructor Injection ‣only valid and complete objects are created ‣better chances for immutability

Service Locator vs. Abstract Factory

‣ both can create objects of different types (services vs. products)

‣ product types belong to a product family, services unrelated with each other

‣ abstract factory creates many instances of a product type, service locator only one instance per service type

32

Page 33: Design Patternshaase/lehre/patterns/... · Constructor vs. Setter Injection Constructor Injection ‣only valid and complete objects are created ‣better chances for immutability

Builder

33

Page 34: Design Patternshaase/lehre/patterns/... · Constructor vs. Setter Injection Constructor Injection ‣only valid and complete objects are created ‣better chances for immutability

Motivating Example (J. Bloch)

34

Suppose a class NutritionFacts that describes food items. A few specifications are mandatory, many are optional:

@Immutablepublic class NutritionFacts { private final int servingSize; // (ml) - mandatory private final int servings; // (per container) - mandatory private final int calories; // - optional private final int fat; // (g) - optional private final int sodium; // (mg) - optional private final int carbs; // (g) - optional ...}

Page 35: Design Patternshaase/lehre/patterns/... · Constructor vs. Setter Injection Constructor Injection ‣only valid and complete objects are created ‣better chances for immutability

Motivating Example (J. Bloch)

35

Question: How to create instances of NutritionFacts?

Option 1: telescoping constructors

Page 36: Design Patternshaase/lehre/patterns/... · Constructor vs. Setter Injection Constructor Injection ‣only valid and complete objects are created ‣better chances for immutability

Option 1 - Telescoping C’tors

36

@Immutablepublic class NutritionFacts { private final int servingSize; // (ml) - mandatory private final int servings; // (per container) - mandatory private final int calories; // - optional private final int fat; // (g) - optional private final int sodium; // (mg) - optional private final int carbs; // (g) - optional public NutritionFacts(int servingSize, int servings) { this(servingSize, servings, 0); }

public NutritionFacts(int servingSize, int servings, int calories) { this(servingSize, servings, calories, 0); }

public NutritionFacts(int servingSize, int servings, int calories, int fat) { this(servingSize, servings, calories, fat, 0); }

Page 37: Design Patternshaase/lehre/patterns/... · Constructor vs. Setter Injection Constructor Injection ‣only valid and complete objects are created ‣better chances for immutability

Option 1 - Telescoping C’tors

37

public NutritionFacts(int servingSize, int servings, int calories, int fat, int sodium) { this(servingSize, servings, calories, fat, sodium, 0); }

public NutritionFacts(int servingSize, int servings, int calories, int fat, int sodium, int carbs) { this.servingSize = servingSize; this.servings = servings; this.calories = calories; this.fat = fat; this.sodium = sodium; this.carbs = carbs; }}

NutritionFacts cocaCola = new NutritionFacts(240, 8, 100, 0, 35, 27);

Sample usage:

Page 38: Design Patternshaase/lehre/patterns/... · Constructor vs. Setter Injection Constructor Injection ‣only valid and complete objects are created ‣better chances for immutability

Second Try...

38

Question: How to create instances of NutritionFacts?

Option 2: JavaBeans Pattern

Page 39: Design Patternshaase/lehre/patterns/... · Constructor vs. Setter Injection Constructor Injection ‣only valid and complete objects are created ‣better chances for immutability

Option 2 - JavaBeans Pattern

39

public class NutritionFacts { private int servingSize = -1; // (ml) - mandatory private int servings = -1; // (per container) - mandatory private int calories = 0; // - optional private int fat = 0; // (g) - optional private int sodium = 0; // (mg) - optional private int carbs = 0; // (g) - optional public NutritionFacts() {}

public void setServingSize(int servingSize) { this.servingSize = servingSize; }

public void setServings(int servings) { this.servings = servings; }

public void setCalories(int calories) { this.calories = calories; }

Page 40: Design Patternshaase/lehre/patterns/... · Constructor vs. Setter Injection Constructor Injection ‣only valid and complete objects are created ‣better chances for immutability

Option 2 - JavaBeans Pattern

40

public void setFat(int fat) { this.fat = fat; }

public void setSodium(int sodium) { this.sodium = sodium; }

public void setCarbs(int carbs) { carbs = carbs; }}

NutritionFacts cocaCola = new NutritionFacts();cocaCola.setServingSize(240);cocaCola.setServings(8);cocaCola.setCalories(100);cocaCola.setSodium(35);cocaCola.setCarbs(27);

Sample usage:

Page 41: Design Patternshaase/lehre/patterns/... · Constructor vs. Setter Injection Constructor Injection ‣only valid and complete objects are created ‣better chances for immutability

Third Try...

41

Question: How to create instances of NutritionFacts?

Option 3: constructor/setter combination

Page 42: Design Patternshaase/lehre/patterns/... · Constructor vs. Setter Injection Constructor Injection ‣only valid and complete objects are created ‣better chances for immutability

Option 3 - C’tor & Setters

42

public class NutritionFacts { private final int servingSize; // (ml) - mandatory private final int servings; // (per container) - mandatory private int calories = 0; // - optional private int fat = 0; // (g) - optional private int sodium = 0; // (mg) - optional private int carbs = 0; // (g) - optional public NutritionFacts(int servingSize, int servings) { this.servingSize = servingSize; this.servings = servings; }

public void setCalories(int calories) { this.calories = calories; }

public void setFat(int fat) { this.fat = fat; }

Page 43: Design Patternshaase/lehre/patterns/... · Constructor vs. Setter Injection Constructor Injection ‣only valid and complete objects are created ‣better chances for immutability

Option 3 - C’tor & Setters

43

public void setSodium(int sodium) { this.sodium = sodium; }

public void setCarbs(int carbs) { carbs = carbs; }}

NutritionFacts cocaCola = new NutritionFacts(240, 8);cocaCola.setCalories(100);cocaCola.setSodium(35);cocaCola.setCarbs(27);

Sample usage:

Page 44: Design Patternshaase/lehre/patterns/... · Constructor vs. Setter Injection Constructor Injection ‣only valid and complete objects are created ‣better chances for immutability

ComparisonOption 1

‣ only valid and complete objects are created

‣ preserves immutability

‣ hard to read

44

Option 2

‣ creation of incomplete, invalid objects

‣ loss of immutability

‣ easy to read

Page 45: Design Patternshaase/lehre/patterns/... · Constructor vs. Setter Injection Constructor Injection ‣only valid and complete objects are created ‣better chances for immutability

ComparisonOption 3

‣ only valid and complete objects are created

‣ easy to read

‣ loss of immutability

45

There is another option that combines the best of all three options!

Page 46: Design Patternshaase/lehre/patterns/... · Constructor vs. Setter Injection Constructor Injection ‣only valid and complete objects are created ‣better chances for immutability

Option 4 - Builder

46

Idea:

Define a builder that can

‣ be fed with a combination of NutritionFacts values, and

‣ then be used to create a NutritionFacts instance.

Option 3 — Builder

Idea:

Define a builder that can

be fed with a combination of NutritionFacts values, and

then be used to create a NutritionFacts instance.

Client

c'tor(NutritionFactsBuilder)NutritionFacts

c'tor(servingSize, servings)setCalories(int calories)setFat(int Fat)setSodium(int sodium)setCarbs(int carbs)build()

NutritionFactsBuilder

return new NutritionFacts(self);

Oliver Haase (HTWG Konstanz) Design Patterns 10 / 16

Option 3 — Builder

pub l i c c l a s s Nu t r i t i o n F a c t s {p r i v a t e f i n a l i n t s e r v i n g S i z e ; // (ml ) � mandatory

p r i v a t e f i n a l i n t s e r v i n g s ; // ( pe r c o n t a i n e r ) � mandatory

p r i v a t e f i n a l i n t c a l o r i e s ; // � o p t i o n a l

p r i v a t e f i n a l i n t f a t ; // ( g ) � o p t i o n a l

p r i v a t e f i n a l i n t sodium ; // (mg) � o p t i o n a l

p r i v a t e f i n a l i n t c a r b s ; // ( g ) � o p t i o n a l

pub l i c s t a t i c c l a s s Bu i l d e r {p r i v a t e f i n a l i n t s e r v i n g S i z e ;p r i v a t e f i n a l i n t s e r v i n g s ;

// o p t i o n a l params i n i t i a l i z e d to d e f a u l t v a l u e s

p r i v a t e i n t c a l o r i e s = 0 ;p r i v a t e i n t f a t = 0 ;p r i v a t e i n t sodium = 0 ;p r i v a t e i n t c a r b s = 0 ;

pub l i c Bu i l d e r ( i n t s e r v i n g S i z e , i n t s e r v i n g s ) {t h i s . s e r v i n g S i z e = s e r v i n g S i z e ;t h i s . s e r v i n g s = s e r v i n g s ;

}

Oliver Haase (HTWG Konstanz) Design Patterns 11 / 16

Page 47: Design Patternshaase/lehre/patterns/... · Constructor vs. Setter Injection Constructor Injection ‣only valid and complete objects are created ‣better chances for immutability

Option 4 - Builder

47

@Immutablepublic class NutritionFacts { private final int servingSize; // (ml) - mandatory private final int servings; // (per container) - mandatory private final int calories; // - optional private final int fat; // (g) - optional private final int sodium; // (mg) - optional private final int carbs; // (g) - optional public static class Builder { private final int servingSize; private final int servings; // optional params initialized to default values private int calories = 0; private int fat = 0; private int sodium = 0; private int carbs = 0; public Builder(int servingSize, int servings) { this.servingSize = servingSize; this.servings = servings; }

Page 48: Design Patternshaase/lehre/patterns/... · Constructor vs. Setter Injection Constructor Injection ‣only valid and complete objects are created ‣better chances for immutability

Option 4 - Builder

48

public void setCalories(int calories) { this.calories = calories; }

public void setFat(int fat) { this.fat = fat; } public void setSodium(int sodium) { this.sodium = sodium; } public void setCarbs(int carbs) { this.carbs = carbs; } public NutritionFacts build() { return new NutritionFacts(this); } }

Page 49: Design Patternshaase/lehre/patterns/... · Constructor vs. Setter Injection Constructor Injection ‣only valid and complete objects are created ‣better chances for immutability

Option 4 - Builder

49

private NutritionFacts(Builder builder) { servingSize = builder.servingSize; servings = builder.servings; calories = builder.calories; fat = builder.fat; sodium = builder.sodium; carbs = builder.carbs; } }

NutritionFacts.Builder cocaColaBuilder = new NutritionFacts.Builder(240, 8);cocaColaBuilder.setCalories(100); cocaColaBuilder.setSodium(35);cocaColaBuilder.setCarbs(27); NutritionFacts cocaCola = cocaColaBuilder.build();

Sample Usage:

Page 50: Design Patternshaase/lehre/patterns/... · Constructor vs. Setter Injection Constructor Injection ‣only valid and complete objects are created ‣better chances for immutability

Builder - Structure

50

Builder — Structure

Client

c'tor(Builder)use()

Product

c'tor(<mandatory params>)setOptionalParam1()setOptionalParam2()build()

Builder

return new Product(self);

Oliver Haase (HTWG Konstanz) Design Patterns 14 / 16

Builder — Participants

Builder:provides c’tor with all mandatory params

initializes optional param to default values

provides set operation for each optional parameter

provides built operation that calls Product c’tor, passes in referenceto itself

Product: provides c’tor that expects a Builder instance and copiesall values into itself

Client:creates Builder instance, thereby passes in all mandatory params

uses set operations to set optional params

calls build operation to have Product instance created

Oliver Haase (HTWG Konstanz) Design Patterns 15 / 16

‣provides c’tor with all mandatory params‣initializes optional params to default values‣provides set operation for each optional param‣provides build operation that calls Product’s c’tor, passes inreference to itself

provides c'tor that expects a Builder instance and copies all values into itself

‣creates Builder instance, thereby passes in all mandatory params ‣uses set operations to set optional params‣calls build operation to have Product instance created

Page 51: Design Patternshaase/lehre/patterns/... · Constructor vs. Setter Injection Constructor Injection ‣only valid and complete objects are created ‣better chances for immutability

Pros & Cons

51

‣ Pro:• creates only valid products

• easy to read

• preserves immutability

• configured builder can be used to create more than one product

⇒ builder object = abstract factory

‣ Con:• more verbose implementation

• more verbose usage than telescoping c'tors

• additional object needed to create product

⇒ additional runtime and memory cost

Page 52: Design Patternshaase/lehre/patterns/... · Constructor vs. Setter Injection Constructor Injection ‣only valid and complete objects are created ‣better chances for immutability

Creational Patterns Discussion

52

Page 53: Design Patternshaase/lehre/patterns/... · Constructor vs. Setter Injection Constructor Injection ‣only valid and complete objects are created ‣better chances for immutability

Overview

53

Prototype

DependencyInjection

Factory Method

Abstract Factory

ServiceLocator

Builder

Singleton

Page 54: Design Patternshaase/lehre/patterns/... · Constructor vs. Setter Injection Constructor Injection ‣only valid and complete objects are created ‣better chances for immutability

Similarities & Commonalities

54

Prototype

DependencyInjection

Factory Method

Abstract Factory

ServiceLocator

Builder

Singleton

: patterns that use a dedicated object to create new objects

A B : A can be used to feed B into client code