CSC/ECE 517 Fall 2009/wiki3 5 rm

From Expertiza_Wiki
Jump to navigation Jump to search

Dependency Inversion policy

Introduction

The Dependency Inversion Principle has been proposed by Robert C. Martin. It states that:

"High level modules should not depend upon low level modules. Both should depend upon abstractions. Abstractions should not depend upon details. Details should depend upon abstractions."

The principle is reverse the conventional philosophy of high level functions in softwares need to depend on the low level functions. The principle states that high level or low level modules should not depend upon each other, instead they should depend upon abstractions. Further it also states that these abstractions should not depend on the details and inversely the details should depend on the abstractions. According to this principle the way of designing a class structure is to start from high level modules to the low level modules:

High Level Classes → Abstraction Layer → Low Level Classes

Overview

The Dependency Inversion Principle is defined as follows:

  1. High-level modules should not depend upon low-level modules. Both should depend upon abstractions.
  2. Abstractions should not depend upon details. Details should depend upon abstractions.

The problem with the conventional design architecture is that the higher level components depends on the lower level components. This can be understood from the diagram below.

Figure 1: Higher-level components depend upon lower-level components

From the above diagram we see that the component A depends on component B, which in turn depends on component C. These dependencies make the higher level modules or components more complex and inflexible. This also leads to tight coupling of higher and lower level components. Thus reducing the over all flexibility of the system.

The primary motive of the dependency inversion principle is to decouple the high level components from their dependency on the low level components of the system. This can be obtained by creating interfaces as a part of the higher level component package which define the components for the extra functionality required. This protects the component from depending on any specific implementation of the provided interface/functionality. Thus making the given function more portable. The above example can be restructured as follows

Figure 2: Relationship diagram

As one can see in the above figure the component B doesn't depend on A but rather depends on the interface that is also used by A. The same relationship is additionally shown between components B and C. Take special note that the interfaces are packaged together with the higher-level components and are defined in terms of the higher-level component’s needs, not the lower-level component’s behavior. It is this association of the interface with the client component which logically inverts the conventional dependency flow.

Example of Dependency inversion principle

Source

// Dependency Inversion Principle - Bad example
class Worker {
    public void work() {
    // ....working
    }
}
class Manager {
    Worker m_worker;
    public void setWorker(Worker w) {
        m_worker=w;
    }
    public void manage() {
        m_worker.work();
    }
}
class SuperWorker {
    public void work() {
    //.... working much more
    }
}


The code shown below implements the code above using Dependency Inversion principle.This helps us in solving the following problems.

  1. Manager class should not be changed.
  2. Minimized risk to affect old funtionality present in Manager class.
  3. No need to redone the unit testing for Manager class.
// Dependency Inversion Principle - Good example
interface IWorker {
    public void work();
}
class Worker implements IWorker{
    public void work() {
    // ....working
    }
}
class SuperWorker implements IWorker{
    public void work() {
    //.... working much more
    }
}
class Manager {
    IWorker m_worker;
    public void setWorker(IWorker w) {
        m_worker=w;
    }
    public void manage() {
        m_worker.work();
    }
}

A design pattern based on dependency inversion policy (Template design pattern)

The Template Design pattern implements the Dependency Inversion Principle by setting up the outline or skeleton of an algorithm, leaving the details to be implemented by the classes or modules implementing it. This way, the sub classes will be getting there information from the abstract classes. Further these abstract classes are not dependent on the details while the vice versa is true. The UML diagram below gives you better understanding of the Template design pattern. There are method calls to operation1() and operation2(). The definition of these methods are defined in the subclass which override them.

Figure 3: Template Design Pattern

Why call it dependency inversion policy?

The dependency structure of a well designed object oriented application is "inverted" with respect to the dependency structure that normally results from a "traditional" application which is implemented in a more procedural style. In a procedural application high level modules depend upon low level modules and abstractions depend upon details.

Consider the implications of high level modules that depend upon low level modules. It is the high level modules that contain the important policy decisions and business models of an application. It is these models that contain the identity of the application. Yet, when these modules depend upon the lower level modules, then changes to the lower level modules can have direct effects upon them; and can force them to change. It is the high level modules that ought to be forcing the low level modules to change. It is the high level modules that should take precedence over the lower level modules. High level modules simply should not depend upon low level modules in any way. Moreover, it is high level modules that we want to be able to reuse. When high level modules depend upon low level modules, it becomes very difficult to reuse those high level modules in different contexts. However, when the high level modules are independent of the low level modules, then the high level modules can be reused quite simply.

Benefits and Consequences

Dependency Inversion Principle proposes a useful mechanism in decoupling the dependencies between the high and low level components of the system. This not only makes sure that the high level components don't directly depend on the low level components, it also makes sure that the core functionality with n the application can be more easily reused in other contexts.Applying Dependency Inversion Principle makes it easier for reusing the higher level components, but the negative aspect of this is that it prevents the reuse of low level components. Further Dependency Inversion Principle does account for the reuse of lower-level components by maintaining the client interface in a separate package, assigning ownership of this package to one or more consumers of a lower-level component can itself be problematic.

Conclusion

This principle is applied to make sure that the high level classes are not directly dependent on the low level classes, they are doing that using either by interfaces or abstract classes.In that case the creation of new low level objects inside the high level classes(if necessary) can not be done using the operator new. Instead, some of the Creational design patterns can be used, such as Factory Method, Abstract Factory, Prototype. Of course, using this principle implies an increased effort and a more complex code, but more flexible. This principle cannot be applied for every class or every module. If we have a class functionality that is more likely to remain unchanged in the future there is not need to apply this principle.When a component does not depend on lower level components directly but only through abstractions this component is mobile that is, the component is reusable in many different contexts.

Appendix

  • Abstraction - Abstraction is the process or result of generalization by reducing the information content of a concept or an observable phenomenon, typically to retain only information which is relevant for a particular purpose.
  • Tight coupling - tight coupling (or tightly coupled) is a type of coupling that describes a system in which hardware and software are not only linked together, but are also dependant upon each other.
  • Factory factory - The factory method pattern is an object-oriented design pattern. Like other creational patterns, it deals with the problem of creating objects (products) without specifying the exact class of object that will be created.
  • Abstract factory - Abstract Factory Pattern provides a way to encapsulate a group of individual factories that have a common theme. In normal usage, the client software creates a concrete implementation of the abstract factory and then uses the generic interfaces to create the concrete objects that are part of the theme.

References

[1] http://www.objectmentor.com/resources/articles/dip.pdf
[2] http://www.ctrl-shift-b.com/2008/12/examining-dependency-inversion.html
[3] http://blogs.imeta.co.uk/jyoung/archive/2008/12/17/540.aspx
[4] http://en.wikipedia.org/wiki/Dependency_inversion_principle
[5] http://www.lostechies.com/blogs/gabrielschenker/archive/2009/01/30/the-dependency-inversion-principle.aspx
[6] http://www.oodesign.com/dependency-inversion-principle.html
[7] http://www.eventhelix.com/realtimemantra/Object_Oriented/dependency_inversion_principle.htm
[8] http://davidhayden.com/blog/dave/archive/2005/06/10/1261.aspx
[9] http://doodleproject.sourceforge.net/articles/2001/dependencyInversionPrinciple.html
[10] http://stackoverflow.com/questions/62539/what-is-the-dependency-inversion-principle-and-why-is-it-important
[11] http://www.surfscranton.com/architecture/DIPandOCP/img0.html
[12] http://iface.wordpress.com/2006/03/16/dependency-inversion-principle-and-interface/
[13] http://www.exciton.cs.rice.edu/JavaResources/DesignPatterns/TemplatePattern.htm
[14] Martin, R. C. (1996, May). The Dependency Inversion Principle. C++ Report.