One of the issues I've encountered recently is code like this:
type ISomeIntf = interface procedure SomeMethod; end; TSomeClass = class(TInterfacedObject, ISomeIntf) private FValue: Integer; FSomeValue: string; public constructor Create; procedure SomeMethod; end; var SomeClass: TSomeClass; procedure DoSomething(ASomeIntf: ISomeIntf); begin ASomeIntf.SomeMethod; end; procedure Setup; begin SomeClass := TSomeClass.Create; end; procedure InAnotherRoutine; begin DoSomething(SomeClass); end; procedure DoAnotherThing; begin SomeClass.SomeMethod; // that references Self end; { TSomeClass } constructor TSomeClass.Create; begin FValue := 12345; FSomeValue := 'Some important stuff'; end; procedure TSomeClass.SomeMethod; begin WriteLn(FValue); WriteLn(FSomeValue); end; begin Setup; InAnotherRoutine; DoAnotherThing; InAnotherRoutine; end.
What happens when the call to DoSomething in AnotherRoutine finishes, and a call is made to DoAnotherThing, which calls SomeMethod? Copy and paste the above code into a new console program, and find out by running it!
In 2017, with the release of Delphi 10.2 Tokyo, Embarcadero introduced a specialized implementation of the Observer pattern into the System.Classes unit. While it has been in the wild for 9 years, it remains a "hidden" architecture for many, primarily because it serves as the invisible engine behind LiveBindings. Other than live bindings, you can also use the Observer pattern as a way to update component settings to the Windows registry, an .ini file, or persist it elsewhere.
System.Classes