4
votes

Just recently, probably because I've been maintaining some old code, I've started to look at how / why I do things. As you do.

Most of my Delphi programming has been picked up in house, or from examples scattered across the web or manuals. And in some things are done just because "that's how I do it"

What I'm currently wondering about is Declaration, of variables, procedures, functions, etc.

When I am working with a form, I will place all my procedures and functions under public or private. Whilst I will try to avoid global vars and constants will generally go under var or const, either in the interface or implementation, depending on where they need to be called (occasionally though they will go in public / private)

Otherwise, if its just a unit I will declare the procedure in the interface and use in the implementation. Some of the code I've been maintaining recently has no interface declaration but instead has everything properly ordered with calls after procedures...

Is there a correct way to do this? Are there rules of what should / should not go in the class? Or is it a style / when you started thing?

Edit to add

My question is not about whether a declaration of a procedure goes in private/public but whether all declarations in a TForm Unit should go in one of these. Similarly should var / const be in one or the other?

Further clarification

I understand that not declaring in interface, or declaring in public/private/etc affects the visibility of procedures/functions to other units in my applicaiton.
The core of my question is why would i not want to declare? - especially when working in a form/unit when placing in private is much more explicit that the thing declared is not available to other units...

Cheers Dan

2
I don't understand your edit. Could you try to clarify further? The first decision you have is: class member or not (I tried to answer this)? If you have decided that, the next decision is: strict private, private, protected, public or published (I gave some general rules for that). Or what do you want to know exactly?jpfollenius
I suspect that what I groping for (and it's instances like this where my lack of "formal" training lets me down) is where the decision is made be class member or not...Dan Kelly

2 Answers

4
votes

The question seems to deal with scope. In other words, how easily accessible things can or should be.

As a general guideline, you want to reduce the scope of things as much as possible but still keep them accessible enough to be reused. The reason for this is:

  • that as your system grows and becomes more complex, the things that have are larger scope are more easily accessible.
  • as a result, they are more likely to be reused in an uncontrolled fashion.
  • (sounds great) but the problem comes when you want to make changes, and many things use that which you want to change...
  • it becomes far more difficult to make your changes without breaking something else.

Having said that, there is also a distinction between data (variables, constants, class fields, record attributes) and routines (functions, procedures, methods on classes). You'll want to apply the guidelines far more strictly to data because 'strange use' of data could interfere with some of your routines in highly unexpected and hard to debug ways.

Another thing to bear in mind is the special distinction between global variables and class fields or record attributes:

  • using global variables there is only one 'value' (term used loosely) for the entire application.
  • using class fields or record attributes, each new instance of the class or record has its own values independent of other instances.
  • This does seem to imply that you could use some form of global whenever your application only needs one thing. However, as alluded to earlier: this is not the only reason to avoid globals.
    • Personally I even tend to avoid global routines.
    • I'm frequently discovering that things that seemed okay declared global are not as universal as first thought. (E.g. Delphi VCL declares a global Screen object, I work on 2 screens; and many of our clients use 4 to 6.)
    • I also find it useful to associate routines that could have been global with specific classes as class methods. It generally makes it easier to understand the code.

So listing these 'locations' from largest scope to smallest, you would generally strive to pick locations lower down in the list (especially for data).

  • interface global
  • implementation global
  • interface threadvar
  • implementation threadvar
  • published (Note I don't really consider this to be a scope identifier; it's really public scope; "and includes RTTI information" - theoretically, it would be useful to also mark some private attributes as "include RTTI".)
  • public
  • protected
  • private
  • strict private
  • local variable

I must confess: what I have presented here is most certainly an over-simplification. It is one thing to know the goals, another to actually implement them. There is a balancing act between encapsulation (hiding things away) and exposing controlled interfaces to achieve high levels of re-usability.

The techniques to successfully balance these needs fall into a category of far more complicated (and sometimes even contentious) questions on system design. A poor design is likely to induce one to expose 'too much' with 'too large' a scope, and (perhaps paradoxically) also reduce re-usability.

6
votes

Everything that can have a different value depending on the concrete instance belongs to the class, i.e.

TDog = class
strict private
  FColor : TColor;
  FName : String;
public
  property Color : TColor read FColor write FColor;
  property Name : String read FName write FName;
end;

Color and name are clearly attributes of each dog (and each dog will have other values here).

General rules:

  • Fields belong in private (visible in this class and in this unit) or strict private (visible only in this class)
  • If you need access to fields from other classes, create a public property. This gives you the freedom to change the simple field access to a more sophisticated getter / setter method lateron without changing the interface of your class.
  • Everything should be as local as possible. If private is enough, there's no need to make it protected (visible in subclasses too). And only make those things public that you really need from the outside.
  • Forms: only those things that you want to be stored in the DFM file should be published.
  • Put as much as you can in the implementation section and as little as you can in the interface section. This is also true for uses clauses.

You might be confusing the term global variable. If it's declared in a class it's not a global variable (even if declared public). Global variables (which you correctly consider good to avoid) always go in a var section either in the interface or the implementation section (which is preferrable following the general rules above)