views:

137

answers:

4

Hello,

my problem: Inside an application, all interfaces are declared inside an own dll(project "interfaces", for example).

Inside project interfaces, there are many class implementations, too.

Now I need one of this implemented classes inside another project and get a ring dependency because this project is also a reference in project interfaces.

So, what is the best way to get around this ring dependency? Could it be possible that this is a big mistake in the design of the application?

Schematic representation:

IBigInterface.cs (everything in one file):

interface ISomeInterfaceA
{
    void SomeFunctionA(ClassB x);    // ClassB from newProject.cs
    void SomeFunctionB();
}

//
// etc.
//
class ClassA
{
    //
    // Code
    //
}

newProject.cs (everything in one file):

class ClassB
{
    //
    // used in interfaces.dll
    //
}

class ClassC
{
    void SomeFunction(ClassA a)    // ClassA from IBigInterface.cs
    {
        //
        // do something
        //
    }
}

First thing that comes into my mind would be sth. like:

IBigInterface.cs:

interface ISomeInterfaceA
{
    void SomeFunctionA(IInterfaceB x);    // use interface instead of a class
    void SomeFunctionB();
}

interface IInterfaceB
{
    //
    // declarations
    //
}

class ClassA
{
    //
    // implementation
    //
}

newProject.cs:

class ClassB : IInterfaceB    // implementation of IInterfaceB
{
}

class ClassC
{
    void SomeFunction(ClassA a)
    {
        //
        // implementation
        //
    }
}

so that project newProject wouldn't be a reference in project interfaces anymore (although this means changes in the whole application).

P.S.: I inherited this application so the idea of implementing classes in an interface-project was not my idea :). In General, I would create one file per class (so don't point to this :).

+1  A: 

That's exactly the reason why Java requires public definitions to be in their own files (but I think you get the concept here :)).

It's usually not good to mix pure interface and implementation (though there are cases where it could be useful), and it's definitely a troublemaker if you export those into DLLs.

A cyclic dependency means your projects are too coupled to be distinct. This is usually a symptom of bad design (big ball of mud-like). You should either work on removing that coupling or merge both projects together.

Romain
+1  A: 

If you have a specific project that, as you say, contains all your interfaces, why not introduce another project that contains "helper classes" such as ClassA? Then your interface DLL and the projects depending on the interface DLL could use the classes.

Thorsten Dittmar
+5  A: 

First, there's nothing wrong with combining concrete classes and the interfaces they implement into a single assembly (though it would be a bit strange to call the project "interfaces").

Having said that, circular references are usually a sign that you've over-modularized your code: the parts causing the circular reference belong together and they should be merged into a single assembly.

Other times, a circular reference is just a sign that a class is in the wrong layer; the class needs to be moved into another assembly altogether (usually out of a lower-level infrastructure assembly and into a higher-level assembly). For example, ClassC might really belong in another project that references the "interfaces" assembly.

Jeff Sternal
"the parts causing the circular reference belong together" - I agree with you on the general part, but it can also be because a component that belongs in a top layer was placed in a bottom layer. I had the misfortune of taking over such code :( took me days to fix, and move components to the correct layers.
Pete
@Pete - true! Updating my answer.
Jeff Sternal
A: 

I would try to factor out the classes and interfaces that are common to several projects into a "Common" assembly (or similar), which has no dependencies to the assemblies that reference it.

For example, a business entity such as Product does not have to know anything about how it is persisted to a database or fetched via a web service, but the service components that do things with Product, for example IProductsRepository, needs to know what a Product is. Thus the assembly (or namespace) where IProductsRepository is defined holds a reference to the assembly (or namespace) where Product lives, but not the other way around.

Anders Fjeldstad