BorlandTalk.com Forum Index BorlandTalk.com
Borland discussion newsgroups
 
Archives   FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Managing unique IDs
Goto page 1, 2  Next
 
Post new topic   Reply to topic    BorlandTalk.com Forum Index -> Delphi OO design
View previous topic :: View next topic  
Author Message
Ivo Bauer
Guest





PostPosted: Wed Jan 31, 2007 9:12 am    Post subject: Managing unique IDs Reply with quote



Hi!

I'm designing a "database" of properties of chemical substances. Every
chemical substance needs to be described by properties like density,
molecular mass and so on. For this purpose I wrote class named
TSubstance. Now I need to hold a list of these TSubstances that is
meant to be a "database" (TSubstanceDatabase) where each substance has
to be uniquely identified by its name (string).

I'm not sure how to design this feature in an OO way. If I make the
UniqueName property of TSubstance, how do I ensure that it is really
unique in the context of TSubstanceDatabase when TSubstance is
supposed to know nothing about the TSubstanceDatabase where it
belongs? As for now I decided to go the other way round.
TSubstanceDatabase does not hold a list of TSubstance references
directly but rather TSubstanceItem references:

TSubstanceItem = class
private
FSubstance: TSubstance;
FName: string;
public
constructor Create(const AName: string);
destructor Destroy; override;
property Name: string read FName;
property Substance: TSubstance read FSubstance;
end;

constructor TSubstanceItem.Create(const AName: string);
begin
FName := AName;
FSubstance := TSubstance.Create;
end;

destructor TSubstanceItem.Destroy;
begin
FSubstance.Free;
inherited;
end;

Finally TSubstanceDatabase exposes a relevant methods/properties to
describe the relationship between TSubstance and its unique name.
Could you please comment on this design and tell me if I am going in a
right direction?

I'll post more of related code as a separate post in order to
encourage more people to read this message entirely. :-)

Thanks in advance!


--
Ivo Bauer [OZM Research]
Back to top
Ivo Bauer
Guest





PostPosted: Wed Jan 31, 2007 3:25 pm    Post subject: Re: Managing unique IDs Reply with quote



Here comes my code as promised. TSubstance class looks pretty
straightforward (private details were omitted for the sake of
readability). DiplayName property is used for presentation purposes only:

TSubstance = class
public
property AtomCounts[AKind: TAtomKind]: Integer read GetAtomCount
write SetAtomCount;
property Density: Double read FDensity write SetDensity;
property DisplayName: string read FDisplayName write SetDisplayName;
property EnthalpyOfFormation: Double read FEnthalpyOfFormation
write SetEnthalpyOfFormation;
property MolecularMass: Double read FMolecularMass write
SetMolecularMass;
property Role: TSubstanceRole read FRole write SetRole;
end;

Below is the implementation of substance database. I'd be very
grateful to obtain any comments regarding the class design:

TSubstanceDatabaseItem = class
private
FSubstance: TSubstance;
FName: string;
public
constructor Create(const AName: string);
destructor Destroy; override;
property Name: string read FName;
property Substance: TSubstance read FSubstance;
end;

TSubstanceDatabase = class
private
FItemList: TList;
function GetCount: Integer;
function GetSubstance(AIndex: Integer): TSubstance;
public
function Add(const AName: string): TSubstance;
constructor Create;
procedure Clear;
procedure Delete(AIndex: Integer);
destructor Destroy; override;
function IndexOf(ASubstance: TSubstance): Integer;
function NameBySubstance(ASubstance: TSubstance): string;
function SubstanceByName(const AName: string): TSubstance;
procedure Remove(ASubstance: TSubstance);
property Count: Integer read GetCount;
property Substances[AIndex: Integer]: TSubstance read
GetSubstance; default;
end;

{==============================================================================}
{ TSubstanceDatabaseItem
}
{==============================================================================}

constructor TSubstanceDatabaseItem.Create(const AName: string);
begin
Assert(AName = EmptyStr, 'Substance name may not be empty!');
FName := AName;
FSubstance := TSubstance.Create;
end;

{==============================================================================}

destructor TSubstanceDatabaseItem.Destroy;
begin
FSubstance.Free;
inherited;
end;

{==============================================================================}
{ TSubstanceDatabase
}
{==============================================================================}

function TSubstanceDatabase.Add(const AName: string): TSubstance;
var
VItem: TSubstanceDatabaseItem;
begin
{ TODO : Make sure AName is not empty }
if SubstanceByName(AName) = nil then
begin
VItem := TSubstanceDatabaseItem.Create(AName);
try
FItemList.Add(VItem);
Result := VItem.Substance;
except
VItem.Free;
raise;
end;
end
else
begin
raise ESubstanceDatabaseException.CreateStdError(Self,
@rsDuplicateSubstanceName, 'Add');
end;
end;

{==============================================================================}

procedure TSubstanceDatabase.Clear;
var
I: Integer;
begin
for I := Pred(Count) downto 0 do
Delete(I);
end;

{==============================================================================}

constructor TSubstanceDatabase.Create;
begin
FItemList := TList.Create;
end;

{==============================================================================}

procedure TSubstanceDatabase.Delete(AIndex: Integer);
var
VEntry: TSubstanceDatabaseItem;
begin
VEntry := FItemList[AIndex];
FItemList.Delete(AIndex);
VEntry.Free;
end;

{==============================================================================}

destructor TSubstanceDatabase.Destroy;
begin
Clear;
FItemList.Free;
inherited;
end;

{==============================================================================}

function TSubstanceDatabase.GetCount: Integer;
begin
Result := FItemList.Count;
end;

{==============================================================================}

function TSubstanceDatabase.GetSubstance(AIndex: Integer): TSubstance;
begin
Result := TSubstanceDatabaseItem(FItemList[AIndex]).Substance;
end;

{==============================================================================}

function TSubstanceDatabase.IndexOf(ASubstance: TSubstance): Integer;
begin
for Result := 0 to Pred(FItemList.Count) do
begin
if TSubstanceDatabaseItem(FItemList[Result]).Substance =
ASubstance then Exit;
end;
Result := -1;
end;

{==============================================================================}

function TSubstanceDatabase.NameBySubstance(ASubstance: TSubstance):
string;
var
I: Integer;
begin
I := IndexOf(ASubstance);
if I >= 0 then
Result := TSubstanceDatabaseItem(FItemList[I]).Name
else
Result := EmptyStr;
end;

{==============================================================================}

function TSubstanceDatabase.SubstanceByName(const AName: string):
TSubstance;
var
I: Integer;
VItem: TSubstanceDatabaseItem;
begin
Result := nil;
for I := 0 to Pred(FItemList.Count) do
begin
VItem := FItemList[I];
if AnsiCompareText(VItem.Name, AName) = 0 then
begin
Result := VItem.Substance;
Break;
end;
end;
end;

{==============================================================================}

procedure TSubstanceDatabase.Remove(ASubstance: TSubstance);
var
I: Integer;
begin
I := IndexOf(ASubstance);
if I >= 0 then
Delete(I);
end;

{==============================================================================}


--
Ivo Bauer [OZM Research]
Back to top
Gerard
Guest





PostPosted: Wed Jan 31, 2007 3:36 pm    Post subject: Re: Managing unique IDs Reply with quote



En/na Ivo Bauer ha escrit:
Quote:

Below is the implementation of substance database. I'd be very grateful
to obtain any comments regarding the class design:

You seem to duplicate much of what can be done using a TStringList. Is

there any particular reason for this?

Regards,

Gerard.
Back to top
Gerard
Guest





PostPosted: Wed Jan 31, 2007 3:39 pm    Post subject: Re: Managing unique IDs Reply with quote

En/na Ivo Bauer ha escrit:
Quote:
Hi!

I'm designing a "database" of properties of chemical substances. Every
chemical substance needs to be described by properties like density,
molecular mass and so on. For this purpose I wrote class named
TSubstance. Now I need to hold a list of these TSubstances that is meant
to be a "database" (TSubstanceDatabase) where each substance has to be
uniquely identified by its name (string).

I'm not sure how to design this feature in an OO way. If I make the
UniqueName property of TSubstance, how do I ensure that it is really
unique in the context of TSubstanceDatabase when TSubstance is supposed
to know nothing about the TSubstanceDatabase where it belongs?

Keep TSubstance free from uniqueness constraints, and let
TSubstanceDatabase do the check when you add a TSubstance instance, as
it would be done if you where adding data to a database table with such
constraints.

Regards,

Gerard.
Back to top
Ivo Bauer
Guest





PostPosted: Wed Jan 31, 2007 3:42 pm    Post subject: Re: Managing unique IDs Reply with quote

Gerard napsal(a):
Quote:
You seem to duplicate much of what can be done using a TStringList. Is
there any particular reason for this?

Perhaps my lack of knowledge about TStringList. I think I'd take a
closer look at this class right now. Could you please enlighten me
about the duplicate functionality you were talking about? Thanks!


--
Ivo Bauer [OZM Research]
Back to top
Ivo Bauer
Guest





PostPosted: Wed Jan 31, 2007 4:15 pm    Post subject: Re: Managing unique IDs Reply with quote

Hi, Gerard!

Gerard napsal(a):
Quote:
Keep TSubstance free from uniqueness constraints, and let
TSubstanceDatabase do the check when you add a TSubstance instance, as
it would be done if you where adding data to a database table with such
constraints.

Thanks for the suggestion. I've been trying to follow it and end up
with the following code which is somehow less complicated and also
looks much better, IMO. Could you please take a look at it?

TSubstance now lookslike this:

TSubstance = class
private
...
FName: string;
...
public
...
constructor Create(const AName: string);
property Name: string read FName;
...
end;

Furthermore, I got rid off the TSubstanceDatabaseItem class because
TSubstanceDatabase now holds list of TSubstance references directly.
The implementation follows:

TSubstanceDatabase = class
private
FSubstances: TList;
function GetCount: Integer;
function GetSubstance(AIndex: Integer): TSubstance;
public
function Add(const AName: string): TSubstance;
constructor Create;
procedure Clear;
procedure Delete(AIndex: Integer);
destructor Destroy; override;
function IndexOf(ASubstance: TSubstance): Integer;
function SubstanceByName(const AName: string): TSubstance;
procedure Remove(ASubstance: TSubstance);
property Count: Integer read GetCount;
property Substances[AIndex: Integer]: TSubstance read
GetSubstance; default;
end;

{==============================================================================}
{ TSubstanceDatabase
}
{==============================================================================}

function TSubstanceDatabase.Add(const AName: string): TSubstance;
begin
{ TODO : Make sure AName is not empty }
if SubstanceByName(AName) = nil then
begin
Result := TSubstance.Create(AName);
try
FSubstances.Add(Result);
except
Result.Free;
raise;
end;
end
else
begin
raise ESubstanceDatabaseException.CreateStdError(Self,
@rsDuplicateSubstanceName, 'Add');
end;
end;

{==============================================================================}

procedure TSubstanceDatabase.Clear;
var
I: Integer;
begin
for I := Pred(Count) downto 0 do
Delete(I);
end;

{==============================================================================}

constructor TSubstanceDatabase.Create;
begin
FSubstances := TList.Create;
end;

{==============================================================================}

procedure TSubstanceDatabase.Delete(AIndex: Integer);
var
VSubstance: TSubstance;
begin
VSubstance := FSubstances[AIndex];
FSubstances.Delete(AIndex);
VSubstance.Free;
end;

{==============================================================================}

destructor TSubstanceDatabase.Destroy;
begin
Clear;
FSubstances.Free;
inherited;
end;

{==============================================================================}

function TSubstanceDatabase.GetCount: Integer;
begin
Result := FSubstances.Count;
end;

{==============================================================================}

function TSubstanceDatabase.GetSubstance(AIndex: Integer): TSubstance;
begin
Result := TSubstance(FSubstances[AIndex]);
end;

{==============================================================================}

function TSubstanceDatabase.IndexOf(ASubstance: TSubstance): Integer;
begin
Result := FSubstances.IndexOf(ASubstance);
end;

{==============================================================================}

function TSubstanceDatabase.SubstanceByName(const AName: string):
TSubstance;
var
I: Integer;
begin
for I := 0 to Pred(FSubstances.Count) do
begin
Result := TSubstance(FSubstances[I]);
if AnsiCompareText(Result.Name, AName) = 0 then Exit;
end;
Result := nil;
end;

{==============================================================================}

procedure TSubstanceDatabase.Remove(ASubstance: TSubstance);
var
I: Integer;
begin
I := IndexOf(ASubstance);
if I >= 0 then
Delete(I);
end;

{==============================================================================}


--
Ivo Bauer [OZM Research]
Back to top
Gerard
Guest





PostPosted: Wed Jan 31, 2007 4:21 pm    Post subject: Re: Managing unique IDs Reply with quote

En/na Ivo Bauer ha escrit:
Quote:
Gerard napsal(a):
You seem to duplicate much of what can be done using a TStringList. Is
there any particular reason for this?

Perhaps my lack of knowledge about TStringList. I think I'd take a
closer look at this class right now. Could you please enlighten me about
the duplicate functionality you were talking about? Thanks!

Setting the Duplicates property of a TStringList to dupError and the

Sorted property to True will raise an exception when adding a duplicated
string.
Or you can check if a string is already stored by
calling IndexOf(aString).
You can also store objects along with a key string with
MyStringList.AdObject(aString, aObject);

Retrieving an object by the key string would be

i := MyStringList.IndexOf(aString);
if i <=0 then
aObject := nil
else
aObject := TMyObjectType(MyStringList.Object[i]);

It works well for reasonable numbers of objects (say, less than some
millions Wink )

HTH,


Gerard.
Back to top
Gerard
Guest





PostPosted: Wed Jan 31, 2007 4:32 pm    Post subject: Re: Managing unique IDs Reply with quote

Hi IVO,


En/na Ivo Bauer ha escrit:
Quote:

Furthermore, I got rid off the TSubstanceDatabaseItem class because
TSubstanceDatabase now holds list of TSubstance references directly. The
implementation follows:


See my previous post. Here's the same code using a TStringList.

Quote:
TSubstanceDatabase = class
private
FList: TStringList;



function TSubstanceDatabase.Add(const AName: string): TSubstance;
var

i: integer;
Quote:
begin
{ TODO : Make sure AName is not empty }
i := FList.IndexOf(AName);

if i >= 0 then
Quote:
raise ESubstanceDatabaseException.CreateStdError(Self,
@rsDuplicateSubstanceName, 'Add');
else
Result := TSubstance.Create(AName);
FList.AddObject(AName, Result);
end;

{==============================================================================}


procedure TSubstanceDatabase.Clear;
var
I: Integer;
begin
// Only neede if you need to free the objects

for I := 0 To Flist.Count do
TSubstance(FList.Objects[i]).Free;
// Clear the list
Flist.Clear;
Quote:
end;

{==============================================================================}


constructor TSubstanceDatabase.Create;
begin
FList := TStringList.Create;

FList.Sorted := True;
FList.Duplicates := dupError;
Flist.CaseSensitive := true; // Or false, depending on your needs
Quote:
end;

{==============================================================================}


procedure TSubstanceDatabase.Delete(AIndex: Integer);
var
VSubstance: TSubstance;
begin
VSubstance := TSubstance(FList.Objects[AIndex]);

FSubstance.FRee;
Flist.Delete(Index);
Quote:
end;

{==============================================================================}


destructor TSubstanceDatabase.Destroy;
begin
Clear;
FList.Free;
inherited;
end;

{==============================================================================}


function TSubstanceDatabase.GetCount: Integer;
begin
Result := FList.Count;
end;

{==============================================================================}


function TSubstanceDatabase.GetSubstance(AIndex: Integer): TSubstance;
begin
Result := TSubstance(FList[Index]);
end;

{==============================================================================}


function TSubstanceDatabase.IndexOf(ASubstance: TSubstance): Integer;
begin
Result := FList.IndexOfObject(ASubstance);
end;

{==============================================================================}


function TSubstanceDatabase.SubstanceByName(const AName: string):
TSubstance;
var
I: Integer;
begin
i := FList.IndexOf(AName);

if i >= 0 then
Result := TSubstance(Flist.Objects[i])
else
Result := nil;
Quote:
end;

{==============================================================================}


procedure TSubstanceDatabase.Remove(ASubstance: TSubstance);
var
I: Integer;
begin
I := IndexOf(ASubstance);
if I >= 0 then
Delete(I);
end;

{==============================================================================}


HTH,


Gerard.
Back to top
Ivo Bauer
Guest





PostPosted: Wed Jan 31, 2007 6:26 pm    Post subject: Re: Managing unique IDs Reply with quote

Hi, Gerard!

Gerard napsal(a):
Quote:
Setting the Duplicates property of a TStringList to dupError and the
Sorted property to True will raise an exception when adding a duplicated
string.
Or you can check if a string is already stored by
calling IndexOf(aString).
You can also store objects along with a key string with
MyStringList.AdObject(aString, aObject);

I already knew that it's possible to store an object reference along
with each string, but I somehow missed TStringList's duplicate
checking feature and the IndexOf method also slipped through my
attention. Thanks for pointing it out!

Quote:
Retrieving an object by the key string would be

i := MyStringList.IndexOf(aString);
if i <=0 then
aObject := nil
else
aObject := TMyObjectType(MyStringList.Object[i]);

Shouldn't it be "if i < 0 then"? Otherwise, the first item (with index
of zero) would be always discriminated. :-)

Quote:
It works well for reasonable numbers of objects (say, less than some
millions Wink )

In this case the database is supposed to hold about 50 substances, so
I don't anticipate any significant performance issues. :-)


--
Ivo Bauer [OZM Research]
Back to top
Bart
Guest





PostPosted: Wed Jan 31, 2007 6:31 pm    Post subject: Re: Managing unique IDs Reply with quote

Quote:

It works well for reasonable numbers of objects (say, less than some
millions Wink )

In this case the database is supposed to hold about 50 substances, so
I don't anticipate any significant performance issues. :-)


You could use THashedStringList instead

Regards,

Bart
Back to top
Ivo Bauer
Guest





PostPosted: Wed Jan 31, 2007 6:36 pm    Post subject: Re: Managing unique IDs Reply with quote

Hi, Gerard!

Gerard napsal(a):
Quote:
See my previous post. Here's the same code using a TStringList.

I've just seen it and replied already. I'm going to refactor my code
to make use of TStringList features, duplicate checking in particular.
Thanks again for taking your time to adapt my code for use with
TStringList.


--
Ivo Bauer [OZM Research]
Back to top
Ivo Bauer
Guest





PostPosted: Wed Jan 31, 2007 6:44 pm    Post subject: Re: Managing unique IDs Reply with quote

Hi, Bart!

Bart napsal(a):
Quote:
You could use THashedStringList instead

Amazing! I've never heard of this class before. Thanks for your
suggestion.


--
Ivo Bauer [OZM Research]
Back to top
Ivo Bauer
Guest





PostPosted: Wed Jan 31, 2007 7:25 pm    Post subject: Re: Managing unique IDs Reply with quote

Ivo Bauer napsal(a):
Quote:
I'm going to refactor my code to make use of TStringList features,
duplicate checking in particular.

Done and also uses hashed variant of string list internally as
suggested by Bart. Here is the code:


TSubstanceDatabase = class
private
FList: THashedStringList;
function GetCount: Integer;
function GetSubstance(AIndex: Integer): TSubstance;
public
function Add(const AName: string): TSubstance;
constructor Create;
procedure Clear;
procedure Delete(AIndex: Integer);
destructor Destroy; override;
function IndexOf(ASubstance: TSubstance): Integer;
function SubstanceByName(const AName: string): TSubstance;
procedure Remove(ASubstance: TSubstance);
property Count: Integer read GetCount;
property Substances[AIndex: Integer]: TSubstance read
GetSubstance; default;
end;

function TSubstanceDatabase.Add(const AName: string): TSubstance;
begin
{ TODO : Make sure AName is not empty }
Result := TSubstance.Create(AName);
try
FList.AddObject(AName, Result);
except
Result.Free;
raise;
end;
end;

{==============================================================================}

procedure TSubstanceDatabase.Clear;
var
I: Integer;
begin
for I := Pred(Count) downto 0 do
Delete(I);
end;

{==============================================================================}

constructor TSubstanceDatabase.Create;
begin
FList := THashedStringList.Create;
FList.Duplicates := dupError;
FList.Sorted := True;
FList.CaseSensitive := False;
end;

{==============================================================================}

procedure TSubstanceDatabase.Delete(AIndex: Integer);
var
VSubstance: TSubstance;
begin
VSubstance := Substances[AIndex];
FList.Delete(AIndex);
VSubstance.Free;
end;

{==============================================================================}

destructor TSubstanceDatabase.Destroy;
begin
Clear;
FList.Free;
inherited;
end;

{==============================================================================}

function TSubstanceDatabase.GetCount: Integer;
begin
Result := FList.Count;
end;

{==============================================================================}

function TSubstanceDatabase.GetSubstance(AIndex: Integer): TSubstance;
begin
Result := TSubstance(FList[AIndex]);
end;

{==============================================================================}

function TSubstanceDatabase.IndexOf(ASubstance: TSubstance): Integer;
begin
Result := FList.IndexOfObject(ASubstance);
end;

{==============================================================================}

function TSubstanceDatabase.SubstanceByName(const AName: string):
TSubstance;
var
I: Integer;
begin
I := FList.IndexOf(AName);
if I >= 0 then
Result := Substances[I]
else
Result := nil;
end;

{==============================================================================}

procedure TSubstanceDatabase.Remove(ASubstance: TSubstance);
var
I: Integer;
begin
I := IndexOf(ASubstance);
if I >= 0 then
Delete(I);
end;

{==============================================================================}

Thanks to all who were involved!


--
Ivo Bauer [OZM Research]
Back to top
Gerard
Guest





PostPosted: Wed Jan 31, 2007 8:23 pm    Post subject: Re: Managing unique IDs Reply with quote

En/na Ivo Bauer ha escrit:

Quote:

Shouldn't it be "if i < 0 then"? Otherwise, the first item (with index
of zero) would be always discriminated. :-)

True.


Quote:
It works well for reasonable numbers of objects (say, less than some
millions Wink )

In this case the database is supposed to hold about 50 substances, so I
don't anticipate any significant performance issues. :-)

Should be Ok, then.


Regards,

Gerard.
Back to top
Bart
Guest





PostPosted: Wed Jan 31, 2007 9:45 pm    Post subject: Re: Managing unique IDs Reply with quote

Ivo,

A little bit off topic. But do you know if there is any OPC server or what
so ever which communicates with a Simatic S5 PLC. (Maybe a free one)

I believe this is sort of your business isn't it ?

Regards,

Bart
Back to top
Display posts from previous:   
Post new topic   Reply to topic    BorlandTalk.com Forum Index -> Delphi OO design All times are GMT
Goto page 1, 2  Next
Page 1 of 2

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2006 phpBB Group
SEO toolkit © 2004-2006 webmedic.