JamesEggers
JamesEggers

Reputation: 12935

What's the best way to handle circular dependencies amongst objects?

In my code, I have the the following objects:

On the project I'm working on, the ErrorManager needs to pull configuration information using the ConfigManager instance while the ConfigManager uses the ErrorManager incase an error occurs.

At the moment, I'm doing the following in code:

ErrorManager _errorManager = new CustomErrorManager();
ConfigManager _configManager = new CustomConfigManager(_errorManager);
_errorManager.SetConfigurationManager(_configManager);

Is there a way for me to clean up this circular reference of dependencies?

Upvotes: 6

Views: 800

Answers (4)

Alex B
Alex B

Reputation: 24936

I would create the following:

ErrorConfig _errorConfig = ...; 
// ErrorConfig is a new config object containing only ErrorManager Configuration
ErrorManager _errorManager = new CustomErrorManager(_errorConfig);
ConfigManager _configManager = new CustomConfigManager(_errorManager);

Now, the ConfigManager can can use the ready-to-run ErrorManager without a bootstrapping problem where the ErrorManager is not ready to handle errors.

Upvotes: 3

BenAlabaster
BenAlabaster

Reputation: 39846

I would create a extension methods for each called AddRelationship, passing in the other object as the parameter.

The object passed would add the relationship and then call the AddRelationship method of the other:

static void AddRelationship(this ConfigManager configMgr, ErrorManager errMgr)
{
    this.ErrorManager = errMgr;
    if (this != errMgr.ConfigManager)
        errMgr.AddRelationship(this);
}

static void AddRelationship(this ErrorManager errMgr, ConfigManager configMgr)
{
    this.ConfigManager = configMgr;
    if (this != configManager.errMgr)
        configMgr.AddRelationship(this);
}

This means that you can add the relationship using either object.

ConfigManager cfg = new ConfigManager();
ErrorManager err = new ErrorManager();
//Assign using either:
err.AddRelationship(cfg);
//Or
cfg.AddRelationship(err);

You should also create RemoveRelationship extensions.

static void RemoveRelationship(this ConfigManager configMgr, ErrorManager errMgr)
{
    if (this.errorManager == errMgr)
    {
        this.errorManager = null;
        if (errManager.configManager == this) 
            errMgr.RemoveRelationship(this);
    }
}

static void RemoveRelationship(this ErrorManager errMgr, ConfigManager cfgMgr)
{
    if (this.ConfigManager == cfgMgr)
    {
        this.configManager = null;
        if (cfgMgr.errorManager == this)
            cfgMgr.RemoveRelationship(this);
    }
}

I don't know that circular references are a particularly good coding practice, but this should solve the question as asked.

Upvotes: 0

dirkgently
dirkgently

Reputation: 111200

Refactor your code. You can probably break up the classes to isolate a portion that you can initialize first and pass on to both the classes.

Upvotes: 0

Travis Jensen
Travis Jensen

Reputation: 5420

Circular references are usually best cleaned up by refactoring a third class that both depend on. For instance, you might have something like this:

BootstrapConfigManager _bcm = new BootstrapConfigManager();
ErrorManager _errorManager = new CustomErrorManager(_bcm);
ConfigManager _configManager = new CustomConfigManager(_bcm, _errorManager);

Upvotes: 2

Related Questions