Choosing object-oriented programming means leaving some visual programming practices behind you.
One of the unique features of Delphi is that it is at the same time a visual and an object-oriented development environment. In fact, every type of component (visual or not) is implemented as a class. Every time you define a new form, you are creating a new class. Every component on the screen and every form is an instance, an object of the component or form class. Delphi's component model was the first to bridge object-oriented programming and visual programming. JavaBeans borrowed a lot from this model. And as for the Microsoft visual tools, not one comes close to this integration.
In Delphi there isn't even a clear-cut distinction between classes and components. Technically, components are objects of classes inheriting from the TComponent class, one of the topmost classes of the VCL hierarchy. As you probably know, inheriting from TComponent is required to install your class or component on the Delphi palette and manage it at design time. At runtime, all objects (components or not) are treated equally.
What are the practical implications of the fact that a component is an object? It means that you can do in code whatever you do in the development environment. At run time, using Object Pascal code, you can dynamically create new components, place them on forms, and hook into event handlers you've written. In other words, you can use two totally different approaches to place a button on a form. You can drag the button icon over the form at design-time in the Delphi IDE, or you can write a couple of lines of code to do the same thing. The simple CreateC example in my Mastering Delphi 5 book illustrates the idea.
The visual approach is certainly easier and faster, but writing code is more flexible and powerful. There is no way to create programs in Delphi without using objects. It's impossible. But it's also easier than you might think to screw it up. The problem is that to simplify coding, Delphi's visual programming model favors some bad practices. In fact, Delphi has many default behaviors, which are good for beginners and produce applications rapidly, but that can be harmful in complex examples because they violate a key tenet of object-oriented programming: encapsulation.
The most obvious default to avoid is the automatic creation of all the forms when the program starts. I've seem programs that were having resource problems because they created as many as 300 forms at startup! In Delphi 5 there is now an environment setting you can use to turn off this default. Use it. But there is a related problem that has to do more with object-orientation: the definition of a global variable for forms.
This default is OK for simple programs but a drawback in larger applications. The fact that the system automatically declares a global variable for each class is handy but confusing. Often Delphi newcomers consider Form1 and TForm1 to be the same thing. They'll refer to the specific object (Form1) inside the code of the class methods, and that prevents them from creating multiple copies of the form. Even worse, they refer to specific global objects of other form classes. I know this often saves coding time, but it's a poor practice and increases coupling.
Publishing a component's fields inside a form can also cause problems because, again, you are increasing coupling. Although the fields associated with a form and the fields associated with a component are related, they are two separate things. Mixing them up, a common error, produces nasty effects. My rule is never to refer to components of one form class from another one. How do you accomplish this? You write properties or methods for the form that then communicate with the components the form hosts. Components should be encapsulated, or every change in your user interface will result in a nightmare.
Here's an example. Suppose you have a main form with a
status bar hosting a status message. From other forms you can
display messages by
Form1.StatusBar1.Panels.Text := 'my
What if you change the user interface, moving the message to a different panel of the status bar? What if you replace the status bar with another component, or you want to log messages, or make any other change in this structure? You'll have to change this code, which probably appears in 378 different places throughout all the units of the program. My suggestion? Add a Message property to the form and display the message to the status bar when the property value changes. Take a look at HideComp which totally removes published fields.
The examples I've mentioned so far are only the tip of the iceberg; there are far more techniques for producing programs that conform to the principles of object-oriented programming using Delphi. I've provided a more complete and detailed list of good object-oriented programming rules in an article published in the July 1999 issue of The Delphi Magazine, if you can find a copy (unfortunately the article isn't available on-line). Besides the examples mentioned above, I suggested two other techniques in the article: leveraging visual form inheritance and writing custom components to share code.
Besides using components and objects according to object-oriented principles , the application's overall structure should also be object-oriented. This means giving careful consideration to managing and operating on data in an object-oriented manner and thinking about where business logic is implemented. I'm getting more and more involved in these architectural questions, and I'll let you know my findings in a future column. If you are interested now, a good starting point for your research is Scott Ambler's web site.
In my writings and public presentations, I devote a lot of time to addressing effective object-oriented practices in Delphi and invariably find out that for a lot of Delphi programmers this is a totally new concept. The fact that the IDE favors a more na´ve programming approach doesn't help. Visual programming in Delphi is good, but you should move to real OOP if you want to unleash Delphi's power.