views:

165

answers:

5

Hello everyone,

I have a DLL, say A, which implements general algorithms to solve mathematical equations. I have another DLL, say B, which implements mathematical equations DLL B uses DLL A in order to solve it’s equations but DLL B must be able to execute a given equation from DLL A (DLL A implements some numerical, proximity methods and “tries” different values on a given equation as its steps towards desired mathematical solution) Now, DLL A must “know” DLL B and vise versa.

Is this a “bad” design? Can I think about such a code as amateurish cose? What would you do (please remember that DLL A implements general algorithm that are used by different other DLLs which implements mathematical equations)?

Thanks David

+1  A: 

There is no reason that DLL A needs to know explicitly about DLL B, it just needs an interface to be able to evaluate equations that it has been given to solve.

The interface could be specified by a function signature, a pointer to a suitable function would passed by B to A, or it could be presented as an abstract class and a pointer or reference to what is actually a derived instance could be passed from B to A.

It is usually not a good idea, although it is possible, to have mutually dependent dlls. In this case, I don't think that it is necessary.

Charles Bailey
A: 

That's a good question. Yes, that would be a bad design. Two solutions:

  • Put everything in the same DLL
  • Split A into two DLLs (one part which is used by B, and another part which uses B)

You might find more on this topic by Googling for 'cyclic' and/or 'acyclic' 'dependency'.

ChrisW
+1  A: 

It seems that A is an older dll, because it is used by many others. What seems bad is that you need to change A so that it call B explicitly, this might signals that you are going to do change again and again in A. This is bad news because changing A will require to rebuild all others dll.

You'll need to abstract how B execute equations or how A solve equations, by introducing a callback of any form that you give to B (virtual, callback, template ). I'll go for B since A is older and probably more used. Here is some pseudo code where Solver is a class of A, Equation is a class of B :

solver=new Solver;
equation=new Equation(&solver);

Solver needs to implement some sort of abstract interface. The best solution depends of your language and framework. Google for "dependency inversion principle" or "dependency injection".

Emmanuel Caradec
+1  A: 

This problem (circular dependency of implementations between packages) is exactly why the Dependency Inversion Principle (DIP) was created. Charles Bailey provided a solution that employs DIP to break the dependency. Instead of coupling two concrete classes directly to each other (classes or functions, it doesn't make much difference for applying DIP), couple them to an abstraction.

In this case, we have an interface that defines a "mathematical function" and that is consumed by the solver DLL. We also have an interface that defines "solver algorithms" and that is consumed by the function DLL. We have the concrete classes/functions depend on the abstractions. In C++ terms, these would be interfaces and when calling a method in the function DLL, the solver would pass itself to the function as a pointer to the "solver interface". The function calls methods on the solver interface to call back into the solver at the appropriate points. Similar, the solver has a class that takes a pointer to the mathematical function interface and it calls back into the function at the appropriate time.

Bob Martin's book Agile Principles, Patterns and Practices in C# covers these object-oriented design principles, including DIP. He has concrete examples that show these principles in practice. Although the book uses C# (the first edition used Java), the same principles and practices apply to C++.

legalize
A: 

This is not bad design if you are implementing a callback pattern where DLL A publishes a callback interface and DLL B implements the callback method according to the interface and supplies a pointer to it when calling A.

  • A progress callback for a long running function is a common usage of this pattern.
  • Cooperative co-routines like the solver, with the callback injecting candidate algorithms (matching possibly another interface) is another common example.
Jeff Leonard
It can be OK for the DLLs have a header file (e.g. an interface) in common, but it's terrible if there's a link-time inter-dependency: if A imports a function exported by B, while B imports a function exported by A.
ChrisW