5
votes

So I'm kinda new to OO programming. Delphi has properties which are intended to be a "more elegant" way to acess class data than getters/setters (read it here usage of property vs getters/setters in business classes).

When should I use the fields directly and when should I use getters/setters inside the property? I'm gessing only when the data needs to be manipulated, but I'm not sure.

Edit:

Is it wrong to omit a setter that does nothing but return the value of the field itself?

  property Field :integer read FField write FField;
3
There are four questions here, which is three too many. And even then, many of the questions are really asking for subjective opinion. I think you need to narrow the focus somewhat. - David Heffernan
Thx I'll think of a better way to put it - rodolfoThePig
And one question is really two questions in itself. - Jerry Dodge
There is no real difference in using setter/getter or a property which is just a kind of wrapper for both. The getter/setter methods are normally declared as private but get exposed public by a public property. So using properties is just to get the class layout cleaner - Sir Rufo

3 Answers

14
votes

Why properties?

For starters, a quick summary from the documentation on properties:

A property, like a field, defines an attribute of an object. But while a field is merely a storage location whose contents can be examined and changed, a property associates specific actions with reading or modifying its data. Properties provide control over access to an object's attributes, and they allow attributes to be computed.

Why not only setter and getter?

Separation of storage and access can indeed be achieved by the use of just getters and setters, and leave the property be. That is true, but the question you link to originates from a language difference: Delphi does have properties, and the answers there already explain why to use them. The two most obvious reasons are (1) cleaner code and (2) assignment capability. I think this answer deals with it quite extensive already.

Furthermore, without the use of properties, getters and setters are always needed, where with properties they are not. Assume a setter implementation, but no getter: a property can directly read the field.

Class completion

When you just declare a property's name and its type, Delphi's class completion defaults to reading a private field and a private setter which sets the private field. Mind that this is just the default configuration, which you again can modify to your needs. When you fully specify the property declaration, class completion will comply and add a private field, a getter and/or setter as required for your declaration.

Properties without getter and setter

Is it wrong to omit a setter that does nothing but return the value of the field itself?

When a property has no getter nor setter and it just reads and writes the field, then you could conclude that there is no difference beside being consistent. But that is not the case. The field and the property have distinct names, and therefore possibly distinct meaning. Meaning that you can give. See Using properties instead of fields in class methods of the same unit is a bad practice?.

When to use a getter or setter?

... I'm gessing only when the data needs to be manipulated ...

Well, that is partly true. Manipulation is one of the many reasons. Consider a Price property of type String, and its private field FPrice:

  • Limitation: When the price needs to be equal or higher than zero,
  • Delegation: When FPrice is part of another field, or when it otherwise is beyond the responsibility of this class,
  • Verification: When the price may only have two decimals behind the comma,
  • Interpretation: When the price is entered in thousands, but should be stored in cents,
  • Effect: When the price has consequences for another field, for example rate or margin,
  • Activation: When an edit to the price requires immediate action, for example updating the GUI,
  • Conversion: When the price is entered in dollars but should be stored in yen,
  • Cancelation: When the price does not make sense, for example when it is entered in scientific notation.

Note that a Price property is quite rudimentary. Leaving its setter or getter for future implementation is very well possible. But imagine more advanced properties which cannot do without setter or getter:

  • A field that needs to be created before consulting:

    function TMyObject.GetBarelyUsed: TRare;
    begin
      if FBarelyUsed = nil then
        FBarelyUsed := TRare.Create(Self);
      Result := FBarelyUsed;
    end;
    
  • An item can be selected, but the item itself does not know what to do. Instead the owner does. Notice the complete absence of the private field in this case:

    procedure TItem.SetSelected(Value: Boolean);
    begin
      if Value <> Selected then
      begin
        if Value then
          Owner.Selection.Add(Self)
        else
          Owner.Selection.Remove(Self);
      end;
    end;
    
  • An image control, specialized in viewing your own image format. Assignment of the FileName property involves: checking the correct file extension, checking file existance, storing the file name in the private field, loading the file, adjusting the pictures' dimensions, or else undoing the previous assignment:

    procedure TAwDxfImage.SetFileName(const Value: TFileName);
    begin
      if FFileName <> Value then
        if SameText(ExtractFileExt(Value), '.' + SDxf) and
          FileExists(Value) then
        begin
          FFileName := Value;
          FGraphic.LoadFromFile(FFileName);
          FGraphic.SetBounds(Width, Height);
        end
        else
        begin
          FFileName := '';
          FGraphic.Clear;
        end;
    end;
    

Source: NLDelphi

4
votes

In addition to @NGLN answer there is another use case for property getter/setter.

Accessing class instances through interface is only possible via instance methods. If you have to access property in such case you have to implement getter/setter methods.

type
  IField = interface
    function GetField: integer;
    procedure SetField(value: integer);
    property Field: integer read GetField write SetField;
  end;

  TField = class(TInterfacedObject, IField)
  protected
    FField: integer;
    function GetField: integer;
    procedure SetField(value: integer);
  public
    property Field: integer read GetField write SetField;
  end;

var
  f: IField;
  x, n: integer;
...
  f := TField.Create;
  f.Field := 5;
  f.SetField(6);
  n := f.Field;
  x := f.GetField;

Of course, depending whether you need only read or write access to that property you can omit setter or getter in your interface declaration.

Keep in mind that accessing instance through interface gives all interface implemented methods public visibility. That is why in above example you can call f.GetField despite it being declared protected (or even private).

2
votes

What is unacceptable is to do this

TMyClass = class
private
public
  Fubar :integer;
end;

the remainder of your examples are fine. I would be happy to receive code from you that reads like this

TMyClass = class
private 
  FFu : integer;
public
  property Fu :integer read FFu write FFu;
end;

because I can confidently change it to

TMyClass = class
private 
  FFu : integer;
  procedure SetFu(Value : Integer);
  function GetBar() : string;
public
  property Fu :integer read FFu write SetFu;
  property Bar : String read GetBar;
end;

without breaking existing code.

I have a personal dislike of setters that do nothing

procedure TMyClass.SetFu(Value : Integer);
begin
  FFu := Value;
end;

But in truth it is harmless.

Does that help?

This would be a valid reason or incentive for using a setter

procedure TMyClass.SetFu(Value : Integer);
begin
  if FFu <> Value then begin  
    FFu := Value;
    if Assigned(FAfterFooChanged) then
      FAfterFooChanged(FFu);
  end;
end;

not 'manipulation' as such ...