This week I'll tell you about inversion of control. This is very useful thing if you are going to develop dynamic application in compliance with modern approach to software development.
Spring framework gives you flexibility providing dynamical beans
wiring, but for deeper understanding let's consider principle of
inversion of control beyond Spring. Look at this code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class Calculator {
private final DbWriter dbWriter;
public Calculator() {
// Class Calculator can't be used in DbWriter absence.
this.dbWriter = new DbWriter();
}
// Some methods in Calculator class
public final Integer opAdd(Integer a, Integer b) {
return a + b;
}
public final boolean saveResult(Integer number){
return dbWriter.saveInDb(number);
}
}
public class DbWriter {
public DbWriter(){
// code for database initialization
}
public boolean saveInDb(Integer value ){
// save value to db
// if save operation is successful return true
return true;
}
}
This is an example of wrong style in designing programs, because of
program will lose its flexibility. Class Calculator can’t be used
independently of DBSaver class. So, what will you do if you have to
change code in a short time?
Thus we have to separate classes into two (or much more in real projects) independent logical blocks called beans. Let’s to create a new interface contains methods concerned with both classes.
In a such a way we destroy dependencies between two classes and obtain
possibility to choose right way to save data to db in our case.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
public interface IResultSaver {
public boolean saveResult(Integer result);
}
public class Calculator {
private IResultSaver saver;
public Calculator() {
}
// Some methods in Calculator class
public final Integer opAdd(Integer a, Integer b) {
return a + b;
}
//Here you can put any instance of classes implement IResultSaver
public final void setSaver(IResultSaver saver) {
// Choose saver
this.saver = saver;
}
public final boolean saveResult(Integer resultToSave){
rerurn saver.saveResult(resultToSave)
}
}
// DB saver.
public class DbWriter implements IResultSaver {
public DbWriter() {
// code for database initialization
initializeDbConnection();
}
@Override
public boolean saveResult(Integer result) {
return saveDbOperation(result);
}
}
public class FileWriter implements IResultSaver {
public FileWriter() {
// code for database initialization
initializeFile();
}
@Override
public boolean saveResult(Integer result) {
return saveFileOperation(result);
}
}
Calculator class can use any classes via interface, so we can use them
in elegant manner. So everything is clear, and we can apply this
principle
for calculation operations like in last article. This is a nice example
of inversion of control (IoS). Spring framework provides this trick
using containers for
creation some class instances and dependences between them in XML file.
Take a look at this code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
public interface IOperation {
public Integer operation(final Integer a, final Integer b);
}
public interface IResultSaver {
public boolean saveResult(Integer result);
}
public class Calculator {
private IResultSaver saver;
private IOperation operation;
// Spring container automaticaly sets
// beans corespond to XML conficuraion file via setters
public void setSaver(IResultSaver saver) {
this.saver = saver;
}
public void setOperation(AddBean operation) {
this.operation = operation;
}
public boolean saveResult(final Integer result) {
return saver.saveResult(result);
}
public final Integer calculate(final Integer a, final Integer b) {
return operation.operation(a, b);
}
}
// DB saver.
public class DbWriter implements IResultSaver {
public DbWriter(final String dataSourceName) {
// code for database initialization
System.out.println("Here you can initialize you db connection!"
+ dataSourceName);
}
public boolean saveResult(final Integer result) {
// code for saving result
System.out.println("This is Db saver!");
return true;
}
}
// File saver
public class FileWriter implements IResultSaver {
public FileWriter(final String filename) {
// code for database initialization
System.out.println("Initializing file writer with parameter:file name" + filename);
}
public boolean saveResult(final Integer result) {
//code for saving result
System.out.println("This is file saver!");
return true;
}
}
// Bean for add
public class AddBean implements IOperation{
public Integer operation(final Integer a, final Integer b) {
return a + b;
}
}
// Bean for multiply
public class MultiplyBean implements IOperation {
public Integer operation(final Integer a,final Integer b) {
return (Integer) a * b;
}
}
public class main {
public static void main(String args[]) {
BeanFactory factory = new XmlBeanFactory(new ClassPathResource("spring-config.xml"));
Calculator calculator = (Calculator) factory.getBean("calculator");
calculator.saveResult(calculator.calculate(2, 2));
}
}
This is pretty sample of Inversion of control using Spring framework.
Spring container hides creation of instances of correspond beans, and we
obtain dynamic application.
Configuration information for container contains XML file and you can
change during execution of your application.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://springframework.org/schema/beans
http://springframework.org/schema/beans/spring-beans.xsd">
<!--IOperation beans-->
<bean name="add" class="InversioOfControl.AddBean"/>
<bean name="mul" class="InversioOfControl.MultiplyBean"/>
<!--IResultSaver beans-->
<bean name="db" class="InversioOfControl.DbWriter">
<constructor-arg value="yourDataSource"/>
</bean>
<bean name="file" class="InversioOfControl.FileWriter">
<constructor-arg value="fileForSave.txt"/>
</bean>
<!-- Here we describe calculator bean contains two props -->
<bean name="calculator" class="InversioOfControl.Calculator">
<property name="saver" ref="db"/>
<property name="operation" ref="add"/>
</bean>
</beans>
So user can choose beans dynamically, during program execution. User has to change names of beans in property fields.