Need for Dependency Injection
Let us understand why do we need Dependency Injection
As we all learned that DI is required since it provides loose coupling.
Let’s see how this is needed in some scenarios by considering the below requirement
Requirement :
Client need to open the door, whenever it opens he wants the alarm to be activated,and whenever he closes the door he wants alarm to be deactivated.
Create Door.java
- package com.kb.di;
- public class Door {
- public void open()
- {
- }
- public void close()
- {
- }
- }
package com.kb.di; public class Door { public void open() { } public void close() { } }
Now I need to add Alarm functionality to the Door class
Create Alarm.java – Here I am using sound alarm feature.
- package com.kb.di;
- public class SoundAlarm {
- public SoundAlarm() {
- System.out.println("SoundAlarm()");
- }
- public void activate() {
- System.out.println("SoundAlarm activated");
- }
- public void deactivate() {
- System.out.println("SoundAlarm deactivated");
- }
- }
package com.kb.di; public class SoundAlarm { public SoundAlarm() { System.out.println("SoundAlarm()"); } public void activate() { System.out.println("SoundAlarm activated"); } public void deactivate() { System.out.println("SoundAlarm deactivated"); } }
To provide this feature to Door class , I need use ‘has a‘ relationship also called as composition.
Modified Door class is as below
- package com.kb.di;
- public class Door {
- private SoundAlarm alarm=new SoundAlarm();
- public void open(){
- alarm.activate();
- }
- public void close()
- {
- alarm.deactivate();
- }
- }
package com.kb.di; public class Door { private SoundAlarm alarm=new SoundAlarm(); public void open(){ alarm.activate(); } public void close() { alarm.deactivate(); } }
Main client program(DIDemo)
- package com.kb.di;
- public class DIDemo {
- public static void main(String[] args) {
- Door d = new Door();
- d.open();
- d.close();
- }
- }
package com.kb.di; public class DIDemo { public static void main(String[] args) { Door d = new Door(); d.open(); d.close(); } }
Output
Now we finished our requirement of implementing the Alarm feature to Door class.
New Requirement
Now client requirement has been changed and they want Visual Alarm feature for Door class instead of sound alarm.
How can we achieve it ?
Create VisualAlarm class to have its functionality
- package com.kb.di;
- public class VisualAlarm {
- public VisualAlarm() {
- System.out.println("VisualAlarm()");
- }
- public void activate() {
- System.out.println("VisualAlarm activated");
- }
- public void deactivate() {
- System.out.println("VisualAlarm deactivated");
- }
- }
package com.kb.di; public class VisualAlarm { public VisualAlarm() { System.out.println("VisualAlarm()"); } public void activate() { System.out.println("VisualAlarm activated"); } public void deactivate() { System.out.println("VisualAlarm deactivated"); } }
Approach 1
Modify the Door class by adding VisualAlarm class variable as below
- package com.kb.di;
- public class Door {
- //private SoundAlarm alarm=new SoundAlarm();
- private VisualAlarm alarm=new VisualAlarm();
- public void open()
- {
- alarm.activate();
- }
- public void close()
- {
- alarm.deactivate();
- }
- }
package com.kb.di; public class Door { //private SoundAlarm alarm=new SoundAlarm(); private VisualAlarm alarm=new VisualAlarm(); public void open() { alarm.activate(); } public void close() { alarm.deactivate(); } }
Now run DIDemo.java
- package com.kb.di;
- public class DIDemo {
- public static void main(String[] args) {
- Door d = new Door();
- d.open();
- d.close();
- }
- }
package com.kb.di; public class DIDemo { public static void main(String[] args) { Door d = new Door(); d.open(); d.close(); } }
Output
We have achived client’s new modification as well but where did we change ?
Door class – yes we did a change in Door class.
Now again if client comes with sound alarm feature, we need to change our Door class.
So how to avoid this change in our domain class ?
Factory pattern, Yes lets try with Factory pattern
Create a factory which produces the alarm and we name it as Alarm Factory.
Create an interface Alarm and make SoundAlarm ,VisualAlarm class to implement this Alarm interface.
We are doing this to achive Program To Supertype not concrete type(Also called Program to Interface)
Create Alarm Interface
- package com.kb.di;
- public interface Alarm {
- public void activate();
- public void deactivate();
- }
package com.kb.di; public interface Alarm { public void activate(); public void deactivate(); }
Create SoundAlarm class
- package com.kb.di;
- public class SoundAlarm implements Alarm {
- public SoundAlarm() {
- System.out.println("SoundAlarm()");
- }
- public void activate() {
- System.out.println("SoundAlarm activated");
- }
- public void deactivate() {
- System.out.println("SoundAlarm deactivated");
- }
- }
package com.kb.di; public class SoundAlarm implements Alarm { public SoundAlarm() { System.out.println("SoundAlarm()"); } public void activate() { System.out.println("SoundAlarm activated"); } public void deactivate() { System.out.println("SoundAlarm deactivated"); } }
Create VisualAlarm class
- package com.kb.di;
- public class VisualAlarm implements Alarm{
- public VisualAlarm() {
- System.out.println("VisualAlarm()");
- }
- public void activate() {
- System.out.println("VisualAlarm activated");
- }
- public void deactivate() {
- System.out.println("VisualAlarm deactivated");
- }
- }
package com.kb.di; public class VisualAlarm implements Alarm{ public VisualAlarm() { System.out.println("VisualAlarm()"); } public void activate() { System.out.println("VisualAlarm activated"); } public void deactivate() { System.out.println("VisualAlarm deactivated"); } }
Create Alarm Factory
- package com.kb.di;
- public class AlarmFactory {
- public Alarm getRequiredAlarm(){
- return new VisualAlarm();
- }
- }
package com.kb.di; public class AlarmFactory { public Alarm getRequiredAlarm(){ return new VisualAlarm(); } }
Create Door class
- package com.kb.di;
- public class Door {
- private Alarm alarm = new AlarmFactory().getRequiredAlarm();
- public void open()
- {
- alarm.activate();
- }
- public void close()
- {
- alarm.deactivate();
- }
- }
package com.kb.di; public class Door { private Alarm alarm = new AlarmFactory().getRequiredAlarm(); public void open() { alarm.activate(); } public void close() { alarm.deactivate(); } }
Door class gets the Visual alarm from factory.
To change it to sound alarm feature. we need not to modify the Door class but we need to modify the Factory to return sound alarm as below
- package com.kb.di;
- public class AlarmFactory {
- public Alarm getRequiredAlarm(){
- return new SoundAlarm();
- }
- }
package com.kb.di; public class AlarmFactory { public Alarm getRequiredAlarm(){ return new SoundAlarm(); } }
So ultimately change is required in Java class but this time it is factory class instead of Door class.
So how to avoid this change in Java class completely ?
Not possible unless and until we use Spring’s Dependency Injection.
Lets achive it using Spring’s DI in a very simple way
Step 1. Create Alarm Interface – same as above
Step 2. Create VisualAlarm class – same as above
Step 3. Create SoundAlarm class – same as above
Step 4. Create Door class – need to modify to add Alarm reference and its getter, setter as below
- package com.kb.di;
- public class Door {
- private Alarm alarm;
- public void open() {
- alarm.activate();
- }
- public void close() {
- alarm.deactivate();
- }
- public Alarm getAlarm() {
- return alarm;
- }
- public void setAlarm(Alarm alarm) {
- this.alarm = alarm;
- }
- }
package com.kb.di; public class Door { private Alarm alarm; public void open() { alarm.activate(); } public void close() { alarm.deactivate(); } public Alarm getAlarm() { return alarm; } public void setAlarm(Alarm alarm) { this.alarm = alarm; } }
Step 5. Create spring config file
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:context="http://www.springframework.org/schema/context"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
- http://www.springframework.org/schema/context
- http://www.springframework.org/schema/context/spring-context-3.0.xsd">
- <bean id = "door" class ="com.kb.di.Door">
- <property name="alarm" ref="soundAlarm"/>
- </bean>
- <bean id="soundAlarm" class="com.kb.di.SoundAlarm"/>
- <bean id="visualAlarm" class="com.kb.di.VisualAlarm"/>
- </beans>
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <bean id = "door" class ="com.kb.di.Door"> <property name="alarm" ref="soundAlarm"/> </bean> <bean id="soundAlarm" class="com.kb.di.SoundAlarm"/> <bean id="visualAlarm" class="com.kb.di.VisualAlarm"/> </beans>
Step 6. Create a client program
- package com.kb.di;
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
- public class DIDemo {
- public static void main(String[] args) {
- ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
- Door d = context.getBean("door",Door.class);
- d.open();
- d.close();
- }
- }
package com.kb.di; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class DIDemo { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); Door d = context.getBean("door",Door.class); d.open(); d.close(); } }
Now if client wants the feature of visual alarm, no need of changing any java class, we can achive it just by changing the xml file as below
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:context="http://www.springframework.org/schema/context"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
- http://www.springframework.org/schema/context
- http://www.springframework.org/schema/context/spring-context-3.0.xsd">
- <bean id = "door" class ="com.kb.di.Door">
- <property name="alarm" ref="visualAlarm"/>
- </bean>
- <bean id="soundAlarm" class="com.kb.di.SoundAlarm"/>
- <bean id="visualAlarm" class="com.kb.di.VisualAlarm"/>
- </beans>
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <bean id = "door" class ="com.kb.di.Door"> <property name="alarm" ref="visualAlarm"/> </bean> <bean id="soundAlarm" class="com.kb.di.SoundAlarm"/> <bean id="visualAlarm" class="com.kb.di.VisualAlarm"/> </beans>
Now new feature has been added without changing any java class.
Spring injects dependent object using our xml configuration, so we can add corresponding bean for our client based on their requirement.
A nice and simple explanation with example. Thanks a lot
Excellent!!
example are very helpful, thanks sir
Thanks KB