Design Better Software with the Inversion of Control Pattern

Design Better Software with the Inversion of Control Pattern

he Inversion of Control (IoC) pattern, also known as Dependency Injection, has recently become popular in the J2EE community. Several open source projects, including Spring, PicoContainer, and HiveMind, use the IoC pattern to develop lightweight J2EE Containers.

IoC is not a new concept, however. It has been around for several years now. Using object-oriented design principles and features such as interface, inheritance, and polymorphism, the IoC pattern enables better software design that facilitates reuse, loose coupling, and easy testing of software components. This article discusses IoC and demonstrates how to use this pattern in your software design without having to implement any of the open source frameworks.

IoC Design Pattern
Assume Class A has a relationship with Class B: it wants to use the services of Class B. The usual way to establish this relationship is to instantiate Class B inside Class A. Though this approach works, it creates tight coupling between the classes. You can’t easily change Class B without modifying Class A. To eliminate the coupling, you can have a Configurator inject the instance of Class B (Object “b”) to the instance of Class A (Object “a”). If you want to change the implementation of Class B later on, you simply change the Configurator object. So, the control of how Object “a” gets the reference of Object “b” is inverted. Object “a” is not responsible for getting the reference to Object “b”. Instead, the Configurator is responsible for it. This is the basis for the IoC design pattern.

 
Figure 1. Object “a” Directly Creates Object “b”

To demonstrate the benefits of the Configurator object in this case, the following examples show a simple design without IoC and one with IoC (click here to download the sample code for this article).

Listing 1 and Figure 1 are simple examples in which Class A uses Class B:

public class A{  private B b;  public A(){    b=new B();}Listing 1. Class A Directly Refers Class B 

Listing 1 assumes the following design decisions:

  1. Class A needs a reference to Class B.
  2. Class B is a concrete class that has a default constructor.
  3. Class A owns the instance of class B.
  4. No other class can access the instance of Class B.
 
Figure 2. Object “a” First Creates Object “c” and Then Object “b” by Passing Object “c”

If any one of the above design decisions change, then the code in Listing 1 must be modified. For example, if Class B changed to have a non-default constructor, which takes Class C (see Figure 2), then Listing 1 would change to Listing 2.

public class A{  private B b;  public A(){   C c=new C();    b=new B(c);}Listing 2. Class A Directly Refers Class B and Class C 

Once again, Listing 2 assumes certain design decisions. Now Object “a” owns both Object “b” and Object “c”. If Class B or Class C changes at all, then Class A needs to change as well. In essence, a simple design of a simple class with implicit design assumptions becomes a maintenance nightmare in the future. Consider how difficult making changes would be if you had this scenario in a typical application with several classes.

Alternatively, if you use a framework that uses the IoC pattern, the responsibility of creating Object “b” moves from Object “a” to the IoC framework, which creates and injects Object “b” into Object “a”. This insulates Class A from the changes in Class B. Before Object “a” is used, it needs the reference to Object “b”. The IoC framework will inject Object “b” into Object “a”.

 
Figure 3. IoC Framework Creates Object “b” and Sets It to Object “a”

Listing 3 shows Class A from the previous listings modified to use the IoC pattern.

public class A{  private B b;  public A(){  }   public setB(B b){   this.b=b; } }Listing 3. Class A Gets the Reference to Class B via setB 

Listing 3 assumes the following design decisions:

  1. A needs a reference to B, and it doesn’t need to know how B is instantiated.
  2. B can be an interface, an abstract class, or a concrete class.
  3. Before the instance of Class A is used, it needs a reference to the instance of Class B.

From the above design decisions, you can see that there is no tight coupling between Class A and Class B. Both can be changed independently without affecting each other. Of course, if there is any change in the public methods of Class B, Class A needs to change as well. But how Object “b” is created and managed is not decided in the implementation of Object “a”. Instead, the IoC framework uses the setB() method in Object “a” to inject Object “b” (see Figure 3).

IoC Frameworks
Several open source IoC frameworks like Spring, PicoContainer, and HiveMind support the IoC pattern. While the general IoC principle is simple, each of these frameworks supports different implementations and offers different benefits. The IoC pattern can be implemented in three ways: setter based, constructor based, and interface based. This section briefly discusses each of them. For more in-depth details, refer to the frameworks’ home pages.

Setter-Based IoC
This type of IoC uses a setter method to inject the referenced object into a referring object. This is the most common type of IoC, and both Spring and PicoContainer implement it. Setter-based IoC is good for objects that take optional parameters and objects that need to change their properties many times during their lifecycles. Its main disadvantage is that you need to expose all the properties of an object to the outside world when using a setter method. This breaks one of the key OO principles: data encapsulation and information hiding.

Constructor-Based IoC
This type of IoC uses a constructor to set the reference of the object. Its main advantage is that only the creator knows about the referenced object. Once the object is created, the client code that uses the object is unaware of the referenced object. This approach may not work in all types of applications, however. For example, if you use an external API that needs a default constructor, then you need to go with a setter-based IoC. A constructor-based IoC is the main type of implementation in Spring.

Interface-Based IoC
In this type of IoC, objects implement a specific interface from the IoC framework, which the IoC framework will use to properly inject the objects. One of the main advantages of this type is that it doesn’t need an external configuration file to configure the object references. Since you need to use the IoC framework’s marker interface, the IoC framework knows how to glue the objects together. It is similar to using EJB. Any EJB container knows how to instantiate your objects and hook them with itself. The main disadvantage of this approach is that the marker interface ties your application to a specific IoC framework. Apache Avalon is based on this approach but this project has been closed.

An Example of IoC
If you are starting a new project, you can choose any of the open source IoC frameworks based on your needs. If you want to use the IoC pattern in your existing project then you need to write your own classes that support IoC. Though the open source frameworks offer off-the-shelf components and may provide many more features than your own implementation, you can still develop a set of classes that support the IoC pattern. This section demonstrates how.

Say you want to write a CustomerService object to process customer data. Assume customer data is stored in two different places: a relational database and an XML file. You also want to have CustomerService create Customer objects by reading data from either the database or the XML file. Now, say you want to accomplish this without changing the source code of CustomerService to switch between the database and XML file.

DataSource
First, design an interface (see Listing 4) and define common methods, which you will use to retrieve and store customer data. Both XMLDataSource and JDBCDataSource classes implement this interface.

public interface DataSource {	public Object retrieveObject();	public void setDataSourceName(String name);	public String getDataSourceName();	public void storeObject(Object object);}Listing 4. Common Interface to Customer Data 

XMLDataSource
Listing 5 shows a class that implements DataSource and provides an implementation that uses an XML file to retrieve and store customer data.

public class XMLDataSource implements DataSource {	private String name;	/**	 * Default Constructor	 */	public XMLDataSource() {		super();	}	/**	 * Retrieve Customer data from XML Source and construct	 * Customer object	 */	public Object retrieveObject() {		//get XML data, parse it and then  construct		//Customer object 		return new Customer("XML",10);	}		/**	 * Set the DataSource name	 */	public void setDataSourceName(String name) {		this.name=name;			}	/**	 * Return DataSource name	 */	public String getDataSourceName() {		return name;	}	/**	 * Store Customer into XML file	 */	public void storeObject(Object object) {		//Retrieve customer data and store it in 		//XML file			}}Listing 5. Class to Retrieve Customer Data from XML File 

RelationalDataSource
The class in Listing 6 also implements DataSource and provides the same implementation as XMLDataSource with only one difference: this class retrieves and stores customer data using a relational database.

public class RelationalDataSource implements DataSource {	private String name;		/**	 * Default constructor	 */	public RelationalDataSource() {		super();	}	/**	 * Using the DataSource retrieve data for Customer and build a 	 * Customer object to return it to the caller	 */	public Object retrieveObject() {		//get data for Customer object from DB and create a 		//Customer object		return new Customer("Relational",10);	}	/**	 * Set the DataSource name	 */	public void setDataSourceName(String name) {		this.name=name;			}	/**	 * Return the name of the DataSource	 */	public String getDataSourceName() {		return name;	}	/**	 * Store Customer into relational DB	 */	public void storeObject(Object object) {		//store the customer data into Relational DB			}}Listing 6. Class to Retrieve Customer Data from Relational DB 

Customer
The simple class in Listing 7 holds customer data, which either the XMLDataSource or RelationalDatSource object will construct.

public class Customer {	private String name;	private int age;		/**	 * Default Constructor	 */	public Customer(String name, int age) {		this.name=name;		this.age=age;	}	/**	 * @return Returns the age.	 */	public int getAge() {		return age;	}	/**	 * @param age The age to set.	 */	public void setAge(int age) {		this.age = age;	}	/**	 * @return Returns the name.	 */	public String getName() {		return name;	}	/**	 * @param name The name to set.	 */	public void setName(String name) {		this.name = name;	}}Listing 7. Class That holds Customer Data 

CustomerService
This class in Listing 8 has a reference to DataSource, which it uses to retrieve and store Customer objects. The actual implementation of DataSource will be either XMLDataSource or RelationalDataSource, which will be injected into the CustomerService object. The constructor of the CustomerService object accepts a concrete implementation of the DataSource object and uses it to retrieve and store customer data.

public class CustomerService {	private DataSource dataSource;	private Customer customer;		/**	 * Constructor in which DataSource object is injected. Based on the 	 * ioc.properties this object can either refer to RelationlDataSource or 	 * XMLDataSource	 */	public CustomerService(DataSource dataSource) {		super();		this.dataSource=dataSource;		customer=(Customer)dataSource.retrieveObject();	}	/**	 * Modify Customer name 	 * @param name	 */	public void updateCustomerName(String name) {		customer.setName(name);	}		/**	 * Modify Customer age	 * @param age	 */	public void updateCustomerAge(int age){		customer.setAge(age);	}		/**	 * 	 * @return Customer name	 */	public String getCustomerName(){		return customer.getName();	}		/**	 * 	 * @return Customer age	 */	public int getCustomerAge(){		return customer.getAge();	}	}Listing 8. CustomerService Class That Gets DataSource Reference via ServiceConfigurator 
 
Figure 4. IoC Using ServiceConfigurator

ServiceConfigurator
The ServiceConfigurator in Listing 9 is a main class that uses the IoC pattern to inject the concrete implementation of DataSource into the CustomerService object. It reads the configuration parameters (see Figure 4) from the ioc.properties file and decides to create either an XMLDataSource or a RelationalDataSource object. The DataSource object is created using its default Constructor, and the name of the DataSource is set using its setter method. The created DataSource object is injected into CustomerService using its constructor, which accepts a DataSource reference. The created CustomerService object is stored in the ServiceConfigurator registry so that it will be returned when a subsequent request comes for the CustomerService object.

Figure 4 shows the inner workings of ServiceConfigurator, and Figure 5 shows the UML diagram for all of the classes explained previously. You can switch between XMLDataSource and RelationalDataSource just by editing the ioc.properties file.

public class ServiceConfigurator {	public static final String IoC_CONFIG_FILE="ioc.properties";	private Properties props;	private HashMap serviceRegistery;	private static ServiceConfigurator thisObject;		/**	 * This method first checks if there is a ServiceConfigurator instance	 * exist, if not creates a one, stored it and returns it	 * @return	 */	public static ServiceConfigurator createServiceConfigurator(){		if(thisObject==null){			thisObject=new ServiceConfigurator();		}		return thisObject;	}		/**	 * Private Constructor makes this class singleton	 */	private ServiceConfigurator() {			props = new Properties();			serviceRegistery=new HashMap();					loadIoCConfig();			createServices();	}		/**	 * Load the IoC_CONFIG_FILE properties file	 *	 */	public void loadIoCConfig(){		InputStream is = this.getClass().getClassLoader().getResourceAsStream(IoC_CONFIG_FILE);        try {            props.load(is);            is.close();        } catch (FileNotFoundException e) {            e.printStackTrace();        } catch (IOException e) {        	e.printStackTrace();        }	}		/**	 * Create the CustomerService by getting the DataSource name from the	 * properties file. The CustomerService object is stored in the	 * serviceRegistery so that it will be retrieved when requested. 	 * During the construction of CustomerService the DataSource object	 * is injected into it. So the CustomerService can access the DataSource	 * to retrieve the Customer.	 *	 */	public void createServices(){		String dataSourceName=props.getProperty("dataSource");		DataSource dataSource=null;		if(dataSourceName!=null){			try {				dataSource=(DataSource)Class.forName(dataSourceName).newInstance();			} catch (InstantiationException e) {				e.printStackTrace();			} catch (IllegalAccessException e) {				e.printStackTrace();			} catch (ClassNotFoundException e) {				e.printStackTrace();			}		}		//name of the DataSource is retrieved from the properties file		dataSource.setDataSourceName(props.getProperty("name"));		CustomerService customerService=new CustomerService(dataSource);		serviceRegistery.put(CustomerService.class,customerService);	}	/**	 * Stored Service is retrieved from serviceRegistery for the given	 * Class object	 * 	 * @param classObj	 * @return	 */	public Object getService(Class classObj){		return serviceRegistery.get(classObj);	}	}Listing 9. ServiceConfigurator that Uses IoC to Inject DataSource into CustomerService 
 
Figure 5. ServiceConfigurator UML Class Diagram

The ServiceConfigurator is a simple class that supports the IoC pattern. You can modify it to add more features such as simultaneously supporting multiple DataSource objects, dynamically injecting the DataSource into CustomerService, and life cycle management.

CustomerServiceTester
The class in Listing 10 is a JUnit test that uses CustomerService. It uses ServiceConfigurator to retrieve the CustomerService object, which is used to change the Customer name and age.

public class CustomerServiceTester extends TestCase{	private ServiceConfigurator serviceConfig;		/**	 *Default Constructor 	 */	public CustomerServiceTester() {		super();	}		/**	 * Create ServiceConfigurator	 */	public void setUp() throws Exception{		super.setUp();		serviceConfig=ServiceConfigurator.createServiceConfigurator();	}	/**	 * Test CustomerService and check for Customer	 * @throws Exception	 */	public void testCustomerService() throws Exception{		CustomerService custService=(CustomerService)serviceConfig.getService(CustomerService.class);		assertNotNull(custService);		custService.updateCustomerAge(30);		custService.updateCustomerName("Mani");		assertEquals(30,custService.getCustomerAge());		assertEquals("Mani",custService.getCustomerName());			}Listing 10. JUnit Test that Uses CustomerService 

Service Configurator vs. Service Locator
The implementation of ServiceConfigurator in Listing 9 may lead you to think that there is no difference between the ServiceConfigurator class that uses IoC and one that uses the Service Locator pattern. In fact, you can modify the CustomerService in Listing 8 to use a Service Locator, which will give the CustomerService class either an XMLDataSource or a RelationalDataSource object, based on the Service Locator’s internal implementation. By changing the internal implementation of Service Locator, you can switch the DataSource from which CustomerService gets its Customer. In this context, both Service Locator and ServiceConfigurator provide the same functionality to CustomerService. The main difference is how CustomerService gets the DataSource reference. In Service Locator, CustomerService must get the DataSource reference directly from Service Locator, while in ServiceConfigurator, CustomerService gets the DataSource indirectly through ServiceConfigurator.

So, what is the advantage of using Service Locator instead of ServiceConfigurator? ServiceConfigurator frees CustomerService from either acquiring or instantiating the DataSource directly. This makes CustomerService easy to test: simply inject the appropriate DataSource into it. More over, as explained previously in Listing 1 and Listing 2, because you do not make any assumptions about how CustomerService will get the DataSource reference, you can reuse the CustomerService object in different applications. This is the main advantage of using Service Configurator over Service Locator.

What to Inject?
In Figure 5, you see that CustomerService has a relationship with both the DataSource and the Customer classes. Yet this discussion has been about how to inject DataSource into CustomerService. Why not inject Customer into CustomerService? In fact, it is perfectly legitimate to make the ServiceConfigurator inject the Customer object into CustomerService by creating an interface for Customer and referencing it in CustomerService. It all depends on your application requirements and how you want to design the CustomerService class. The previous example made DataSource responsible for creating and storing the Customer object and created a tight coupling between DataSource and Customer. In the future, any change in the Customer class will require changes to all of the implementations of DataSource. However, the design decision was made with the assumption that the public methods of Customer would not undergo any changes, and if any changes occurred in the internals of Customer, then they will not affect the DataSource.

The only anticipated change is how the Customer data is retrieved and stored. Currently, the Customer data is stored in a relational database or in an XML file. Perhaps in the future, it might be stored in an object database or retrieved via a Web service. In either scenario, you can have new classes retrieve and store Customer data. So, based on these assumptions, the example injects only the DataSource into CustomerService, which is used to retrieve and store Customer data.

Add on to Simple Example
This article discussed the IoC pattern and briefly discussed open source frameworks that use it. It also discussed how you could incorporate the IoC pattern in your existing applications using the ServiceConfigurator example. Though simple, the ServiceConfigurator class supports the IoC pattern and provides all of its benefits. You can modify the ServiceConfigurator to add more features as you need for your applications.

devx-admin

devx-admin

Share the Post:
Savings Extravaganza

Big Deal Days Extravaganza

The highly awaited Big Deal Days event for October 2023 is nearly here, scheduled for the 10th and 11th. Similar to the previous year, this

Remote Learning

Revolutionizing Remote Learning for Success

School districts are preparing to reveal a substantial technological upgrade designed to significantly improve remote learning experiences for both educators and students amid the ongoing

Revolutionary SABERS Transforming

SABERS Batteries Transforming Industries

Scientists John Connell and Yi Lin from NASA’s Solid-state Architecture Batteries for Enhanced Rechargeability and Safety (SABERS) project are working on experimental solid-state battery packs

Savings Extravaganza

Big Deal Days Extravaganza

The highly awaited Big Deal Days event for October 2023 is nearly here, scheduled for the 10th and 11th. Similar to the previous year, this autumn sale has already created

Cisco Splunk Deal

Cisco Splunk Deal Sparks Tech Acquisition Frenzy

Cisco’s recent massive purchase of Splunk, an AI-powered cybersecurity firm, for $28 billion signals a potential boost in tech deals after a year of subdued mergers and acquisitions in the

Iran Drone Expansion

Iran’s Jet-Propelled Drone Reshapes Power Balance

Iran has recently unveiled a jet-propelled variant of its Shahed series drone, marking a significant advancement in the nation’s drone technology. The new drone is poised to reshape the regional

Solar Geoengineering

Did the Overshoot Commission Shoot Down Geoengineering?

The Overshoot Commission has recently released a comprehensive report that discusses the controversial topic of Solar Geoengineering, also known as Solar Radiation Modification (SRM). The Commission’s primary objective is to

Remote Learning

Revolutionizing Remote Learning for Success

School districts are preparing to reveal a substantial technological upgrade designed to significantly improve remote learning experiences for both educators and students amid the ongoing pandemic. This major investment, which

Revolutionary SABERS Transforming

SABERS Batteries Transforming Industries

Scientists John Connell and Yi Lin from NASA’s Solid-state Architecture Batteries for Enhanced Rechargeability and Safety (SABERS) project are working on experimental solid-state battery packs that could dramatically change the

Build a Website

How Much Does It Cost to Build a Website?

Are you wondering how much it costs to build a website? The approximated cost is based on several factors, including which add-ons and platforms you choose. For example, a self-hosted

Battery Investments

Battery Startups Attract Billion-Dollar Investments

In recent times, battery startups have experienced a significant boost in investments, with three businesses obtaining over $1 billion in funding within the last month. French company Verkor amassed $2.1

Copilot Revolution

Microsoft Copilot: A Suit of AI Features

Microsoft’s latest offering, Microsoft Copilot, aims to revolutionize the way we interact with technology. By integrating various AI capabilities, this all-in-one tool provides users with an improved experience that not

AI Girlfriend Craze

AI Girlfriend Craze Threatens Relationships

The surge in virtual AI girlfriends’ popularity is playing a role in the escalating issue of loneliness among young males, and this could have serious repercussions for America’s future. A

AIOps Innovations

Senser is Changing AIOps

Senser, an AIOps platform based in Tel Aviv, has introduced its groundbreaking AI-powered observability solution to support developers and operations teams in promptly pinpointing the root causes of service disruptions

Bebop Charging Stations

Check Out The New Bebob Battery Charging Stations

Bebob has introduced new 4- and 8-channel battery charging stations primarily aimed at rental companies, providing a convenient solution for clients with a large quantity of batteries. These wall-mountable and

Malyasian Networks

Malaysia’s Dual 5G Network Growth

On Wednesday, Malaysia’s Prime Minister Anwar Ibrahim announced the country’s plan to implement a dual 5G network strategy. This move is designed to achieve a more equitable incorporation of both

Advanced Drones Race

Pentagon’s Bold Race for Advanced Drones

The Pentagon has recently unveiled its ambitious strategy to acquire thousands of sophisticated drones within the next two years. This decision comes in response to Russia’s rapid utilization of airborne

Important Updates

You Need to See the New Microsoft Updates

Microsoft has recently announced a series of new features and updates across their applications, including Outlook, Microsoft Teams, and SharePoint. These new developments are centered around improving user experience, streamlining

Price Wars

Inside Hyundai and Kia’s Price Wars

South Korean automakers Hyundai and Kia are cutting the prices on a number of their electric vehicles (EVs) in response to growing price competition within the South Korean market. Many

Solar Frenzy Surprises

Solar Subsidy in Germany Causes Frenzy

In a shocking turn of events, the German national KfW bank was forced to discontinue its home solar power subsidy program for charging electric vehicles (EVs) after just one day,

Electric Spare

Electric Cars Ditch Spare Tires for Efficiency

Ira Newlander from West Los Angeles is thinking about trading in his old Ford Explorer for a contemporary hybrid or electric vehicle. However, he has observed that the majority of

Solar Geoengineering Impacts

Unraveling Solar Geoengineering’s Hidden Impacts

As we continue to face the repercussions of climate change, scientists and experts seek innovative ways to mitigate its impacts. Solar geoengineering (SG), a technique involving the distribution of aerosols

Razer Discount

Unbelievable Razer Blade 17 Discount

On September 24, 2023, it was reported that Razer, a popular brand in the premium gaming laptop industry, is offering an exceptional deal on their Razer Blade 17 model. Typically

Innovation Ignition

New Fintech Innovation Ignites Change

The fintech sector continues to attract substantial interest, as demonstrated by a dedicated fintech stage at a recent event featuring panel discussions and informal conversations with industry professionals. The gathering,

Import Easing

Easing Import Rules for Big Tech

India has chosen to ease its proposed restrictions on imports of laptops, tablets, and other IT hardware, allowing manufacturers like Apple Inc., HP Inc., and Dell Technologies Inc. more time

Semiconductor Stock Plummet

Dramatic Downturn in Semiconductor Stocks Looms

Recent events show that the S&P Semiconductors Select Industry Index seems to be experiencing a downturn, which could result in a decline in semiconductor stocks. Known as a key indicator

Anthropic Investment

Amazon’s Bold Anthropic Investment

On Monday, Amazon announced its plan to invest up to $4 billion in the AI firm Anthropic, acquiring a minority stake in the process. This decision demonstrates Amazon’s commitment to