views:

297

answers:

5

A colleague of mine and I have been discussing how to declare variables in a function.

Let's say you have a class called TStrings (using Delphi for the sake of explanation) that has at least one abstract method and a descendant class called TStringList which obviously implements the abstract method, but it introduces nothing else you need that is not already implemented in the ancestor, how would you declare a function variable of type TStringList?

Here are two examples. Which is considered better practice and why?

procedure AddElements;
var
  aList: TStringList;
begin
  aList := TStringList.Create;
  try
    aList.Add('Apple');
    aList.Add('Pear');
  finally
    aList.free;
  end;
end;

procedure AddElementsII;
var
  aList: TStrings;
begin
  aList := TStringList.Create;
  try
    aList.Add('Apple');
    aList.Add('Pear');
  finally
    aList.free;
  end;
end;
A: 

My vote is the second form - the idea being that TStrings defines a contract/interface and its better to code to them.

Chris Kimpton
+1  A: 

It is a TStringList, so you should also declare it as TStringList (first example). Everything else could confuse you or others that read the code later.

schnaader
A: 

I'd say that it depends on whether you expect if the TStringList might be changed to something else that implements TStrings or not. If you don't expect it to change, use TStringList and gain access to special features that are in TStringList alone (guess this is not the case). If you expect it might change declare it as TStrings and stick to the "safe" methods.

In this specific case I'd say it doesn't matter. Hell, you could probably change the variable declaration and nothing would change anyway. So use whichever you like best - it's a question of preference.

Vilx-
A: 

I agree with Schnaader.

TStringList has more properties and methods that TStrings (which is an abstract class). Using the TStrings variable prohibits the use of these members unles you are using casts. But that is making things worse in my opinion.

You can use TStrings in a function argument.

procedure TMyClass.MyMethod(const AList: TStrings);
begin
end;

Or as a property. But local variables and fields are more versatile if they are decalared their real type.

Gamecat
A: 

It depends...

In Java, I often saw the recommendation of making declarations using the highest abstraction level that is usable, although it generally apply to interfaces.

For example:

Collection list = new ArrayList();
[loop] list.add(someItem); [end loop]

etc.
Why? It allows to change implementation (a detail in some cases: some implementations are better suited to some usages (queue, linked list, stack...) so it might be mostly a speed/memory concern) by minimizing the impact of the change.

Of course, if you use methods specific to an implementation, you must be more specific in the declaration.

Another advantage: when a method expect a Collection parameter, it can work on a broader range of input as long as it needs to use only generic methods.

PhiLho