2. 2 of 30
Agenda
Program units coupling
Why one should avoid tight coupling
Problems of coupling reduction
Coupling reduction example
Dependency injection (DI): definition, components, injection type
Inversion of control (IoC)
IoC + DI
Dependency: lifecycle, scope, resolution
Reference implementations
Benefits and disadvantages
3. 3 of 30
Program units coupling
Coupling is a measure of the independence of components
Tight (high, strong)
Module A
Module B
Loose (low, weak)
Module A
Module B
None
Module A
Module B
Module C
Module D
Module C
Module D
Module C
Module D
4. 4 of 30
Program units coupling: types
Content: A modifies B, B is completely dependent on A
(e. g. A overrides implementation of method in B)
Common: A and B share the same common data
(e. g. A and B share the same global variable)
Control: A passes parameters to control the flow of B
Stamp: A and B share the same Value-Object type
Data: Only data are passed between A and B
Message: Defined message types are sent between A and B
None: A and B do not interact in any way
5. 5 of 30
Why one should avoid tight coupling
A change in one module forces
a ripple effect of changes
in other modules
A particular module is harder
to reuse and test,
because dependent modules
must be included
6. 6 of 30
Warning
Do NOT try to reduce coupling between every pair of units
just for the sake of loyalty, if it doesn’t feel right
Level of coupling is a matter of design, it is neither good or bad
For example:
• Module A doesn’t make any sense without module B
• A and B work as a whole atomic unit
(e.g. pipeline processing)
• The application is made to work with an exact
third-party Service, API or Hardware
8. 8 of 30
Problems of coupling reduction
Components independence: modules make no assumptions
about what other systems do, but rely on their contracts
Abstraction: use interface to define a type
and focus on what is important about the component
Instantiation: concrete implementation of the interface
has to be instantiated (creational patterns like Abstract Factory)
Components linking: provide a wiring layer
between components A and B (Service Locator)
9. 9 of 30
Example: tight coupling
public class A {
B b = new B();
C c = new C();
public void doA() {
b.doB();
c.doC();
}
}
10. 10 of 30
Example: abstraction (use interfaces)
public class A {
IB b = new B();
IC c = new C();
public void doA() {
b.doB();
c.doC();
}
}
11. 11 of 30
Example: instantiation (creational
patterns)
public class A {
IB b = FactoryB.createIB();
IC c = FactoryC.createIC();
public void doA() {
b.doB();
c.doC();
}
}
12. 12 of 30
Example: components linking
public class A {
IB b; IC c;
public A(IB b, IC c) {
this.b = b;
this.c = c;
}
public void doA() {
b.doB();
c.doC();
}
}
13. 13 of 30
Example: components linking
public class Starter {
A a; IB b; IC c;
public void startUp() {
b = FactoryB.createIB();
c = FactoryC.createIC();
a = new A(b, c);
}
public A getA() {
return a;
}
}
15. 15 of 30
Dependency injection (DI)
So we noticed that decoupled modules
need to be linked for the interaction
16. 16 of 30
DI: definition
Dependency Injection (DI) is a design pattern
aimed to eliminate the hard-coded dependencies
between program modules and make it possible
to change them at compile-time and/or run-time
17. 17 of 30
DI: components
Dependency: a module that other modules depend on
Consumer (Client): a dependent module
Injector (Container): a component that retrieves/creates
dependencies and wires them to the consumers
Resolver: a component that chooses a concrete implementation
for the abstract type requested
19. 19 of 30
DI: injection types
Constructor: dependency is wired during the consumer is being
created
Setter: setter method is used to set/change the dependency
Interface: dependency provides an interface that must be
implemented by any dependent client
20. 20 of 30
Inversion of control (IoC)
“Don’t call us, we’ll call you”
Methods defined by the user to tailor the framework will often be
called from within the framework itself
Framework plays the role of main application and user code
defines action to perform in response to application activity
21. 21 of 30
IoC + DI
IoC framework
Application
DI Container
Configuration
Metadata
Modules
Application
Configuration
Localized texts,
etc.
22. 22 of 30
IoC + DI: configuration metadata
External: XML or other external metadata format
<bean class="com.example.MyComponent"/>
Annotation-based: byte-code level metadata description
@Component
public class MyComponent { … }
Code-based: custom code that declares dependencies
@Configuration
public class AppConfig {
@Bean public MyComponent myComponent () {
return new MyComponent();
}
}
23. 23 of 30
Dependency: lifecycle
Creation: dependencies may be created calling the default
constructor (default) or factory method
•
•
•
•
•
Callbacks: container provides a set of callbacks called on events:
Dependency initialization (pre and post)
Dependency destruction (pre and post)
Container startup
Container shutdown
etc.
24. 24 of 30
Dependency: scopes
Singleton (container): a single instance for the whole container
Prototype: a single definition for any amount of instances
Thread: a single instance per thread (thread scope singleton)
Custom: define your own scope
Web aware containers may provide:
Request
Session
25. 25 of 30
Dependency: resolution
Explicit reference wiring configuration
Autowiring
• Type: the dependency is resolved based on class type
• Name: based on unique component name
• Collections of types
26. 26 of 30
Example: using DI
@Component
public class A {
IB b; IC c;
@Autowired
public A(IB b, IC c) {
this.b = b;
this.c = c;
}
}
27. 27 of 30
Reference implementations
Java
• J2EE 6+ compatible containers (Glassfish, etc.)
• Spring – a part of the large versatile framework
• Google Guice – lightweight, works for android
.NET
• Spring.NET
29. 29 of 30
Disadvantages
Runtime linking cause runtime errors instead of compiletime
Longer application startup
•
•
•
•
Dependency hell:
Circular dependencies
Conflicting dependencies
Long chains of dependencies
A lot of dependencies for a simple functionality