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