Jump to content
Mike Torrettinni

Suggestion on avoiding unit cycling and still keep code organized

Recommended Posts

I have a few global units that use most of the project Forms, units to run code that needs to be run on all Forms, units, like:

TranslatorUnit - translates project

SettingsUnit - read/save user, project settings

PrepareForNewProjectUnit - clear current working project state and initialize project to work on new data - like restarting project, but you don't need to exit and re-open it to work on new data

ReleaseManagerUnit - version info related methods

 

What these units all have in common is that they all use most of the Forms, units so they can change Form controls, data records, global classes/vars as needed. But also have methods to do same or similar things on demand!

Like this:

TranslatorUnit translate everything with TranslateProject, and also has a TranslateControl method that gets called by some Forms while user is using the project, so this unit needs to use the TranslatorUnit. So we have direct unit dependency, cycle.

 

I like this design because everything related to a specific area (translation, settings...) is organized in each own unit. But this unit cycling seems to be forcing me to go back to old days, when I had everything specific to each Form, unit within that unit and I had to call TranslateMainForm, TranslateForm2, Read/Save settings MainForm, read/save settings Form2...

 

So, it comes down to: organized unit cycling vs 'spread the code around' non-unit cycling solution.

 

Any thoughts on this? (if my explanation makes sense)

 

 

 

Share this post


Link to post

Unit dependency cycles are bad. Period.

 

One possibility to consider is that of placing all of the units you access into the implementation section. So, for example, if your TranslatorUnit is laid out something like this:

 

unit TranslatorUnit;

interface

  procedure TranslateAll(const ALanguage: TapLanguage);

 

implementation

  uses

    Unit1, Unit2; // your list of used units here

 

  procedure TranslateAll(const ALanguage: TapLanguage);

  begin

      // code here to perform the translation work.

  end; 

 

end.

 

The basic idea is that none of your application units need to be in the interface section. They need to be referenced by the translation routines, but if they are not specified in parameters of interfaced routines, can be declared in the implementation only. I have a module which is built in exactly this way. Although many of the units in the uses clause remain tangled in unit dependency cycles, this new unit participates in -- and contributes to -- zero dependency cycles.

  • Like 1

Share this post


Link to post

This is not working if I need to use any methods from TranslatorUnit in Unit1 or Unit2. MMX Unit dependency analyzer shows cycle: Unit1 -> TranslatorUnit -> Unit1, even if all uses are in Implementation section.

Edited by Mike Torrettinni

Share this post


Link to post
17 minutes ago, Mike Torrettinni said:

This is not working if I need to use any methods from TranslatorUnit in Unit1 or Unit2. MMX Unit dependency analyzer shows cycle: Unit1 -> TranslatorUnit -> Unit1, even if all uses are in Implementation section.

I can't switch projects right now, but in my case, I have a module which follows the form I outlined above from memory, and with over 100 units in the implementation uses clause, and all of them referenced by code in the implementation section, MMX reports no cycles.

Share this post


Link to post

 

31 minutes ago, Bill Meyer said:

I can't switch projects right now, but in my case, I have a module which follows the form I outlined above from memory, and with over 100 units in the implementation uses clause, and all of them referenced by code in the implementation section, MMX reports no cycles.

When you have a chance, please check.

Share this post


Link to post
On 7/26/2019 at 3:32 PM, Mike Torrettinni said:

So, it comes down to: organized unit cycling vs 'spread the code around' non-unit cycling solution.

 

Any thoughts on this? (if my explanation makes sense)

 

 

 

To avoid unit cycles your form units should not depend on a unit which actually translates anything. Them should depend on some interface ITranslator which have methods TranslateProject and TranslateControl.

Each unit create the implementation on demand with some kind of factory. E.g. Translator := TFactory.CreateTranslator();

The factory may be implemented as a simple container for implementations. E.g. actual implementation class (which depends on all forms) at some point during initialization registers itself as implementation of ITranslator interface.

With this design you don't have unit dependency cycles. In real life case it's better to use some DI container for this instead of creating it by yourself.

Quote

This is not working if I need to use any methods from TranslatorUnit in Unit1 or Unit2. MMX Unit dependency analyzer shows cycle: Unit1 -> TranslatorUnit -> Unit1, even if all uses are in Implementation section.

This is because there is a cycle indeed. You cannot declare mutual units dependency in interface sections. At least one unit should use the other from implementation section. Otherwise the code will not compile.

Edited by Georgge Bakh
  • Like 1
  • Thanks 1

Share this post


Link to post

How I solved this specific situation is that I put TranslateForm into form Create constructor. So, for now I like this situation:

- TranslatorUnit doesn't reference any units anymore

- one call of TranslateProject is replaced by TranslateForm(Self) call in overridden Form constructor Create - so, still 1 line of code in 1 place! even though is executed on each Form Create

- on demand calls like TranslateControl can be called from any form

- no more unit dependency cycles (with TranslatorUnit)

 

result: organized code without unit dependency cycling 🙂

 

Thanks to all!

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×