Circular dependency
This article has multiple issues. Please help improve it or discuss these issues on the talk page. (Learn how and when to remove these template messages)
(Learn how and when to remove this template message)
|
In software engineering, a circular dependency is a relation between two or more modules which either directly or indirectly depend on each other to function properly. Such modules are also known as mutually recursive.
Contents
Overview[edit]
Circular dependencies are natural in many domain models where certain objects of the same domain depend on each other.[1] However, in software design circular dependencies between larger software modules are considered an anti-pattern because of their negative effects,[1] however such circular (or cyclic) dependencies have been found to be widespread among the source files of real-world software.[2] Mutually recursive modules are, however, somewhat common in functional programming, where inductive and recursive definitions are often encouraged.
Problems of circular dependencies[edit]
Circular dependencies can cause many unwanted effects in software programs. Most problematic from a software design point of view is the tight coupling of the mutually dependent modules which reduces or makes impossible the separate re-use of a single module.
Circular dependencies can cause a domino effect when a small local change in one module spreads into other modules and has unwanted global effects (program errors, compile errors). Circular dependencies can also result in infinite recursions or other unexpected failures.
Circular dependencies may also cause memory leaks by preventing certain very primitive automatic garbage collectors (those that use reference counting) from deallocating unused objects.
Causes and solutions[edit]
In very large software designs, software engineers may lose the context and inadvertently introduce circular dependencies. There are tools to analyze software and find unwanted circular dependencies.[3]
Circular dependencies are often introduced by inexperienced programmers who need to implement some kind of callback functionality. Experienced programmers avoid such unnecessary circular dependencies by applying design patterns like the observer pattern.
Example of circular dependencies in C++[edit]
Implementation of circular dependencies in C/C++ can be a bit tricky, because any structure or class definition must be placed above its usage in the same file. A circular dependency between classes A and B will thus both require the definition of A to be placed above B, and the definition of B to be placed above A, which of course is impossible. A forward declaration is therefore needed to accomplish this.
The following example illustrates how this is done.
- File a.h:
#ifndef A_H
#define A_H
class B; //forward declaration
class A {
public:
B* b;
};
#endif //A_H
- File b.h:
#ifndef B_H
#define B_H
class A; //forward declaration
class B {
public:
A* a;
};
#endif //B_H
- File main.cpp:
#include "a.h"
#include "b.h"
int main() {
A a;
B b;
a.b = &b;
b.a = &a;
}
Note that although a name (e.g. A
) can be declared multiple times, such as in forward declarations, it can only be defined once (the One Definition Rule).
Self-reference example[edit]
This section contains instructions, advice, or how-to content. (October 2009) |
Following is another example of forward declaration, which might be useful if the application needs a self-sustaining array of objects which is able to add and remove objects from itself during run-time:
- File a.h:
class A {
public:
static A *first, *last;
A *previous, *next;
A();
~A();
};
The static variables first and last have to be defined, because their declaration does not reserve memory space for them. Note: static variables do not change from object to object and stay the same for this given class.
They should also be initialized to 0, or NULL, so we know what they are to start with.
- File a.cpp:
#include "a.h"
A *A::first=0, *A::last=0; // don't put the word static here, that will cause an error
A::A() {
if(first==0) first=this; //first A created
previous=last;
if(previous != 0) previous->next=this;
last=this;
next=0;
}
A::~A() {
if(previous != 0) previous->next=next;
if(next != 0) next->previous=previous;
}
See also[edit]
References[edit]
- ^ a b Lakos, John (1996-07-20). Large-Scale C++ Software Design (1st ed.). Boston: Addison-Wesley. ISBN 9780201633627.
- ^ Melton, Hayden; Tempero, Ewan (2007-01-12). "An empirical study of cycles among classes in Java". Empirical Software Engineering. 12 (4): 389–415. CiteSeerX 10.1.1.141.5362. doi:10.1007/s10664-006-9033-1. ISSN 1382-3256.
- ^ JDepend for Java
External links[edit]
- Why Are Circular References Considered Harmful? : On stackoverflow.com