tags:

views:

381

answers:

6

Is there a way of getting around circular unit references in Delphi?

Maybe a newer version of delphi or some magic hack or something?

My delphi project has 100 000+ lines of code mostly based on singleton classes. I need to refactor this, but that would mean several months of "circular reference" hell :)

+7  A: 

Use the implementation section uses whenever possible, and limit what's in the interface uses clause to what has to be visible in the interface declarations.

There is no "magic hack". Circular references would cause an endless loop for the compiler (unit A requires compiling unit B which requires compiling unit A which requires compiling unit B, etc.).

If you have a specific instance where you think you cannot avoid circular references, edit your post and provide the code; I'm sure someone here can help you figure out how to get it fixed.

Ken White
+10  A: 
  1. It seems, you have quite serious code design issues. Beside many signs of such issues, the one is the unit circular references. But as you said - you cannot refactor all the code.
  2. Move all what is possible to IMPLEMENTATION section. They are allowed to have circular references.
  3. To simplify task (2), you can use 3d party tools. I would recommend - Peganza Pascal Analyzer (http://www.peganza.com). It will suggest what you can move to the implementation section. As will give you much more hints to improve your code quality.
da-soft
I do not agrre with the first point. Circular references which may appear in the refactoring of one unit into many units are not always a proof of bad design of the initial code. They are the effect of a compiler restriction, not meaning 'good' or 'bad' code. Example: using the Delphi 2009 Enterprise to create code for the GoF Visitor Pattern, all code is contained in one unit - if I try to split it up to units for model and visitor classes, I run into circular references. Is there a code design issue in the Visitor pattern then? (See http://stackoverflow.com/questions/2356318/)
mjustin
No, but the visitor pattern doesn't require multiple units either
Marco van de Voort
+17  A: 

I've been maintaining close to a million lines of legacy code for the past 10 years so I understand your pain!

In the code that I maintain, when I've encountered circular uses, I frequently have found that they are caused by constants or type definitions in unit A that are needed by unit B. (Sometimes it's also a small bit of code (or even, global variables) in Unit A that is also needed by unit B.

In this situation (when I'm lucky!) I can carefully extract those parts of the code into a new unit C that contains the constants, type definitions, and shared code. Then units A and B use unit C.

I post the above with some hesitance because I'm not an expert on software design and realize there are many others here who are far more knowledgeable than I am. Hopefully, though, my experience will be of some use to you.

Tom1952
+1. IMHO no need for hesitating here.
Ulrich Gerhardt
+1 I'm with Mr Gerhardt. No need for hesitating.
Fabricio Araujo
+1 An "AppTypes.pas" unit is a very common thing in Delphi apps.
cjrh
+1  A: 

Modelmaker Code Explorer has a really nice wizard for listing all the uses, including cycles.

It requires that your project compiles.

I agree with the other posters that it is a design issue.
You should carefully look at your design, and remove unused units.

At DelphiLive'09, I did a session titled Smarter code with Databases and data aware controls which contains quite few tips on good design (not limited to DB apps).

--jeroen

Jeroen Pluimers
+3  A: 

Similar Question: Delphi Enterprise: how can I apply the Visitor Pattern without circular references?

The solution presented by Uwe Raabe uses interfaces to resolve the circular dependency.

mjustin
+4  A: 

There is many ways to avoid circular references.

  1. Delegates. Way too often, an object will execute some code that should be done in an event instead than being done by the object itself. Whether it is because the programmer working on the project was too short on time(aren't we always?), didn't have enough experience/knowledge or was just lazy, some code like this eventually end up in applications. Real world exemple : TCPSocket component that directly update some visual component on the application's MainForm instead of having the main form register a "OnTCPActivity" procedure on the component.

  2. Abstract Classes/Interfaces. Using either of them allow to remove a direct dependance between many units. An abstract class or an interface can be declared alone in its own unit, limiting dependancies to a maximum. Exemple: Our application has a debug form. It has uses on pretty much the whole application as it displays information from various area of the application. Even worse, every form that allows to show the debug form will also also end up requiring all the units from the debug form. A better approach would be to have a debug form which is essentially empty, but that has the capacity to register "DebugFrames".

    TDebugFrm.RegisterDebugFrame(Frame : TDebugFrame);

    That way, the TDebugFrm has no dependancies of its own (Except than on the TDebugFrame class). Any and all unit that requires to show the debug form can do so without risking to add too many dependancies either.

There are many other exemple... I bet it could fill a book of its own. Designing a clean class hierarchy in a time efficient fashion is pretty hard to do and it comes with experience. Knowing the tools available to achieve it and how to use them is the 1st step to achieve it. But to answer your question... There is no 1-size-fit-all answer to your question, it's always to be taken on a case by case basis.

Ken Bourassa