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 

Object design

 
Post new topic   Reply to topic    BorlandTalk.com Forum Index -> Delphi OO design
View previous topic :: View next topic  
Author Message
Alan
Guest





PostPosted: Tue Dec 21, 2004 12:05 am    Post subject: Object design Reply with quote



I am going to write an small application similar to library information
system for myself.
My objects are TPatron, TItem, TReservedItem, TLoanedItem.

For a Patron object, should I embed the collection of Reserved items and
Loaned items into this Patron object:
TPatron =
private
fName: String;
fPhone: String;
......
fLoanedItems: Collection of TLoanedItem;
fReservedItems: Collection of TReservedItem;
....
public
....
end;


Or should I design a bigger class:
TPatronBig =
private
fPatron: TPatron;
fLoanedItems: Collection of TLoanedItems;
fReservedItems: Collection of TReserved items;
....
public
.....
end;



Back to top
Joanna Carter (TeamB)
Guest





PostPosted: Tue Dec 21, 2004 9:54 am    Post subject: Re: Object design Reply with quote



"Alan" <NOSPAMalan_pltse (AT) yahoo (DOT) com.au> a écrit dans le message de news:
41c768d4$1 (AT) newsgroups (DOT) borland.com...

Quote:
I am going to write an small application similar to library information
system for myself.
My objects are TPatron, TItem, TReservedItem, TLoanedItem.

For a Patron object, should I embed the collection of Reserved items and
Loaned items into this Patron object:
TPatron =
private
fName: String;
fPhone: String;
......
fLoanedItems: Collection of TLoanedItem;
fReservedItems: Collection of TReservedItem;
....
public
....
end;

This kind of construct is trying to emulate the RAD master-detail construct
encouraged by TDataset and should be avoided at all costs.

To start with, you are making the classic mistake of trying to overuse
inheritance inappropriately :-)

An Item does not change its type when it becomes reserverd or loaned, it
simply changes its state.

TItemState = is Available, isLoaned, isReserved;

TItem = class
private
fState: TItemState;
...
public
property State: TItemState
read fState;
write SetState;
end;

Then you need at least two separate classes to describe the Loans and/or
Reservations, thereby allowing you to record things like reservation start
date, reservation expiry limit, loan date, return date, etc.

The individual loans should be held in a 'Loan Record Book' which can be
queried by Patron to give you a list of Loans for that Patron; also queried
by Item to see which Patron has a particular item.

So you get :

TLoanBook = class
...
public
function GetItems(const Patron: TPatron): TItemList;
function GetPatron(const Item: TItem): TPatron; // possibly TPatronList ?
end;

Quote:
Or should I design a bigger class:
TPatronBig =
private
fPatron: TPatron;
fLoanedItems: Collection of TLoanedItems;
fReservedItems: Collection of TReserved items;
....
public
.....
end;

I would question the use of a wrapper class just to hold an instance of a
class that has the same semantics as the outer class. See my Loan Record
List idea.

Joanna

--
Joanna Carter (TeamB)

Consultant Software Engineer
TeamBUG support for UK-BUG
TeamMM support for ModelMaker



Back to top
Jim Cooper
Guest





PostPosted: Tue Dec 21, 2004 10:39 am    Post subject: Re: Object design Reply with quote




Quote:
For a Patron object, should I embed the collection of Reserved items and
Loaned items into this Patron object:

I agree with Joanna. No you shouldn't :-)

Loans and reservations are things that happen to books (or whatever).

I think Joanna's item state is a little too simple though, as an item
can be both borrowed and reserved, and potentially also be reserved by
many people (used to happen to popular references at my uni library,
anyway <g>).

Also, the lifetime of borrowings and reservations is likely to be
different. Reservations may well come and go, but I would have thought
it would be common to want a permanent record of all borrowings.

But in general, I also think you should work outwards from loans and
reservations to people and books, not the other way around.

Cheers,
Jim Cooper

_______________________________________________

Jim Cooper [email]jim (AT) falafelsoft (DOT) com[/email]
Falafel Software http://www.falafelsoft.com
_______________________________________________

Back to top
Joanna Carter (TeamB)
Guest





PostPosted: Tue Dec 21, 2004 12:06 pm    Post subject: Re: Object design Reply with quote

"Jim Cooper" <jim (AT) falafelsoft (DOT) com> a écrit dans le message de news:
41c7fd4f$1 (AT) newsgroups (DOT) borland.com...

Quote:
I think Joanna's item state is a little too simple though, as an item
can be both borrowed and reserved, and potentially also be reserved by
many people (used to happen to popular references at my uni library,
anyway <g>).

Yes, of course Smile In that case you could use something like a 'set' or
persistent state machine.

Joanna

--
Joanna Carter (TeamB)

Consultant Software Engineer
TeamBUG support for UK-BUG
TeamMM support for ModelMaker



Back to top
Alan
Guest





PostPosted: Tue Dec 21, 2004 11:00 pm    Post subject: Re: Object design Reply with quote

Here's my classes in cut down version:
TItem = class
private
fItemID: Integer;
fItemCode: String;
fTitle: String;
....
function GetItemCode: String;
procedure SetItemCode(aCode: String);
.....
public
....
procedure GetItemDetailsByCode;
property ItemCode: String read GetItemCode write ItemCode;
...
end;

TPatronLoanedItem = class
private
fPatronID: Integer;
fPatronFirstname: String;
fPatronLastname: String;
fItemID: Integer;
fItemCode: String;
fTitle: String;
fLoanDate: TDateTime;
fDueDate: TDateTime;

public
...
end;

TPatronReservedItem = class
private
fPatronID: Integer;
fPatronName: Sting;
fItemID: Integer;
fItemCode: String;
fReservedDate: TDateTime;
public
....
end;

TPatronObject = class
private
fPatronID: Integer;
fPatronFirstname: String;
fPatronLastname: String;
fAddress: String;
....
fNotes: String;
fLoanItems: TObjectList; <-- list of TPatronLoanedItem
fReservedItems: TObjectList; <-- list of TPatronReservedItem
....(getter and setters)
public
...
property ....
end;

This way I can fetch the loaned and reserved item for this patron.


Back to top
Joanna Carter (TeamB)
Guest





PostPosted: Wed Dec 22, 2004 12:26 am    Post subject: Re: Object design Reply with quote

"Alan" <NOSPAMalan_pltse (AT) yahoo (DOT) com.au> a écrit dans le message de news:
[email]41c8ab06 (AT) newsgroups (DOT) borland.com[/email]...

Quote:
Here's my classes in cut down version:
TItem = class
private
fItemID: Integer;
fItemCode: String;
fTitle: String;
....
function GetItemCode: String;
procedure SetItemCode(aCode: String);
.....
public
....
procedure GetItemDetailsByCode;
property ItemCode: String read GetItemCode write ItemCode;
...
end;

Why do you bother prepending Item to the field names ? This seems
unnecessary.

I don't see the need for the GetItemDetailsByCode method ?

Quote:
TPatronLoanedItem = class
private
fPatronID: Integer;
fPatronFirstname: String;
fPatronLastname: String;
fItemID: Integer;
fItemCode: String;
fTitle: String;
fLoanDate: TDateTime;
fDueDate: TDateTime;

public
...
end;

TPatronReservedItem = class
private
fPatronID: Integer;
fPatronName: Sting;
fItemID: Integer;
fItemCode: String;
fReservedDate: TDateTime;
public
....
end;

You are using IDs to link objects; this is not really how OO systems are
designed. One would tend to use the actual objects.

TLoan = class
private
fID: Integer;
fPatron: TPatron;
fItem: TItem;
fLoanDate: TDateTime;
fDueDate: TDateTime;

public
...
end;

TReservation = class
private
fID: Integer;
fPatron: TPatron;
fItem: TItem;
fReservedDate: TDateTime;
public
....
end;

Quote:
TPatronObject = class
private
fPatronID: Integer;
fPatronFirstname: String;
fPatronLastname: String;
fAddress: String;
....
fNotes: String;
fLoanItems: TObjectList; <-- list of TPatronLoanedItem
fReservedItems: TObjectList; <-- list of TPatronReservedItem
....(getter and setters)
public
...
property ....
end;

Don't use this method to get lists related to the Patron and create typesafe
lists rather than using raw object lists.

TItemManager = class
public
function GetLoanItems(const Patron: TPatron): TLoanList;
function GetReservedItems(const Patron: TPatron): TReservationList;
...
end;

It is not a function of business objects to refer to each other by IDs, this
is something that is better handled by the persistence mechanism; in
persistence terms, the class of the object is usually persisted as an
integer as is the instance. These two IDs are a persistable representation
of the in-memory type and the memory address

Joanna

--
Joanna Carter (TeamB)

Consultant Software Engineer
TeamBUG support for UK-BUG
TeamMM support for ModelMaker



Back to top
Alan
Guest





PostPosted: Wed Dec 22, 2004 11:16 am    Post subject: Re: Object design Reply with quote

Quote:
I don't see the need for the GetItemDetailsByCode method ?
I need to populate the details of the TItem object by

eg.
Item.Code := 'HarryPot';
Item.GetItemDetailsByCode;
So this procedure itself will call the data access object to populate the
fields of this item.

I just wonder is this TItem just a storage place without any functions ?
Should I have another item business object has TItem embedded in as member
field?



Quote:
TItemManager = class
public
function GetLoanItems(const Patron: TPatron): TLoanList;
function GetReservedItems(const Patron: TPatron): TReservationList;
...
end;

Is this TItemManager a singleton ?




Back to top
Joanna Carter (TeamB)
Guest





PostPosted: Wed Dec 22, 2004 11:56 am    Post subject: Re: Object design Reply with quote

"Alan" <nospam_alanpltse (AT) yahoo (DOT) com.au> a écrit dans le message de news:
[email]41c95756 (AT) newsgroups (DOT) borland.com[/email]...

Quote:
I don't see the need for the GetItemDetailsByCode method ?
I need to populate the details of the TItem object by
eg.
Item.Code := 'HarryPot';
Item.GetItemDetailsByCode;
So this procedure itself will call the data access object to populate the
fields of this item.

You seem to be implying that your business classes have knowledge of their
data access; this will severely limit the flexibility of any system that you
write.

Take a look at the articles on OPF (Object Persistence Frameworks) on my web
site, www.carterconsulting.org.uk as well as other references.

The idea of an OPF is to completely separate the data storage from the
business logic so that you can change the persistence mechanism easily
without having to rewrite great swathes of code in your business classes.

Essentially, you have a singleton Object Store that has methods like :

TObjectStore = class
public
// store a single object
class procedure StoreObject(const Obj: TBusinessObject);
// retrieve single object whose ID is already known
class procedure RetrieveObject(const Obj: TBusinessObject);
// delete a single object
class procedure DeleteObject(const Obj: TBusinessObject);
// retrieve all objects of a given type
class function RetrieveCollectionForType(const ObjType: TBusinessClass);
TBusinessObjectList;
// retrieve all objects of a type that match given Criteria
class function RetrieveCollectionForCriteria(const Criteria: TCriteria);
TBusinessObjectList;
...
end;

Then to use the criteria search you need a Criteria class, instances of
which are usually created by calls similar to :

crit := TEqualsCriteria.Create(TItem, 'Code', 'HarryPot');

Your client code to choose a Patron and then retrieve a list of Loans for
that Patron could be :

var
crit: TCriteria;
patronList: TPatronList;
patron: TPatron;
loanList: TLoanList;
begin
crit := TEqualsCriteria.Create(TPatron, 'Name', 'Carter');
try
patronList :=
TPatronList(TObjectStore.RetrieveCollectionForCriteria(crit));
finally
crit.Free;
end;

// assuming only one Patron retrieved
patron := patronList[0];

crit := TObjectCriteria.Create(TLoan, 'Patron' patron);
try
loanList := TLoanList(TObjectStore.RetrieveCollectionForCriteria(crit));
finally
crit.Free;
end;

....

end;

A good criteria class hierarchy will allow for nesting (bracketing) of
criteria so that complex queries can be performed.

Quote:
I just wonder is this TItem just a storage place without any functions ?
Should I have another item business object has TItem embedded in as member
field?

None of your business classes should have any connection with persistent
storage.

Quote:
Is this TItemManager a singleton ?

It could be but not necessarily, the OPF is a singleton; you could in theory
have multiple Item Managers accessing it at the same time.

Does any of this make sense yet ? Please continue to ask about what you
don't understand; it really does get easier Smile)

Joanna

--
Joanna Carter (TeamB)

Consultant Software Engineer
TeamBUG support for UK-BUG
TeamMM support for ModelMaker



Back to top
Alan
Guest





PostPosted: Wed Dec 22, 2004 10:47 pm    Post subject: Re: Object design Reply with quote


Quote:
The idea of an OPF is to completely separate the data storage from the
business logic so that you can change the persistence mechanism easily
without having to rewrite great swathes of code in your business classes.

Essentially, you have a singleton Object Store that has methods like :

TObjectStore = class
public
// store a single object
class procedure StoreObject(const Obj: TBusinessObject);
// retrieve single object whose ID is already known
class procedure RetrieveObject(const Obj: TBusinessObject);
// delete a single object
class procedure DeleteObject(const Obj: TBusinessObject);
// retrieve all objects of a given type
class function RetrieveCollectionForType(const ObjType: TBusinessClass);
TBusinessObjectList;
// retrieve all objects of a type that match given Criteria
class function RetrieveCollectionForCriteria(const Criteria: TCriteria);
TBusinessObjectList;
...
end;

For the class procedure StoreObject(const Obj: TBusinessObject), so to store
only one instance of each business object class or many instances of each
business object class ? eg for item.





Back to top
Alan
Guest





PostPosted: Wed Dec 22, 2004 11:00 pm    Post subject: Re: Object design Reply with quote

Are the TPatron, TItem I have before becomes the TBusinessObject ?
Do they only have getters and setters providing access to the values ?
Are they all descendant of TBusinessObject ? Like

TBusinessObject = class
private
public
end;

TItemBusinessObject = class(TBusinessObject)
private
public
end;

TPatronBusinessObject = class(TBusinessObject)
private
public
end;



Back to top
Joanna Carter (TeamB)
Guest





PostPosted: Wed Dec 22, 2004 11:23 pm    Post subject: Re: Object design Reply with quote

"Alan" <NOSPAMalan_pltse (AT) yahoo (DOT) com.au> a écrit dans le message de news:
41c9fca4$1 (AT) newsgroups (DOT) borland.com...

All business classes need to derive from TBusinessObject as the base class
holds the metadata mechanism necessary to get at the property values

TItem = class(TBusinessObject)
private
public
end;

TPatron = class(TBusinessObject)
private
public
end;

Joanna

--
Joanna Carter (TeamB)

Consultant Software Engineer
TeamBUG support for UK-BUG
TeamMM support for ModelMaker


Back to top
Joanna Carter (TeamB)
Guest





PostPosted: Wed Dec 22, 2004 11:24 pm    Post subject: Re: Object design Reply with quote

"Alan" <NOSPAMalan_pltse (AT) yahoo (DOT) com.au> a écrit dans le message de news:
41c9f979$1 (AT) newsgroups (DOT) borland.com...

Quote:
For the class procedure StoreObject(const Obj: TBusinessObject), so to
store
only one instance of each business object class or many instances of each
business object class ? eg for item.

The idea of the OPF is to store as many instances as required; a sort of OO
database :-)

Joanna

--
Joanna Carter (TeamB)

Consultant Software Engineer
TeamBUG support for UK-BUG
TeamMM support for ModelMaker



Back to top
Alan
Guest





PostPosted: Thu Dec 23, 2004 1:16 am    Post subject: Re: Object design Reply with quote

Quote:
// retrieve single object whose ID is already known
class procedure RetrieveObject(const Obj: TBusinessObject);

To retrieve the business object by RetrieveObject, that is by ID as you
mentioned, so the instance of business object represent an individual item
for example, but not sigleton ?

So ,

TItemBusinessObject = class(TBusinessObject)
private
fID: Integer;
fCode: String;
fTitle: String;
....
function GetCode: String;
procedure SetCode(aCode: String);
.....
public
....
procedure GetDetailsByCode;
property Code: String read GetCode write SetCode;
...
end;

May be I am still in the dark?




Back to top
Joanna Carter (TeamB)
Guest





PostPosted: Thu Dec 23, 2004 8:10 am    Post subject: Re: Object design Reply with quote

"Alan" <NOSPAMalan_pltse (AT) yahoo (DOT) com.au> a écrit dans le message de news:
[email]41ca1c77 (AT) newsgroups (DOT) borland.com[/email]...

Quote:
To retrieve the business object by RetrieveObject, that is by ID as you
mentioned, so the instance of business object represent an individual item
for example, but not sigleton ?

Certainly, it represents an individual item.

Quote:
TItemBusinessObject = class(TBusinessObject)

Just a little point; don't append 'BusinessObject' to all your class names,
it is assumed that they are without having to be so named. Such a naming
convention tends to make code less readable than just using the name on its
own.

Quote:
....
procedure GetDetailsByCode;
property Code: String read GetCode write SetCode;
...
end;

You don't need the GetDetailsByCode method.

As I mentioned, the BusinessObject class should provide a metadata mechanism
that allows you to access the properties and their values from outside of
the class.

This can be provided by Delphi's default RTTI mechanism which requires you
to publish any properties that you want to get at.

Or it can be provided by something known as a Vaule Type Framework; if you
search old posts in this group, you will find previous mention of this idea.

Whichever way you do it, from a storage/retrieval point of view, you should
not have any mention of this kind of logic in the actual business classes.

Take another look at the post I made that talks about Criteria and their use
in retrieving objects from the OPF. The general idea is that it is not
possible to retrieve a single object before you have first retrieved a list;
this is similar to the way a database works in that it is only possible to
return a dataset.

The first thing that needs to happen in a user interaction is that the OPF
is asked for a list of objects that match a certain criteria. The OPF will
return that list of objects, each one of which will have had its ID set by
the OPF when it was created to be added to that list by the OPF. In order to
speed up retrieval of large numbers of objects, it is common practice to
only populate the ID and one representative property that allows a dialog to
display a list of e.g. Item Codes.

The user then chooses one of the objects in that list and, since the object
already has its ID, it is then passed to the RetrieveObject method of the
OPF. This method uses the ID to populate the remaining properties of the
object by typically issuing a SQL statement that says something like SELECT
* FROM t_item WHERE id = <object.ID>

Quote:
May be I am still in the dark?

Don't worry, the light gets brighter Smile)

Joanna

--
Joanna Carter (TeamB)

Consultant Software Engineer
TeamBUG support for UK-BUG
TeamMM support for ModelMaker



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

 
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.