5

I've been working on a project, and as it's grown I realize that two parts that cannot be joined together are inter-dependent.

Let's call these two parts a.exe and b.dll. b.dll provides an implementation that allows a.exe to retrieve data, but I want it to be its own standalone assembly so that it can easily be changed out to make a.exe communicate with different data sources.

However, while a.exe is required to reference b.dll, b.dll requires several functions that are an inseparable part of a.exe.

Since I've been compiling -- to test -- as I've been writing this project, a.exe and b.dll both exist, and I can compile b.dll against a.exe and a.exe against b.dll, but how would/can I ever rebuild both of these from source?

  • How have you not gotten a circular dependency error from Visual Studio? – Oded Dec 19 '11 at 18:39
  • @Oded He's using command line compilation - you can iteratively build this, and create circular dependencies -but it makes it so you can never do a clean rebuild (without extreme difficulty). – Reed Copsey Dec 19 '11 at 18:40
  • @ReedCopsey - I thought he might be doing that, but wanted confirmation from OP. As you said, a clean build is impossible in this scenario. – Oded Dec 19 '11 at 18:41
  • Looks like you have your answer, you should should add it (as an answer) and accept it; I think most people would agree both of the existing answers are better approaches to your design situation in general, but your comment on @Reed Copsey's answer is a more specific answer to the question you posed. – Tao Dec 19 '11 at 19:45
  • I'm curious as to **why** Reed Copsey's answer is considered "better". – Robert Allan Hennigan Leahy Dec 19 '11 at 19:50
  • 1
    @RobertAllanHenniganLeahy Tightly coupled designs are difficult in terms of maintenance, extensibility, and creation. See: http://en.wikipedia.org/wiki/Coupling_(computer_science)#Disadvantages In this case, your exact problem is bullet #2 there... – Reed Copsey Dec 19 '11 at 19:53

3 Answers3

5

I would refactor your system into three assemblies:

  • a.exe - Main EXE nothing should reference this
  • b.dll - As you have it today, but does not reference a.exe, it references c.dll
  • c.dll - This should contain the common pieces / parts that both a and b need to reference
JoshBerke
  • 66,142
  • 25
  • 126
  • 164
  • b.dll only contains one class -- let's call it DataSource -- and a.exe contains a library of classes, but the only one that b.dll needs is the class called Server, which is the main component of a.exe. – Robert Allan Hennigan Leahy Dec 19 '11 at 18:46
4

In general, it would be a good idea to refactor this, and move the shared dependencies into their own assembly (c.dll). This way, both a.exe and b.dll could reference c.dll, and you'd avoid this circular dependency.

Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
  • If I did that though, in this situation, I can only imagine that c.dll would depend on a.exe and b.dll. The functionality isn't properly separable from the code in b.dll and a.exe. – Robert Allan Hennigan Leahy Dec 19 '11 at 18:56
  • 1
    @RobertAllanHenniganLeahy Your goal should be to separate this. Try to design your API so that it's decoupled. There's no reason that the types should all be interdependent. You can always define an interface in c.dll that a.exe or b.dll implements to provide functionalitiy, or use delegates, etc. Circular dependencies like this can always be worked around... – Reed Copsey Dec 19 '11 at 18:58
  • I supposed I don't see the need to avoid inter-dependency just for the sake of avoiding it, especially when the methods you suggest potentially increase complexity and size and potentially decrease performance. I figured an easy way past this problem though -- invoke csc.exe, compile the source for a.exe and b.dll into a.exe, invoke csc.exe, compile the source for b.dll into b.dll and reference a.exe, and then invoke csc.exe one last time, compile the source for a.exe into a.exe and reference b.dll. – Robert Allan Hennigan Leahy Dec 19 '11 at 19:29
2

Invoke csc.exe, compile the source for a.exe and b.dll into a.exe, invoke csc.exe, compile the source for b.dll into b.dll and reference a.exe, and then invoke csc.exe one last time, compile the source for a.exe into a.exe and reference b.dll.