Short answer - both MainSystem and TEvent must be either abstract classes or interfaces.
You people forget one important way to isolate classes: Events.
They are basically callback functions but the Pascal syntax makes them easy to use. They can use any function signature.
Every big well made Pascal program uses them and they are not restricted to GUI programming.
Nitorami, don't use name TEvent. FPC libs already has it and the name confuses with the whole event system naming conventions. Make it TEventOfNitorami if you must.
Anyway, here is a real event to be triggered when TNode changes:
type
TNodeChangedEvent = procedure(ANode: TNode) of object;
Then TNode defines:
OnChanged: TNodeChangedEvent;
.. and in the code somewhere:
if Assigned(OnChanged) then
OnChanged(Self);
TNode must be known by the MainSystem, an abstract base class if need be.
The MainSystem now can have a private:
procedure ResortList(ANode: TNode);
which is then given to a newly created Node instance, maybe like:
MyNode.OnChanged := @ResortList;
TNode does not need to know about MainSystem. It only knows that somebody wants to be notified when it changes.
See Lazarus IDE code for plenty more examples of using events.
This is standard problem, all programmers encounter sooner or later. Strange thing about Delphi/FPC - you can cross reference two classes, if they're declared in one unit, but can't, if they're declared in two different units. C/C++ is better in this case, cuz "#include MyUnit.h" - works differently. It doesn't try to also recursively include all units, MyUnit depends on - it just adds "flat" declarations to c/cpp file - all other work is being done by linker. It simply works as if two units would be merged into single one. In Delphi/FPC you can't do it - therefore large amount of extra work is needed to achieve the same result.
Amazingly the extra work improves code, I would say
always.
Some 8 years ago I used to think like you. I remember writing that the restriction of circular dependencies is a PITA and hinders serious development with Pascal.
At some point I realized that after an initial PITA the code becomes better and easier to maintain.
Pascal allows circular dependencies through implementation section, but your code becomes even better if you avoid them!
Think of design patterns. They are proven maintainable solutions for common design problems. There the lines between boxes represent dependencies. The lines always point to one direction. Design patterns never have circular dependencies! However code implementations in C or Java or other languages often have them which means they don't follow the diagrams they are based on. Allowing free circular dependencies encourages to write spaghetti code which is a real PITA in lots of legacy code made by many people over the years.
So, good code design boils down to one thing:
dependency management. Less dependencies means better code.