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 

Re: Some questions concerning Joanna Carter's Object Store a

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





PostPosted: Fri Oct 03, 2003 9:12 am    Post subject: Re: Some questions concerning Joanna Carter's Object Store a Reply with quote



Petter Holmström wrote:

Quote:
I'm currently trying to implement my own object persistent framework and
I've been using Joanna Carter's Object Store articles as guidelines, but
(mostly) with my own code.

That's what I like to see, take someone else's ideas, tear them apart and
try to get it right. :-)

Quote:
First of all: What is the difference between the StorageMechanism and the
CollectionBroker?

The Persistence Mechanism is the part of the OPF that can be sub-classed to
suit different data providers such as SQL databases, XML, streams, etc.

At the time of writing those articles, I followed quite a lot of Scott
Ambler's ideas, except for the handling of object collections. Scott used
the idea of a Criteria that had several functions to return either: Cursor,
Proxies, Objects or Records, I felt that this was not the best idea.

Therefore, I decided to add methods to the Persistence Broker that would
return a Collection of objects. One would accept a Type as a parameter and
return the equivalent of 'SELECT * FROM TABLENAME' and another would accept
Selection Criteria which would do the same thing but transform the Criteria
into a 'WHERE' clause.

The Collection Broker is instantiated internally by the Persistence
Mechanism to allow the paging of objects into the Collection on demand from
the Collection.

For an SQL version of the Object Store, this means that the Collection acts
like a Cursor in that it loads in one direction; trying to access the last
object in the Collection would mean either loading all the objects first or
internally reversing the 'ORDER BY' of the SQL and loading from the 'start'
of the query to the 'end' of the collection in reverse order.

Whatever the strategy used to fill the collection, the Collection Broker
allows you to change that strategy, if necessary at run-time.

My design has changed quite a bit since those articles, because I found that
Scott Ambler's ideas tied the OPF too strongly to the concept of using a SQL
backend. Now I have a Persistence Broker that has methods for managing
Objects and Collections, but has absolutely no idea what is being used for
storage.

The Persistence Broker needs to be a Singleton, so all the methods of that
class are class methods that look up the correct instance of Persistence
Mechanism for the class being handled and forward the request to that
mechanism. It is only in a derivative of Persistence Mechanism that SQL is
brought into the equation, but the concepts of Criteria and Transactions are
both valid Object concepts and can be dealt with in a non-SQL way in other
derivatives.

Quote:
Second: Which object(s) actually populate(s) the business objects and
retrieves the data?

In the version of the OPF discussed in those articles, either the
Persistence Mechanism or the Collection Broker, but now I have a Query class
that can be subclassed to handle different data access components and it is
that that does the actual population of objects and collections from within
the persistence mechanism.

Quote:
Third: Have I understood this right?: The RelationalDatabase only provides
"SQL Syntax" that is used for example by the SelectionCriteria and OrderBy
classes when they're formatting their SQL-output, as well as by those
objects that are actually performing the queries.

I am not quite sure about what you mean here, could you please clarify your
statement?

Quote:
I'm very new to this subject, but now when I've got the idea (at least I
think so) I realized that I have a very complex application (currently
driven by events/data-aware components) that really should be rewritten
using OO...

Yes, it is astounding how many people find that RAD just isn't rapid after
all :-)

Joanna

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



Back to top
Joanna Carter
Guest





PostPosted: Fri Oct 03, 2003 2:57 pm    Post subject: Re: Some questions concerning Joanna Carter's Object Store a Reply with quote



Petter Holmström wrote:

Quote:
So the Persistence Mechanism is the class that actually talks to the
datastore, whatever it is?

Yes

Quote:
So the Collection talks to the Persistence Mechanism (using the
Collection's Persistence Mechanism property) which then talks to the
Collection Broker? If the Persistence Mechanism talks to the database,
and handles the Collection Broker internally, why the need for a
Collection Broker object? Btw, do you have an explanation of what a
Broker object actually is?

The latest version of Persistence Mechanism that I have written is for
interfaces using a Metadata framework and looks something like this:

IConnection = interface
[GUID]
function GetName: string;
function GetServer: string;
function GetPath: string;
function GetUserName: string;
function GetPassword: string;
procedure RegisterType(const IID: TGUID; TypeID: Longword);
function StoreObject(const Obj: IPersistable): Boolean;
function RetrieveObject(const Obj: IPersistable): Boolean;
function DeleteObject(const Obj: IPersistable): Boolean;
function RetrieveCollectionForType(const IID: TGUID): IList;
function RetrieveCollectionForCriteria(const Criteria:
ISelectionCriteria): IList;
end;

If the Collection type supports being paged then calling the
RetrieveCollection methods will create a Collection Broker which will be
assigned to the Collection to handle the GetItem[idx] method of the
Collection; every call to GetItem will check to see if there are enough
objects to satisfy the request, if not it will get the next page into the
collection.

If the Collection does not support paging, then it will be filled straight
away by the Persistence Mechanism or Connection.

Quote:
Do you think you could provide some examples of which methods a Persitence
Broker should provide? Should it have methods suchs ass LoadObject,
StoreObject, LoadCollection, StoreCollection, etc..?

TPersistenceBroker = class
class procedure AddConnection(const Connection: IConnection);
class function GetConnection(const Idx: string): IConnection;
class procedure SetActiveConnection(const Idx: string);
class function GetActiveConnection: IConnection;
class procedure RegisterType(const IID: TGUID; TypeID: Longword);
class function StoreObject(const Obj: IPersistable): Boolean;
class function RetrieveObject(const Obj: IPersistable): Boolean;
class function DeleteObject(const Obj: IPersistable): Boolean;
class function RetrieveCollectionForType(const IID: TGUID): IList;
class function RetrieveCollectionForCriteria(const Criteria:
ISelectionCriteria): IList;
end;

Quote:
Is this correct:?
- The persistence mechanism is used to store and retrieve data, and how it
does this is its "own business", so to say.

Yes

Quote:
- It is the responsibility of the Collection to load objects.

No, it the responsibility of the Persistence mechanism to retrieve the
objects and add them to the Collection; it may do that all in one go or it
may use a Broker to facilitate paging.

Quote:
Which object handles the deletion and storage of objects?

The Persistence Mechanism

Quote:
What happens when the Object.Store or Object.Delete methods are executed?

Normally, the appropriate PersistenceMechanism (if it is SQL based) creates
a Delete statement and executes it. If the Collection is being brokered,
then the broker should be re-initialised, otherwise you can ask the
mechanism to delete the object followed by deleting from the collection.

Quote:
As the methods of the RelationalDatabase are GetStringClauseWhere,
GetStringClauseEquals, etc. I figured they would return values such as:

function TSQL92Database.GetClauseStringFrom: string;
begin
Result := 'FROM %s';
end;

function TSQL92Database.GetClauseStringWhere: string;
begin
Result := 'WHERE %s';
end;

function TSQL92Database.GetClauseStringOrderBy: string;
begin
Result := 'ORDER BY %s';
end;

Quote:
In short I thought this object would be used when constructing the SQL
queries, making it possible to use different database flavours (with
different SQL syntax).

Correct

Quote:
For example, if I have an EqualsCriteria object with an integer value, the
AsSQLClause property would return something like this, getting the "value
format" from the RelationalDatabase object:

Criteria should have several overloaded constructors that can take the
different types of parameters, these should convert the value to a string
and then the AsSQLString method of the class can concatenate the field,
operator and value to give a valid SQL clause.

Joanna

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



Back to top
Wayne Niddery [TeamB]
Guest





PostPosted: Sun Oct 05, 2003 5:11 pm    Post subject: Re: Some questions concerning Joanna Carter's Object Store a Reply with quote



Petter Holmström wrote:
Quote:
... and while we're at the subject:
How did you implement your transaction support? I've been trying to
come up with a solution, with no success.
Should all the objects have some kind of Deleted or Modified flag
that gets set when Object.Store or Object.Delete is called? Then when
the transaction gets committed it would actually save/delete the
objects to/from the object store. But what happens it the transaction
gets rolled back? How do I know which newly created objects I should
destroy and which objects I should reload or undelete?

My domain (business) objects have such flags - new, modified, deleted. These
are set when, e.g, any property of the domain is changed, or I call delete
on it, etc. These objects do not have a Store/Save method.

When I pass that domain (actually a list containing it) to a persistence
object, that persistence object starts a transaction.
The Save mechanism of the persistence object looks at each domain object and
figures out what to do (insert/update/delete), passing the domain to the
corresponding virtual method.

If it successfully makes it through the list and is able to commit the
transaction, it then goes through again and clears the flags. In the case of
a rollback, there's nothing to undo - the domain flags are still correct.
(Of course that leaves the issue of why the rollback and whether the changed
domains will be able to be persisted at all, but at least to this point
everything remains consistent).

--
Wayne Niddery - Logic Fundamentals, Inc. (www.logicfundamentals.com)
RADBooks: http://www.logicfundamentals.com/RADBooks.html
"It is error alone which needs the support of government. Truth can
stand by itself." - Thomas Jefferson



Back to top
Joanna Carter
Guest





PostPosted: Sun Oct 05, 2003 7:28 pm    Post subject: Re: Some questions concerning Joanna Carter's Object Store a Reply with quote

Petter Holmström wrote:

Quote:
Being new to this technique of programming and never having worked with
interfaces before I found some of your code examples a bit too confusing,
for example all the Type methods with a TGUI-parameter or why the
persistence broker's methods had to be class methods, etc. All this
TGUI-stuff I did not understand at all.

Just as with TClass being the type of TObject so that a variable of TClass
can hold a reference to any class type, so a TGUID is the variable type that
can store a GUID or interface type (the two are interchangeable). If I want
to pass ICustomer as an interface type to a method, then I need a TGUID
parameter.

The Persistence Broker has to be a Singleton, only one instance, so by
making the methods class methods, I am using the fact that there is only one
class to ensure that there is only one Broker. Unfortunately, Delphi does
not support class variables until we get the .NET version, so I use
variables in the implementation section of the containing unit as the
nearest equivalent; it is this kind of variable that holds the Connection
List, etc..

Quote:
My object store system will need the following objects:
- A business object that holds the record data

You seem to be trying to create Business Objects to encapsulate DB records.
THis is not a good idea, OPFs are designed to work best the other way round.
i.e. you design your BOs and then get the OPF to store them.

Relational theory is not the same as Object theory when it comes to system
design;

e.g. An Order contains a list of Order Lines:

In a relational system, the Order Lines would each have a reference to the
Order they are a part of.

In an Object system, the Order contains a Collection of Order Line objects
but those Line objects have no knowledge of the Order they are a part of.

It is the job of the OPF to manage that 'impedance mismatch' and translate
the object relationship to a relational relationship.

Quote:
- A collection object that holds a list of business objects

Strictly, Business Object is not a part of the OPF, it simply has to have a
mechanism for exposing its properties, but yes a Collection is usually a
list of BOs

Quote:
- A storage mechanism that:
- Can save a single object
- Can delete a single object
- Can load a single object
- Can load a list of objects

.... and return a list of or single object.

Quote:
- A connection object that communicates with the actual object store, for
example an SQL database.

Yes, but there could be more than one Connection, therefore it is more
correct to say a Connection class, one for each flavour of storage.

Quote:
- A transaction object that provides transaction support.

Once again it would be more correct to refer to a class rather than an
object, and there would be a small hierarchy of classes that can handle
nested transactions and transactions that have to cover multiple
Connections.

Quote:
- A broker object that acts as a middle tier between the business objects/
collections and their respective storage mechanisms.

correct.

Quote:
As I will mostly work with SQL databases the system must support
multi-table selects. It must also support getting data in chunks (this is
btw. one of the parts I find is the most difficult to implement
technically) and proxy-objects.

The requirement for joins diminishes greatly if you design your system
around the Business classes rather than the tables. The only real need for
joins seems to be for reporting and that can be handled by Custom
Persistance Handlers that can translate a Report class into a valid stored
proc or view.

Quote:
In order to make this system as flexible as possible I want to draw clear
lines between the business specific components, i.e. the classes that need
derivatives, and the "general" components, i.e. the classes that can be
reused in other applications "as-is".

The only thing that is common to the BO layer and the OPF is the base BO
class, in that it has to exhibit some bahviour that allows the OPF to
examine its metadata. The OPF should know nothing about the business layer
and vice versa.

Quote:
As I want to be able to use e.g. a FireBird connection object in several
applications I don't want this to be business-aware. I also do not want to
write a new transaction object for each application. Therefor at least
these two objects fall into the "general"-category.

correct

Quote:
The connection object must provide methods for inserting, updating,
retrieving and deleting *records*, completely unaware of how this "raw"
data will be used.

Not quite, the abstract Connection class or interface must be totally
agnostic, but the subclass for, say Firebird, must know precisely how to
talk to Firebird and translate an object into a DB entity.

Quote:
It must also provide methods for transaction handling, such as Start,
Commit and Rollback.

correct

Quote:
The business object and collection object both fall into the business
specific category.

correct, I use the type substituion facility provided by Delphi by using
template interfaces and classes to eliminate having to write a specific
collection class for each business class. See Yoren Asimov's article in Code
Central for how to use template classes in Delphi.

Quote:
The business object represents a record (e.g. Customer) but can also be
proxy-loaded, i.e. only the values of some fields are loaded. This is
useful when showing browseable lists.

This is not always true; there are BOs that are not persistent. This also
assumes that you are modelling your classes on records rather than the other
way round.

Quote:
The collection represents a table or a view (e.g. Customers).

This is also reverse thinking

Quote:
The collection object also holds information about how the list should be
displayed in a list view.

Incorrect, the BOs and collections know nothing about display or storage.

Quote:
The collection must be able to hold different,
but similar, kinds of objects, such as Manager, Employee, Customer, etc.

It is a much better idea to make them type-safe, but they can be polymorphic
if absolutely necessary.

Quote:
The collection can only be loaded in one direction and navigation is only
possible using the First and Next methods. Object retrieval is available
via the CurrentObject property.

You need to think about a separate Iterator class to implement First, Next
and Current Object, these are not truly part of a Collection class.

Quote:
When First is called, the collection requests the first chunk of X objects
from the storage mechanism. It also moves the internal object pointer to
the first object.
When Next is called, the internal object pointer will be increased by one.
When at the object number X+1, the collection requests the next chunk of X
objects, etc, until the entire collection is loaded.

The above is a description of an Iterator, not a Collection.

Quote:
It is also possible to get a specific object using a ObjectByIndex-method.
If the object index is less than the number of already loaded objects the
correct object will returned. If it is grater than the number of loaded
objects Next will be called until the correct object is found.

This is part of the List functionality

Quote:
QUESTION: If I'm loading the list in chunks using an SQL-database does
this mean that I have to execute a query for every chunk?

Only if you don't want to or can't keep a transaction open for the time it
takes to retrieve the whole collection or until the user does not need the
Collection any more.

Quote:
The storage mechanism is business specific. It is responsible for
inserting, updating, deleting and getting objects from the data store.
Every different business object has its own storage mechanism, e.g. one
for Customer, one for Invoice, etc.

You can create OPf Connections that work by creating a 'data' class for each
business class or you can provide a generic mechanism that e.g. knows how to
create SQL from the metadata in the BO.

Quote:
The storage mechanism must also make sure that a newely created object is
inserted into the correct list or that a deleted object is deleted from
the correct list. In order to implement this either:
- The storage mechanism must now which collection(s) a certain object
belongs to
OR
- The business object itself must now which collection(s) it belongs to

It is usually the business process that determines whether an object is
inserted or removed from a collection and/or the OPF.

Since writing the articles, I am now of the firm opinion that the BO classes
should not have Store, Retrieve and Delete methods, all I do now is to have
the BO implement IPersistable as an interface that allows me access to a
unique ID and the state of the object: persistent, deleted, reading,
writing, etc.

Quote:
QUESTION: Which of the above two alternatives is in your opinion the best?

Neither??

Quote:
The broker object is also business-specific. This is an example of how it
is to be used:
Let's say I have the following objects (in this example I'm using classes
but in my real implementation I plan to use interfaces):

As I said above, you could also generate SQL on the fly from the metadata in
the BOs, however if you do it as you suggested that will involve quite a lot
more code but is a valid method.

I tend to use dynamic SQL fo most classes and custom classes for those
classes like reports that need a special join or view.

Quote:
You being the expert on the subject, it will be very interesting to see if
my idea is correct or if "I'm totally out sailing" (a Swedish expression).

There are no real experts, just people that have learnt more than others...
so far :-)

Joanna

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



Back to top
Joanna Carter
Guest





PostPosted: Sun Oct 05, 2003 7:32 pm    Post subject: Re: Some questions concerning Joanna Carter's Object Store a Reply with quote

Petter Holmström wrote:

Quote:
... and while we're at the subject:
How did you implement your transaction support? I've been trying to come
up with a solution, with no success.

Using an SQL DB is easy because you can wrap the DB transaction inside an
Object transaction, but you can also use the Memento pattern to simulate
transactions in non-SQL storage.

Quote:
Should all the objects have some kind of Deleted or Modified flag that
gets set when Object.Store or Object.Delete is called? Then when the
transaction gets committed it would actually save/delete the objects
to/from the object store. But what happens it the transaction gets rolled
back? How do I know which newly created objects I should destroy and
which objects I should reload or undelete?

Deleted or Modified flags are essential in the BO but not for transaction
support.

You may have made changes to a BO, but it is the business process that
creates the transaction and that is responsible for handling what happens to
the BO should the transaction fail or be rolled back.

Joanna

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



Back to top
Joanna Carter
Guest





PostPosted: Mon Oct 06, 2003 8:31 am    Post subject: Re: Some questions concerning Joanna Carter's Object Store a Reply with quote

Petter Holmström wrote:

Quote:
What kind of an ID are we talking about here? And what is it used for?

I use a 64 bit integer split into two 32 bit integers, one for the Type ID
and the other for the Object ID

Joanna

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



Back to top
Joanna Carter
Guest





PostPosted: Thu Oct 09, 2003 11:06 am    Post subject: Re: Some questions concerning Joanna Carter's Object Store a Reply with quote

Petter Holmström wrote:

Quote:
It would be nice if someone who knows about this kind of OOP could check
my code (when it's ready), just in case I have severely misunderstood
some key factor(s). If anyone's interesting, please let me know.

I will cast an eye over it if you would like.

Quote:
I'll be back,

Now, you're not running for state governor are you? :-)

Joanna

--
Joanna Carter
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.