Question

For each setter of a class I have to implement some event logic (OnChanging, OnChanged):

procedure TBlock.SetWeightIn(const Value: Double);
var OldValue: Double;
begin
  OldValue := FWeightIn;
  DoOnChanging(OldValue, Value);
  FWeightIn := Value;
  DoOnChanged(OldValue, Value);
end;

procedure TBlock.SetWeightOut(const Value: Double);
var OldValue: Double;
begin
  OldValue := FWeightOut;
  DoOnChanging(OldValue, Value);
  FWeightOut := Value;
  DoOnChanged(OldValue, Value);
end;

Can you please suggest a way to implement this without duplicating all these lines for each setter?

Was it helpful?

Solution

Try this:

procedure TBlock.SetField(var Field: Double; const Value: Double);
var
    OldValue: Double;
begin
    OldValue := Field;
    DoOnChanging(OldValue, Value);
    Field := Value;
    DoOnChanged(OldValue, Value);
end;

procedure TBlock.SetWeightIn(const Value: Double);
begin
    SetField(FWeightIn, Value);
end;

procedure TBlock.SetWeightOut(const Value: Double);
begin
    SetField(FWeightOut, Value);
end;

OTHER TIPS

Delphi supports indexed properties. Multiple properties can share a single getter or setter, differentiated by an ordinal index:

type
  TWeightType = (wtIn, wtOut);
  TBlock = class
  private
    procedure SetWeight(Index: TWeightType; const Value: Double);
    function GetWeight(Index: TWeightType): Double;
  public
    property InWeight: Double index wtIn read GetWeight write SetWeight;
    property OutWeight: Double index wtOut read GetWeight write SetWeight;
  end;

You can combine this with Cobus's answer to get this:

procedure TBlock.SetWeight(Index: TWeightType; const Value: Double);
begin
  case Index of
    wtIn: SetField(FWeightIn, Value);
    wtOut: SetField(FWeightOut, Value);
  end;
end;

This might give you ideas for other ways you can refer to your fields by index instead of having two completely separate fields for such related values.

You could add an additional method. Something like:

procedure TBlock.setValue(const Value : Double; Location : PDouble);
var
  OldValue : Double;
begin
   OldValue:=Location^;
   DoOnChanging(OldValue,Value);
   Location^:=Value;
   DOnChanged(OldValue, Value);
end;

procedure TBlock.setWeightOut(const Value : Double);
begin
  setValue(value, @FWeightOut);
end;

I haven't compiled / tested the code though. The idea is to have a general setter method that works with a pointer to the location. The specialized versions just call the gerneral method with the adress of the variable to be set. You have to have one type of general setter method per type of variable (double, integer, ...) though. You could modify it to work on Pointer and length of a variable to work with all types - your decision if it's worth it.

if procedure/function's parameters are same and code between begin and end are same then you can just use

procedure SetWeightValue(const Value: Double);
var OldValue: Double;
begin
  OldValue := FWeightIn;
  DoOnChanging(OldValue, Value);
  FWeightIn := Value;
  DoOnChanged(OldValue, Value);
end;

That's it...

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top