What you're pointing to here is a form of dependency injection (DI): you have a form that depends on an external object for correct operation. The obvious approach people take is to add a parameter to the Form's constructor. But what David said above is correct: you cannot simply ignore the 'AOwner' parameter - another value that's already required to be injected by the VCL framework.
When you create a form, it rarely lives in isolation -- only the Main Form works that way most of the time. Forms are typically used to do one of three things: (1) collect and return new data; (2) accept data for editing and return the edited data; (3) accept some data that's used to make a selection and return the selection in some fashion.
When you use data-aware controls, the forms can appear to be self-sufficient; in reality, the VCL framework is injecting (behind the scenes) all of the data-aware control mechanisms. If you don't set up the table and field data in the IDE, then you DO have to explicitly inject all of that when you create the form, although most people don't do it that way. (I mean, why bother using data-aware controls otherwise?)
If instead you make forms that do not or cannot use data-aware controls (eg., because they get their data from some other type of external interface that has no TDataset defined for it), then you need to inject the initial field values first, then fetch updated values afterwards.
That's fine if you're working with a few values -- that's when someone will usually put them in the constructor. But if you've got a bunch of them (think, say, 50), what do you do? You can't put them all on the constructor!
To make matters worse, you may need to fetch updates. If you inject them through the constructor, then you need to make some of them 'var' parameters. This is a very poor, albeit common, design practice.
So the next thing people will do is think, "Oh, I'll put all of those values into an object and then pass the object through the constructor!"
But many times those values already live inside of other objects, meaning you've just doubled your work, because now you need to pass them into and fetch them from your surrogate object -- which you created, ironically enough, in order to simplify passing them into and fetching them from the form!
Note that this approach we've been discussing is, in DI terms, called "constructor injection". Obviously, it has its limits.
An alternative is what's called "property injection" or "setter injection".
The main advantage of "constructor injection" is that the values are all set up and ready to go when the object's construction is complete. With "property/setter injection" you construct the object, THEN inject the dependent values, and then show the form. After the form has closed, you extract updated values from its properties (or getters), and then free the form if needed.
In Delphi, you may need to override the OnClose method of such forms to set the Action to caHide so it doesn't free the form when it closes. (That may be the default; I can never seem to remember.)
Here's what the code looks like, generally speaking, with constructor injection :
myobj := TMyObject.Create(); // an object with initial values to feed the form
myobj.intval := 10;
myobj.str := "hello world!"
myform := TMyForm.Create( self, myobj );
// form could be freed here if OnClose's Action has been set to caFree
// this means myform is no longer valid
// however, it may be <> NIL even if it HAS been freed!
// if not freed, then free it
// now do something with the contents of myobj
if (myobj.intval <> 10) then
. . .
if (myobj.str = '') then
. . .
Here's what property injection looks like, assuming there are individual properties with setters and getters (read/write methods) for each of the fields in the form -- intval and str in this case:
myform := TMyForm.Create( self );
myForm.intval := 10;
myform.str := "hello world!"
myform.ShowModal(); // ensure the form is HIDDEN when it closes, not freed!
// now do something with the field values -- myform must still be valid!
if (myform.intval <> 10) then
. . .
if (myform.str = '') then
. . .
Generally speaking, property injection can be used consistently for virtually all forms in an application. The constructor injection approach almost always starts out passing a few variables on the constructor and evolves to use an object, and then that object starts to take on a life of its own independently, increasing the maintenance costs of the form.
The property injection approach can also use an object (several, in fact!), but in this case it's whatever object(s) already exist. You don't need to create new surrogate objects to ferry the data values in and out of the form. Instead, you simply add some properties and access them from outside.
If you want to implement some business logic and validation in these forms, you can do it in the OnCreate/OnDestroy for constructor injection and OnActivate/OnClose for property injection (but ensure that the OnActivate only executes once).
I maintained a system for a while that had >200 forms. They used all kinds of variations on constructor injection. I also discovered lots of subtle bugs in them because there was so little consistency from one form to the next in terms of how they did stuff. The biggest problem was that they passed lots of external objects from other units into the constructor, which created tight coupling between the forms and these external units. We'd change a property in one of these objects and get errors in a half-dozen forms! Worse, there would be errors in other forms that would show up later that the compiler couldn't detect.
In several cases, they even employed an explicit TObject constructor (ie., just a constructor Create(); override; method in several forms because they couldn't figure out how to get the form's constructor to initialize things in the correct order!
There were even situations where the code created an instance of the form and then proceeded to write values directly into the controls (TEdits, TRadioButtons, etc) on the form! That's a MAJOR NO-NO!!!
There are people who think you should ALWAYS use constructor injection. While I have no particular axe to grind in that respect, I can only reflect on how that tends to occur in practice, which is ... it's dangerous and leads to far too much tight coupling between unrelated units -- at least in so far as how Forms are employed in large Delphi apps.
I've seen lots of "coding standards" used on projects, and I don't ever recall seeing one that standardized on when and how to use constructor vs. property injection when working with non-data aware forms. Something to think about on your next (or current) project.
thanks...its a very nice description! :)