9

I am having a bit of trouble designing a Class. Creating a Customer Class looks straightforward:

TCustomer = Class
  private
    FIdNumber: String;
    FName: String;
    procedure SetName(const Value: String);
    procedure SetNumber(const Value: String);
  public
    Property Name : String read FName;
    Property IdNumber : String read FIdNumber;
    Constructor Create(Number, Name  : String);
end;

constructor TCustomer.Create(ANumber, AName: String);
begin
   SetName(AName);
   SetNumber(ANumber);
end;

MyCustomer := TCustomer.Create('1', 'John Doe');

But well my customer has more properties: where he lives, date of birth etc. etc.

TCustomer = Class
  private
    {..snip snip..}
  public
    Property Name : String read FName;
    Property IdNumber : String read FIdNumber;
    Property Street : String read FStreet;
    Property HouseNumber : Integer : read FHouseNumber;
    ..
    ..
    ..
    Constructor Create(Number, Name, Street : String; Housenumber : Integer .. .. ..);
end;

As you see I end up with a constructor with a whole lot of arguments. What is a better way to construct a object with a lot of properties?

Pieter B
  • 1,874
  • 10
  • 22
  • 4
    My initial thoght is to construct the class only with the *essential* properties that maintain the class instance creation. for example in your case it might be a unique `UserID`. – kobik Mar 06 '13 at 15:52
  • 2
    You can make an analogous of your case to a creation of a new DB record (`insert`). Think what properties are *required* (use them in the constructor arguments) and which properties has defaults (initialize them inside your class constructor). – kobik Mar 06 '13 at 16:02
  • 1
    you can define a **constructor** that takes an array of pair and using RTTI, you parse the TCustomer Class and set field values from parameters, i.e. TMyCustomer.Create([AProp('FirstName', 'Joe'), AProp('LastName', 'Doe'), ...]); –  Mar 06 '13 at 16:06
  • 2
    Your question seems to rely on the premise that a constructor with lots of arguments is bad. What problem do you have with that? Is it that it's hard to tell which positional argument is which, and you want *named* arguments? – Rob Kennedy Mar 06 '13 at 16:28

1 Answers1

15

If a certain class needs to have many fields, I would make a constructor with only mandatory parameters and the rest I would keep in writeable properties:

type
  TCustomer = class
  private
    FName: string;
    FIdNumber: string;
    FStreet: string;
    FHouseNumber: Integer;
  public
    // constructor is empty or just with mandatory parameters
    constructor Create;
    // properties are writeable
    property Name: string read FName write FName;
    property IdNumber: string read FIdNumber write FIdNumber;
    property Street: string read FStreet write FStreet;
    property HouseNumber: Integer read FHouseNumber write FHouseNumber;
  end;

This of course depends, if you can expose those properties so to be writable, but the usage looks in my view better than constructor with many parameters:

var
  Customer: TCustomer;
begin
  Customer := TCustomer.Create;
  Customer.Name := 'Name';
  Customer.IdNumber := 'ID number';
  Customer.Street := 'Street';
  Customer.HouseNumber := 12345;
end;
TLama
  • 75,147
  • 17
  • 214
  • 392
  • 6
    +1 just to mention we can make several `overload`s of the `constructor` if needed ("constructor is empty or just with mandatory parameters") – kobik Mar 06 '13 at 16:23
  • Fluent-style property setters may be used as well, though simple with/do would probably provide for equally concise and more safe code – Arioch 'The Mar 07 '13 at 09:07