Skip to main content

By clicking Submit, you agree to the developerWorks terms of use.

The first time you sign into developerWorks, a profile is created for you. Select information in your developerWorks profile is displayed to the public, but you may edit the information at any time. Your first name, last name (unless you choose to hide them), and display name will accompany the content that you post.

All information submitted is secure.

  • Close [x]

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerworks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

By clicking Submit, you agree to the developerWorks terms of use.

All information submitted is secure.

  • Close [x]

Use Inversion of Control in method signatures

IoC isn't just for components

André Fachat (andre.fachat@de.ibm.com), IT Architect, IBM Global Services
André Fachat is an IT Architect in the Enterprise Java community within IBM Global Business Services in Germany. Although he still knows his first computer's machine language by heart, his areas of expertise now include Web and Enterprise Java application architectures, SOA and Web services, distributed computing, and modeling. He holds a Ph.D. in theoretical physics from the Technical University of Chemnitz, Germany, where he investigated stochastic optimization algorithms on parallel computers. He joined IBM in 1999 and has since conducted various project engagements, including production references. He has worked for IBM in such areas as solution architecture, application development, and consulting.

Summary:  The Inversion of Control (IoC) pattern is normally used for components. This article describes how to use the pattern on method signatures to decrease the coupling between components and improve performance. IBM Global Business Services consultant André Fachat uses two examples to show the flexibility of this approach.

Date:  30 Jan 2007
Level:  Introductory
Also available in:   Chinese  Russian

Activity:  13353 views
Comments:  

Inversion of Control (IoC) and Dependency Injection (DI) are patterns that draw a lot of attention (see Resources). They are mostly used within so-called IoC containers, which inject dependencies into a component in the form of other components. However, the patterns don't define the design of these dependency components' methods. In a classic design, value objects or data transfer objects in those methods are used as method parameters and return values when complex objects are required.

This article shows you that you can also use IoC on the method signatures to decouple the methods from the value objects. You do this by replacing the value objects in the method signatures with interfaces. I show you scenarios where this approach can be useful. I use this pattern frequently and find that it helps me better separate the concerns between the components. And in the runtime, it reduces object-creation and copying efforts.

Value objects as method parameters

Much has been written about IoC, so I'll describe only its general principle: a component "outsources" the configuration, localization, and life-cycle aspects of the components it uses. For example, a data access bean -- instead of looking up a JDBC datasource (configuration and localization) and perhaps handling the connection pooling (life cycle) itself -- "gets" the JDBC connection from somewhere and simply uses it. In an IoC setup, these aspects are normally handled by an IoC container that injects the dependencies into the component by calling a setter method, for example.

IoC focuses on handling the life cycle of components. This article focuses not on the component but on the method parameters of operations that a component provides.

Take a look at the dependency graph for a typical setup of a component with two dependencies and the method parameters used (see Figure 1). These method parameters are defined as value objects; that is, objects with no logic that hold only data values.


Figure 1. Dependency graph for components with value objects as method parameters
Dependency graph with value objects

In Figure 1, Component1 depends on Component2 and Component3 (in terms of IoC), and calls method2 and method3, respectively. It's irrelevant at this point if Component1 "knows" Component2 or Component3 directly or only via interfaces that Component2 and Component3 implement. However, it's essential that normally the method parameters are objects, not interfaces.

In this setup, when Component1 calls method2, it must instantiate Value Object 2 and fill it with values. Likewise, if Component1 calls method3, it then must instantiate Value Object 3 and fill it with values.

Now assume that Component1 needs to call method2 and method3 with the same input data to get different output data. For example, Component1 could be an order-preparation component, method2 a method to determine lead times, and method3 a method to determine prices. Both methods need the same input and provide different output.

In this case, using value objects as method parameters requires that Component1 create the value objects for each method call and actively copy the values required into the value objects. Also, each of the value objects must be instantiated, which is not as expensive as it was with early Java™ versions, but it still requires resources. All this reduces performance. The following sections show how you can optimize performance instead.


Interfaces to the rescue

The goal is to avoid copying values between the various value objects. You can do this by defining the method parameters as interfaces. This way, the calling component can use any object it likes as a method parameter, as long as the object implements the interface.

Figure 2 shows the new dependency graph:


Figure 2. Dependency graph for components with interfaces as method parameters
Dependency graph with interfaces

The dependency methods define the method parameters as interfaces. The calling component instantiates an object -- deliberately not called a value object -- that implements these interfaces and uses this object as a method parameter in both method calls.

The following examples highlight some advantages of this approach.


Example: Pricing and lead times

Again, let's assume that method1 determines lead times for an order and method2 determines prices. A simple definition of these components and methods could be as shown in Listing 1:


Listing 1. Sample components using interfaces as method parameters
interface LeadtimeComponent {
   void getLeadtimes(List<LeadtimeItem> items) throws LeadtimeException;
}
interface LeadtimeItem {
   Long getArticleId();
   BigDecimal getQuantity();
   String getQuantityUnit();

   void setLeadtimeInDays(Integer leadtime);
}

interface PricingComponent {
   void getPrices(List<PriceItem> items) throws PricingException;
}
interface PriceItem {
   Long getArticleId();
   BigDecimal getQuantity();
   String getQuantityUnit();

   void setPrice(BigDecimal price);
   void setPriceUnit(String currency);
}

Note that both interfaces define the very same method signatures for the methods to retrieve the article data: getArticleId(), getQuantity(), and getQuantityUnit(). Also note that the component methods have no return values; they modify the objects given "in place" by calling the setter methods on the parameter objects (that is, interfaces) to set the prices and lead times.

This approach simplifies the implementation of the pipeline pattern (see Resources), where data is "piped" from one component to the next, and where one stage of the pipeline (component) uses data provided by previous steps in the pipeline. Figure 3 shows a sequence diagram that uses the pipeline pattern:


Figure 3. Sequence diagram for order preparation, using the pipeline pattern
Sequence diagram for an order preparation

See the full figure here.

In this example, the order-preparation process first reads the article IDs from the shopping cart. Then it adds more information from the catalog database itself, retrieves lead times and prices (where prices need lead times), and stores the additional information in the cart so it can be used in finalizing the order. If the item objects returned from reading the shopping cart implement the interfaces required by the catalog, lead times, and prices methods, no copying at all is necessary in the process.


Factory methods

You might wonder about the OrderDB component in Figure 3 and its readCart() method. Indeed, this is a special case. In the preceding example, all objects that were modified by the dependency method (such as getPrices(...)) were already passed as method parameters. This is impossible when the component is reading data from a database because in this case, the number of items in the shopping cart is unknown before it's read.

The solution here is to provide a method parameter with a factory method for the items to read, as shown in Listing 2:


Listing 2: Using factory methods in the method parameter
interface OrderDBComponent {
   void readCart(Cart cart) throws OrderDBException;
}
interface Cart {
   Long getCartId();

   CartItem newItem();
   void addItem(CartItem item);
}
interface CartItem {
   void setArticleId(Long articleId);
   ...
}

With this definition, the OrderDB component reads the items from the database and for each item it reads, it gets a new item object (CartItem) from the Cart object using the newItem() method. After filling in the values read from the database, CartItem is added to the cart with the addItem() method. Note that adding the item to the cart only after filling it with values keeps the cart consistent at all times.


Impedance mismatch

The approach I describe works well if the parameter interfaces defined by multiple dependency components are compatible in that they can be implemented by the very same object. Incompatibilities can arise when, for example, two interfaces define the same method with different return value types. You must take care in designing the methods so you don't introduce such incompatibilities. Also, when designing the methods, you should make sure the semantic meaning of methods defined by the method parameter interfaces is the same when the method signatures are the same in different interfaces.

However, even if incompatibilities exist, all is not lost! Adapter objects can transform an object into one that implements the required interface, without copying the data around. Although you must instantiate the adapter object with this approach, it still avoids copying the data values.


Another example

Here's another example that shows the flexibility of this approach. I wrote an editor for a certain object model but wanted to keep the model implementation separate from the editor implementation. So, I let the editor define interfaces for a model that it can edit. Then the actual model implementation implemented the interface in Listing 3:


Listing 3. Model editor interface example
interface ModelEditor {
   void edit(Model model);
}
interface Model {
   ModelElement newElement();
   ModelElement addElement(ModelElement element);
}

In this (vastly) simplified definition, you can see that the addElement() method on the model not only takes a ModelElement as a parameter but also returns an instance of ModelElement. The returned ModelElement is the model element that is being replaced by the newly added model element, or NULL if no element is being replaced. The returned value is then stored in an undo command so that the model can be restored easily by calling addElement() again. Also, the addElement() method implements model consistency checks and rejects invalid changes.


Conclusion

This article showed a specific form of IoC that applies to the parameters of the component methods and not to components. Using interfaces as method parameters is (in IoC terms) a form of context IoC, applied on a dependency of the caller. Just as a dependency component (such as PriceComponent) is injected into a caller component (such as OrderPrepareComponent), the caller component injects its dependency object (the implementation of the method parameter interface) into the dependency component's method. Because the called component is restricted to the methods defined in the parameter interface, the interface can ensure that the object given as a parameter is kept consistent. Carefully minimizing the interface to the functionally necessary methods reduces the coupling between the components.


Resources

Learn

Discuss

About the author

André Fachat is an IT Architect in the Enterprise Java community within IBM Global Business Services in Germany. Although he still knows his first computer's machine language by heart, his areas of expertise now include Web and Enterprise Java application architectures, SOA and Web services, distributed computing, and modeling. He holds a Ph.D. in theoretical physics from the Technical University of Chemnitz, Germany, where he investigated stochastic optimization algorithms on parallel computers. He joined IBM in 1999 and has since conducted various project engagements, including production references. He has worked for IBM in such areas as solution architecture, application development, and consulting.

Report abuse help

Report abuse

Thank you. This entry has been flagged for moderator attention.


Report abuse help

Report abuse

Report abuse submission failed. Please try again later.


developerWorks: Sign in


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Select information in your developerWorks profile is displayed to the public, but you may edit the information at any time. Your first name, last name (unless you choose to hide them), and display name will accompany the content that you post.

Choose your display name

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


Rate this article

Comments

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Java technology, Architecture
ArticleID=192490
ArticleTitle=Use Inversion of Control in method signatures
publish-date=01302007
author1-email=andre.fachat@de.ibm.com
author1-email-cc=

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

For articles in technology zones (such as Java technology, Linux, Open source, XML), Popular tags shows the top tags for all technology zones. For articles in product zones (such as Info Mgmt, Rational, WebSphere), Popular tags shows the top tags for just that product zone.

For articles in technology zones (such as Java technology, Linux, Open source, XML), My tags shows your tags for all technology zones. For articles in product zones (such as Info Mgmt, Rational, WebSphere), My tags shows your tags for just that product zone.

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Special offers