 |
BorlandTalk.com Borland discussion newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
Ivo Bauer Guest
|
Posted: Thu Feb 01, 2007 3:12 am Post subject: What to hold: ID or object reference? |
|
|
OK, that's me again. This time with a question related to my previous
thread. Now that I've got my TSubstanceDatabase class done, I need to
be able to define a TGaseousMixture class that will hold information
about how many and which substances make up the mixture together with
the percentage of each mixture component (TSubstance).
This class will hold a list of items. Each item should contain either
unique ID (name) of a given substance from TSubstanceDatabase or
directly a reference to TSubstance object instance, and the
corresponding value of percentage. I'm leaning towards the former (ID
based) approach because I think TGaseousMixture class should not care
about any substance related specifics other than their IDs. What do
others think?
type
TGaseousMixture = class
private
...
public
...
function Add(const ASubstanceName: string): Integer;
procedure Delete(AIndex: Integer);
function IndexOf(const ASubstanceName: string): Integer;
procedure Remove(const ASubstanceName: string);
property Count: Integer read GetCount;
property Percentages[AIndex: Integer]: Double read GetPercentage
write SetPercentage; default;
property PercentagesBySubstance[const ASubstanceName: string]:
Double read GetPercentageBySubstance write SetPercentageBySubstance;
property SubstanceNames[AIndex: Integer]: string read
GetSubstanceName;
end;
Thanks in advance!
--
Ivo Bauer [OZM Research] |
|
| Back to top |
|
 |
Bob Dawson Guest
|
Posted: Thu Feb 01, 2007 8:55 am Post subject: Re: What to hold: ID or object reference? |
|
|
"Ivo Bauer" wrote
| Quote: | Each item should contain either unique ID (name)
[...]
or directly a reference to TSubstance object instance
|
From your description and code, I'd say your TGaseousMixture either holds
(or is) a TSubstanceCollection. The collection is responsible for exposing
an array of either TSubstance (the ancestor class from which all substances
descend, or ISubstance (the interface that all substances support).
The collection would actually internally hold a private wrapper class, each
instance of which holds the substance instance and a field (or fields)
indicating the amount of that substance or other characteristics of its
membership in that mixture (if any).
How do the methods
| Quote: | function Add(const ASubstanceName: string): Integer;
procedure Delete(AIndex: Integer);
interact with percentage? Is there any mechanism for making sure that |
percentage always totals 100? That's the sort of internal detail I'd expect
the collection to handle.
bobD |
|
| Back to top |
|
 |
Ivo Bauer Guest
|
Posted: Thu Feb 01, 2007 8:33 pm Post subject: Re: What to hold: ID or object reference? |
|
|
Hi, Bob!
Thanks for your response. Please find my comments inlined below.
Bob Dawson napsal(a):
| Quote: | From your description and code, I'd say your TGaseousMixture either holds
(or is) a TSubstanceCollection. The collection is responsible for exposing
an array of either TSubstance (the ancestor class from which all substances
descend, or ISubstance (the interface that all substances support).
|
Yes. Basically, I want to provide user with a way to select one or
more substances from the global substance database in order to define
their own gaseous mixture. This mixture will then be the source of
data for further analysis and calculations. And yes, during these
calculations I will need to know the mixture composition as well as
correspondent substance properties. Previously, I thought about having
a global substance database class instance and a gaseous mixture class
that will hold only substance IDs along with corresponding
amount/percentage. Accessing the substance properties would involve
translation of substance ID (name) to actual TSubstance instance in
the TSubstanceDatabase. But what you propose seems to be logical. Once
the user has selected which substances to be part of the mixture, I
can clone/assign those from the database into the mixture instance.
Then, there would be no more need to query the database for TSubstance
instance given by its name.
| Quote: | The collection would actually internally hold a private wrapper class, each
instance of which holds the substance instance and a field (or fields)
indicating the amount of that substance or other characteristics of its
membership in that mixture (if any).
|
Yes, I've actually written some code which uses a different approach
(using substance names rather than substance references). I'll try to
rewrite my code to follow your approach this evening and will post it
here when done.
| Quote: | How do the methods
function Add(const ASubstanceName: string): Integer;
procedure Delete(AIndex: Integer);
interact with percentage?
|
The complete source code is pasted below. TGaseousMixture (in its
current version) holds a string list which is used for storing both
the substance name and the corresponding amount as well (as pointer to
Double stored in associated Objects[] property).
| Quote: | Is there any mechanism for making sure that
percentage always totals 100? That's the sort of internal detail I'd expect
the collection to handle.
|
Yes, you can currently specify a name of so called "managed" substance
(IOW a substance whose amount in calculated automatically as a
complement to the whole mixture). This is needed because I'm
targetting a multiple gas - air mixtures where the dominant substance
is nitrogen. The user is usually interested in specifying of volume
percentages of other more important mixture components and the amount
of nitrogen is calculated automatically. If this property is empty,
the mixture class only ensures that total sum of all substance amounts
does not exceed 100%. So, in my case, I will always specify nitrogen
as a "managed" substance name.
Here is my code, so you can take a look at it if you're interested (I
will gratefully appreciate that). As I said I'm going to rewrite it
later today and post the results here ASAP.
TGaseousMixture = class
private
FList: THashedStringList;
FManagedSubstanceName: string;
function GetCount: Integer;
function GetPercentage(AIndex: Integer): Double;
function GetPercentageBySubstance(const ASubstanceName: string):
Double;
function GetSubstanceName(AIndex: Integer): string;
function GetTotalPercentage(AIgnoredIndices: TIntegerSet): Double;
procedure SetManagedSubstanceName(const ASubstanceName: string);
procedure SetPercentage(AIndex: Integer; const AValue: Double);
procedure SetPercentageBySubstance(const ASubstanceName: string;
const AValue: Double);
procedure UpdateManagedPercentage;
public
function Add(const ASubstanceName: string): Integer;
constructor Create;
procedure Clear;
procedure Delete(AIndex: Integer);
destructor Destroy; override;
function IndexOf(const ASubstanceName: string): Integer;
procedure Remove(const ASubstanceName: string);
property Count: Integer read GetCount;
property ManagedSubstanceName: string read FManagedSubstanceName
write SetManagedSubstanceName;
property Percentages[AIndex: Integer]: Double read GetPercentage
write SetPercentage; default;
property PercentagesBySubstance[const ASubstanceName: string]:
Double read GetPercentageBySubstance write SetPercentageBySubstance;
property SubstanceNames[AIndex: Integer]: string read
GetSubstanceName;
end;
function TGaseousMixture.Add(const ASubstanceName: string): Integer;
var
P: PDouble;
begin
{ TODO : Make sure AName is not empty }
New(P);
try
P^ := 0.0;
Result := FList.AddObject(ASubstanceName, TObject(P));
except
Dispose(P);
raise;
end;
end;
{==============================================================================}
procedure TGaseousMixture.Clear;
var
I: Integer;
begin
for I := Pred(Count) downto 0 do
Delete(I);
end;
{==============================================================================}
constructor TGaseousMixture.Create;
begin
FList := THashedStringList.Create;
FList.Duplicates := dupError;
FList.Sorted := True;
FList.CaseSensitive := False;
end;
{==============================================================================}
procedure TGaseousMixture.Delete(AIndex: Integer);
var
P: PDouble;
begin
P := PDouble(FList.Objects[AIndex]);
FList.Delete(AIndex);
Dispose(P);
end;
{==============================================================================}
destructor TGaseousMixture.Destroy;
begin
Clear;
FList.Free;
inherited;
end;
{==============================================================================}
function TGaseousMixture.GetCount: Integer;
begin
Result := FList.Count;
end;
{==============================================================================}
function TGaseousMixture.GetPercentage(AIndex: Integer): Double;
begin
Result := PDouble(FList.Objects[AIndex])^;
end;
{==============================================================================}
function TGaseousMixture.GetPercentageBySubstance(
const ASubstanceName: string): Double;
begin
Result := Percentages[IndexOf(ASubstanceName)];
end;
{==============================================================================}
function TGaseousMixture.GetSubstanceName(AIndex: Integer): string;
begin
Result := FList[AIndex];
end;
{==============================================================================}
function TGaseousMixture.GetTotalPercentage(AIgnoredIndices:
TIntegerSet): Double;
var
I: Integer;
begin
Result := 100.0;
for I := 0 to Pred(Count) do
if [I] * AIgnoredIndices = [] then
Result := Result - GetPercentage(I);
end;
{==============================================================================}
function TGaseousMixture.IndexOf(const ASubstanceName: string): Integer;
begin
Result := FList.IndexOf(ASubstanceName);
end;
{==============================================================================}
procedure TGaseousMixture.Remove(const ASubstanceName: string);
var
I: Integer;
begin
I := IndexOf(ASubstanceName);
if I >= 0 then
Delete(I);
end;
{==============================================================================}
procedure TGaseousMixture.SetManagedSubstanceName(const
ASubstanceName: string);
begin
if AnsiCompareText(FManagedSubstanceName, ASubstanceName) <> 0 then
begin
FManagedSubstanceName := ASubstanceName;
UpdateManagedPercentage;
end;
end;
{==============================================================================}
procedure TGaseousMixture.SetPercentage(AIndex: Integer;
const AValue: Double);
var
ManagedIndex: Integer;
IgnoredIndices: TIntegerSet;
MaxPercentage: Double;
begin
ManagedIndex := IndexOf(ManagedSubstanceName);
if (ManagedIndex >= 0) and (AIndex = ManagedIndex) then
begin
raise Exception.Create('Managed percentage cannot be modified');
end;
IgnoredIndices := [AIndex];
if ManagedIndex >= 0 then
begin
Include(IgnoredIndices, ManagedIndex);
end;
MaxPercentage := GetTotalPercentage(IgnoredIndices);
if InRange(AValue, 0.0, MaxPercentage) then
begin
PDouble(FList.Objects[AIndex])^ := AValue;
UpdateManagedPercentage;
end
else
raise Exception.Create('Invalid percentage');
end;
{==============================================================================}
procedure TGaseousMixture.SetPercentageBySubstance(const
ASubstanceName: string;
const AValue: Double);
begin
Percentages[IndexOf(ASubstanceName)] := AValue;
end;
{==============================================================================}
procedure TGaseousMixture.UpdateManagedPercentage;
var
ManagedIndex: Integer;
IgnoredIndices: TIntegerSet;
begin
ManagedIndex := IndexOf(ManagedSubstanceName);
if ManagedIndex >= 0 then
begin
IgnoredIndices := [ManagedIndex];
PDouble(FList.Objects[ManagedIndex])^ :=
GetTotalPercentage(IgnoredIndices);
end;
end;
{==============================================================================}
--
Ivo Bauer [OZM Research] |
|
| Back to top |
|
 |
Powered by phpBB © 2001, 2006 phpBB Group .
|