• Keine Ergebnisse gefunden

Design Patterns

N/A
N/A
Protected

Academic year: 2022

Aktie "Design Patterns"

Copied!
54
0
0

Wird geladen.... (Jetzt Volltext ansehen)

Volltext

(1)

Oliver Haase

Design Patterns

Dependency Injection

(2)

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() { …

} }

(3)

Motivation

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();

}

(4)

Problem

How 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

(5)

Idea

Have the dependency (service) be injected by the client Inversion of Control

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

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.

(6)

Types of Dependency Injection

There are 3 types of dependency injection:

1. constructor injection 2. setter injection

3. interface injection

6

(7)

Constructor Injection - Example

@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;

} }

(8)

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”);

(9)

Interface Injection - Example

Provider of MovieFinder interface also defines injection interface, e.g. :

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) {

(10)

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

(11)

Interface Injection - Example

Usage:

MovieLister movieLister = new MovieLister();

movieLister.injectMovieFinder(new ColonDelimitedMovieFinder(“movies.txt”));

List<Movie> movies =

movieLister.moviesDirectedBy(“Quentin Tarantino”);

(12)

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

(13)

Guice: Constructor Injection

@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;

(14)

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

(15)

Guice: Constructor Injection

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.

(16)

Guice: Modules

Bindings 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");

} }

(17)

Guice: Instantiation

Instances are created by

‣ creating a Guice injector that uses a previously defined module

‣ having the injector create the application object(s)

Injector injector = Guice.createInjector(new MovieListerModule());

MovieLister lister = injector.getInstance(MovieLister.class);

(18)

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;

} }

(19)

Guice: Setter Injection

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.

(20)

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);

(21)

Constructor vs. Setter Injection

Constructor Injection

‣ only valid and complete objects are created

‣ better chances for immutability Setter 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

(22)

DocManager Reloaded

How 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.

(23)

DocManager Reloaded

@ThreadSafe // assuming that concrete Document is threadsafe public 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() {

(24)

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:

(25)

You Want More?

Read more about DI in Martin Fowler’s seminal online article

http://martinfowler.com/articles/injection.html

(26)

Service Locator

26

(27)

Motivation

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();

}

(28)

Problem & Idea

How to remove MovieLister ’s dependency on

ColonDelimitedMovieFinder ?

28

Pass a service locator into MovieLister that can be queried

for all kinds of services.

(29)

Example

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) { ...

(30)

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.

(31)

Example

@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:

(32)

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

(33)

Builder

(34)

Motivating Example (J. Bloch)

34

Suppose a class NutritionFacts that describes food items.

A few specifications are mandatory, many are optional:

@Immutable

public 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

...

}

(35)

Motivating Example (J. Bloch)

Question: How to create instances of NutritionFacts ?

Option 1: telescoping constructors

(36)

Option 1 - Telescoping C’tors

36

@Immutable

public 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);

}

(37)

Option 1 - Telescoping C’tors

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;

} }

Sample usage:

(38)

Second Try...

38

Question: How to create instances of NutritionFacts ?

Option 2: JavaBeans Pattern

(39)

Option 2 - JavaBeans Pattern

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;

}

(40)

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:

(41)

Third Try...

Question: How to create instances of NutritionFacts ?

Option 3: constructor/setter combination

(42)

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;

}

(43)

Option 3 - C’tor & Setters

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

}

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

} }

Sample usage:

(44)

Comparison

Option 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

(45)

Comparison

Option 3

‣ only valid and complete objects are created

‣ easy to read

‣ loss of immutability

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

(46)

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

p u b l i c c l a s s N u 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 ) ma nda to ry p r i v a t e f i n a l i n t s e r v i n g s ; // ( p e r c o n t a i n e r ) ma nda to ry

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

p u b l i c s t a t i c c l a s s B u 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 t o 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 ;

p u b l i c B u 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

(47)

Option 4 - Builder

@Immutable

public 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;

(48)

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);

} }

(49)

Option 4 - Builder

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);

Sample Usage:

(50)

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 reference to itself

Product: provides c’tor that expects a Builder instance and copies all 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 in reference 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

(51)

Pros & Cons

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

(52)

Creational Patterns

Discussion

52

(53)

Overview

Prototype

Dependency Injection Abstract Factory

Service Locator

Builder

(54)

Similarities & Commonalities

54

Prototype

Dependency Injection

Factory Method Abstract Factory

Service Locator

Builder

Singleton

: patterns that use a dedicated object to create new objects A B : A can be used to feed B into client code

Referenzen

ÄHNLICHE DOKUMENTE

Private governance is currently being evoked as a viable solution to many public policy goals. However, in some circumstances it has shown to produce more harm than

Hvis du ønsker, at printeren automatisk skal vælge en papirkilde med det rette papirformat, skal du vælge Auto Selection (Automatisk valg) i printerdriveren eller Auto

Hvis du starter Print Center (Print Center) for første gang, eller der endnu ikke er føjet en printer til Print Center (Print Center), vises følgende skærm:.. Klik på Add (Tilføj),

Women, on the other hand, have been seen to take care of the household (domus) and were therefore more likely to be in the private sphere 6. According to this understanding,

Hvis du bruger en Windows 2000- eller NT 4.0-printerserver, skal du vælge knappen Shared as (Delt som) og derefter skrive navnet i boksen Share Name (Sharenavn).

Wird diese Methode nur innerhalb der Klasse verwendet, sollte diese auch anstatt public als private definiert

Word Finder is an electronic thesaurus for the IBM PC, XT, AT, and compatibles. This special edition of the product has been designed for use with WordStar. This

Liegt keine Zustimmungserklärung für Fax / E-Mail vor, wird die Befundmitteilung per Post versandt. Die Zustimmungserklärung kann über www.Verbraucherschutz.Sachsen-Anhalt.de