 |
BorlandTalk.com Borland discussion newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
Chau Chee Yang Guest
|
Posted: Mon May 16, 2005 2:30 am Post subject: OPF: Designing ObjectState Framework |
|
|
Hi,
I am trying to design the Object State Framework, The State will be
used by RDBMS's for perform SQL updating. I try to enumerate some use
cases to attract more discussion about it:
class TObjectValue is a business domain class, we may treat it like
TInvoice or TCustomer. For example:
TObjectState = [osCreate, osInsert, osUpdate, osDelete, osClean]
TObjectValue = class
property
State: TObjectState;
end;
TInvoice = class(TObjectValue)
TCustomer = class(TObjectValue)
case 1:
When an Object is created:
o := TObjectValue.Create; // o.State := osCreate
o.New; // o.State := osInsert
o.Prop1 := Value1; // o.State := osInsert
o.Prop2 := Value2; // o.State := osInsert
PersistenceFactory.Save(o); // o.State := osClean
case 2:
o := TObjectValue.Create; // o.State := osCreate
PersistenceFactory.Save(o); // o.State := osCreate, Do nothing
case 3:
When an object is load from Persistence Layer:
o := TObjectValue.Create; // o.State := osCreate
PersistenceFactory.Load(o); // o.State := osClean
o.Edit; // o.State := osClean or osUpdate??
o.Prop1 := Value1; // o.State := osUpdate
o.Prop2 := Value2; // o.State := osUpdate
PersistenceFactory.Save(o); // o.State := osClean
case 4:
o := TObjectValue.Create; // o.State := osCreate
PersistenceFactory.Load(o); // o.State := osClean
PersistenceFactory.Save(o); // o.State := osClean, Do nothing
case 5:
When an object is deleted:
o := TObjectValue.Create; // o.State := osCreate
PersistenceFactory.Delete(o); // o.state := osCreate, Do nothing
case 6:
When an object is deleted:
o := TObjectValue.Create; // o.State := osCreate
PersistenceFactory.Load(o); // o.State := osClean
o.Delete; // o.State := osDelete
PersistenceFactory.Delete(o); // o.Clear, o.state := osCreate
I feel the way I design isn't good enough. I use methods like O.New,
O.Edit, O.Delete to set the ObjectState. This kind of coding is similar
to TDataSet's style. It seems like I were poisoned by TDataSet and hard
to get rid of it to design in true OO way. Would anyone share their
thoughts and ideas, please. Thank you.
--
Best regards,
Chau Chee Yang
E Stream Software Sdn Bhd
URL: www.sql.com.my
SQL Financial Accounting
|
|
| Back to top |
|
 |
Stephane Fillon Guest
|
Posted: Mon May 16, 2005 3:05 am Post subject: Re: OPF: Designing ObjectState Framework |
|
|
| Quote: | TObjectState = [osCreate, osInsert, osUpdate, osDelete, osClean]
|
I would prefer not to give RDBMS state name.
TObjectState = [osNew, osDirty, osRemoved, osClean]
--
___________________________________________________________________
Stephane FILLON - mailto: [email]fillon72 (AT) yahoo (DOT) com.au[/email]
39 Balaton Street, Westlake QLD 4074 - Australia
Tel/Fax: +61 (07) 3279 7929
AACS: #3022270 - ICQ: #85420907 - Skype: graal72
GMT +10H
|
|
| Back to top |
|
 |
Bob Dawson Guest
|
Posted: Mon May 16, 2005 6:14 am Post subject: Re: Designing ObjectState Framework |
|
|
"Chau Chee Yang" wrote
| Quote: |
TObjectState = [osCreate, osInsert, osUpdate, osDelete, osClean]
|
I tend to agree with Stephane--name your states for the condition of the
object, not after DBMS actions.
Some considerations:
1. Is there really only one dimension here? Think about
Action on Save() call:
In Database
True False
Clean ignore ignore(?)
Dirty update insert
Deleted ignore ignore
2. I put the question mark on a clean insert--ignoring this may or may not
be right, depending on the convention and what 'clean' means. A new object
may have to be inserted in its default state if required by relational
integrity.
3. Consider the element of serialization within transactions.
| Quote: | o.Prop2 := Value2; // o.State := osInsert
PersistenceFactory.Save(o); // o.State := osClean
|
That will be a problem for complex transactions. Consider
Object A created and Set (now dirty)
start transaction
PersistenceFactory.Save(A);
// what is the state of A? Isn't clean and isn't yet saved
// (transaction might be rolled back)
A.ChangeSomeProperty;
PersistenceFactory.Save(A);
// need to do an update here rather than another insert, because
// within the transaction frame the object already exists
PersistenceFactory.Commit();
//Object A now has known characteristics of being saved and clean.
Point--It's not enough for the object to have a single sense of its RDBMS
status, because that status doesn't change in a simple way. If an object is
submitted to the persistence layer more than once in the course of a
transaction, it has to know that and perhaps behave differently the second
time, evene though it cannot yet be told whether the original command
succeeded.
Note that this also affects deletes; after a call of
PersistenceFactory.Delete(o)
then any further command should be ignored even though the object cannot be
sure that the delete was carried out successfully until the transaction
completes.
bobD
|
|
| Back to top |
|
 |
JFN Guest
|
Posted: Mon May 16, 2005 8:21 am Post subject: Re: Designing ObjectState Framework |
|
|
Bob Dawson wrote:
| Quote: | That will be a problem for complex transactions. Consider
Object A created and Set (now dirty)
start transaction
PersistenceFactory.Save(A);
// what is the state of A? Isn't clean and isn't yet saved
// (transaction might be rolled back)
A.ChangeSomeProperty;
PersistenceFactory.Save(A);
// need to do an update here rather than another insert, because
// within the transaction frame the object already exists
PersistenceFactory.Commit();
//Object A now has known characteristics of being saved and clean.
|
Interesting point.
Wouldn't some kind of a registration stack allow to reverse to any
previous state in case of a RollBack?
start transaction
PersistenceFactory.Save(A); => Push(A);
A.ChangeSomeProperty;
PersistenceFactory.Save(A); => Push(A);
PersistenceFactory.Commit(); => ReleaseStack()
except
PersistenceFactory.RollBack(); => Pop(A), ReleaseStack()
--
Jean-Francois Nifenecker, Bordeaux (EU)
|
|
| Back to top |
|
 |
Joanna Carter (TeamB) Guest
|
Posted: Mon May 16, 2005 8:26 am Post subject: Re: Designing ObjectState Framework |
|
|
"Chau Chee Yang" <ccy (AT) sql (DOT) com.my> a écrit dans le message de news:
[email]428805c6 (AT) newsgroups (DOT) borland.com[/email]...
| Quote: | I am trying to design the Object State Framework, The State will be
used by RDBMS's for perform SQL updating. I try to enumerate some use
cases to attract more discussion about it:
class TObjectValue is a business domain class, we may treat it like
TInvoice or TCustomer. For example:
TObjectState = [osCreate, osInsert, osUpdate, osDelete, osClean]
|
I would mainly agree with both Stephane and Bob. I honestly don't think you
need an Object State Framework though.
Now for the revolutionary (or should that be revolting?) stuff :-)
IMHO the only states you really need to take note of are whether an object
is dirty or not and whether an object has been deleted or not.
Assuming you have an ID inside your objects, then this is sufficient to
indicate whether an object is newly created and not yet persisted, as in
this state, the ID will be nil, null or 0.
So you would only really need a base class something like this :
TObjectValue = class
public
property ID: LongInt...
property IsDirty: Boolean... // readonly
property IsDeleted: Boolean... // readwrite
...
end;
Then you end up with code in the Persistence Layer like this :
function TPersistenceFactory.Store(obj: TObjectValue);
begin
if obj.IsDeleted then
raise Exception.Create('Can't store deleted object');
if not obj.IsDirty then // optional, see Bob's comment
Exit;
if ID <> 0 then
InternalUpdate(obj)
else
InternalInsert(obj);
end;
function TPersistenceFactory.Delete(obj: TObjectValue);
begin
if obj.IsDeleted then
raise Exception.Create('Object already deleted');
if obj.ID = 0 then
raise Exception.Create('Object has never been stored');
InternalDelete(obj);
end;
Joanna
--
Joanna Carter (TeamB)
Consultant Software Engineer
TeamBUG support for UK-BUG
TeamMM support for ModelMaker
|
|
| Back to top |
|
 |
Chau Chee Yang Guest
|
Posted: Mon May 16, 2005 9:05 am Post subject: Re: Designing ObjectState Framework |
|
|
Thank you the reply.
If I have someting like what Stephane suggest:
I := TInvoice.Create; (I.State is osNew)
PersistenceFactory.Load(I); (I.State is osClean)
I.Code.Value := '300-A01'; (I.State is osDirty)
I.DocAmt.Value := 300.00; (I.State is osDirty)
Am I right to say that if I perform the following opeations:
1. I.Revert or I.Undo
This operation should revert the delta back to each properties (Code and
DocAmt) and clear the delta. The I.State will set to osClean)
2. PersistenceFactory.Save(I)
This opeation should clear delta and set the I.State to osClean)
If it is what I said, I think I may need a Delta framework to keep track
the changes user have done. Code and DocAmt property must know it's
"Owner" (I, in this case) to set the Object's State when the property
value changed later.
--
Best regards,
Chau Chee Yang
E Stream Software Sdn Bhd
URL: www.sql.com.my
SQL Financial Accounting
|
|
| Back to top |
|
 |
Joanna Carter (TeamB) Guest
|
Posted: Mon May 16, 2005 9:49 am Post subject: Re: Designing ObjectState Framework |
|
|
"Chau Chee Yang" <ccy (AT) sql (DOT) com.my> a écrit dans le message de news:
[email]4288623d (AT) newsgroups (DOT) borland.com[/email]...
| Quote: | If I have someting like what Stephane suggest:
I := TInvoice.Create; (I.State is osNew)
PersistenceFactory.Load(I); (I.State is osClean)
I.Code.Value := '300-A01'; (I.State is osDirty)
I.DocAmt.Value := 300.00; (I.State is osDirty)
Am I right to say that if I perform the following opeations:
1. I.Revert or I.Undo
This operation should revert the delta back to each properties (Code and
DocAmt) and clear the delta. The I.State will set to osClean)
|
IMO, deltas should be handled with the Memento pattern; IOW, using an
external object to maintain a record of changing state.
So you would have two interfaces :
IMemento = interface
function IsEqualTo(const Other: IMemento): Boolean;
end;
IOriginator = interface
function CreateMemento: IMemento;
procedure SetMemento(const Memento: IMemento);
end;
and TObjectValue would implement IOriginator.
You can then also implement a History class that knows how to hold lists of
Mementos.
| Quote: | 2. PersistenceFactory.Save(I)
This opeation should clear delta and set the I.State to osClean)
|
Yes
| Quote: | If it is what I said, I think I may need a Delta framework to keep track
the changes user have done. Code and DocAmt property must know it's
"Owner" (I, in this case) to set the Object's State when the property
value changed later.
|
Then you could use the Memento pattern and a History list to maintain the
Deltas, but you don't need to know the Owner of properties because you
change the Dirty flag in the setter methods of the properties.
procedure TCustomer.SetName(const value: string)
var
allowChange: Boolean;
args: TValueChangingArgs;
begin
if (fValue <> value) then
begin
allowChange := True;
if Assigned(fOnChanging) then
begin
args := TValueChangingArgs.Create(fName, value, allowChange);
try
fOnChanging(self, args);
finally
args.Free;
end;
end;
if allowChange then
begin
fName := value;
SetIsDirty(True);
if Assigned(fOnChanged);
fOnChanged(this.value);
end
else
raise ValueChangingException.Create(self, 'Name' fName, value);
end;
end;
Joanna
--
Joanna Carter (TeamB)
Consultant Software Engineer
TeamBUG support for UK-BUG
TeamMM support for ModelMaker
|
|
| Back to top |
|
 |
Lee_Nover Guest
|
Posted: Mon May 16, 2005 1:07 pm Post subject: Re: Designing ObjectState Framework |
|
|
On Mon, 16 May 2005 11:49:06 +0200, Joanna Carter (TeamB) <joanna (AT) nospam (DOT) .co.uk> wrote:
| Quote: | if Assigned(fOnChanging) then
begin
args := TValueChangingArgs.Create(fName, value, allowChange);
try
fOnChanging(self, args);
finally
args.Free;
end;
end;
|
nice touch .. how would this be handled in an observer (using observer pattern) ?
a few observers could allow the change and other wouldn't ..
just a thought that crossed my mind :)
|
|
| Back to top |
|
 |
Joanna Carter (TeamB) Guest
|
Posted: Mon May 16, 2005 1:34 pm Post subject: Re: Designing ObjectState Framework |
|
|
"Lee_Nover" <Lee_Nover[nospam]@delphi-si.com> a écrit dans le message de
news: op.sqvdewb0ignj8p (AT) stupidirko (DOT) ..
nice touch .. how would this be handled in an observer (using observer
pattern) ?
a few observers could allow the change and other wouldn't ..
just a thought that crossed my mind :)
This code was developed using C# because it allows multicast events; the
fOnChanging in C# would be such an event, but there is no reason why you
could not use the Observer pattern to broadcast notifications to multiple
Observers instead of calling a multicast event
Joanna
--
Joanna Carter (TeamB)
Consultant Software Engineer
TeamBUG support for UK-BUG
TeamMM support for ModelMaker
|
|
| Back to top |
|
 |
Bob Dawson Guest
|
Posted: Mon May 16, 2005 2:24 pm Post subject: Re: Designing ObjectState Framework |
|
|
"JFN" wrote
| Quote: |
Wouldn't some kind of a registration stack allow to reverse to any
previous state in case of a RollBack?
start transaction
PersistenceFactory.Save(A); => Push(A);
A.ChangeSomeProperty;
PersistenceFactory.Save(A); => Push(A);
PersistenceFactory.Commit(); => ReleaseStack()
except
PersistenceFactory.RollBack(); => Pop(A), ReleaseStack()
|
Not sure a stack would be appropriate, as generally only the initial state
of the object entering the transaction is going to be relative to a
rollback, no matter how many how many times the object is submitted to the
transaction.
If you mean a list of transaction members, then yes that's necessary if the
persistence layer is to notify all interested parties that the transcation
has completed/rolledback. Although here I use a FIFO list rather than a
stack: logically it seems to me that the first item involved in the
transaction should be the first notified of its success/failure, etc.
Member tracking at the transaction level for me solves the problem of
notification (in essence objects subscribe to the trransactions they
participate in), but doesn't solve the problem of remembering previous
object state. If BO marks itself as clean after the save() call, then was it
previously in a loaded or new condition? The object has to remember.
bobD
|
|
| Back to top |
|
 |
Bob Dawson Guest
|
Posted: Mon May 16, 2005 3:18 pm Post subject: Re: Designing ObjectState Framework |
|
|
"Joanna Carter (TeamB)" wrote
| Quote: |
Assuming you have an ID inside your objects, then this is sufficient to
indicate whether an object is newly created and not yet persisted, as in
this state, the ID will be nil, null or 0.
|
Not so fast there...
For collections and relationships objects may need to exchange credentials,
and in my approach that means that they need valid IDs from the moment of
creation. By assuming a nil ID for unsaved objects you're in effect
overloading the meaning of the ID--using nil as a magic flag for another
property (Saved/Unsaved).
| Quote: | function TPersistenceFactory.Store(obj: TObjectValue);
begin
if obj.IsDeleted then
raise Exception.Create('Can't store deleted object');
|
This can be troublesome in the case of owned collections if the convention
is that owning objects save all owned items. If a collection member is
marked for deletion, then saving the owner twice can result in a deletion
the first time through, and an exception the second.
Additionally, for performance reasons it might be desirable to ignore the
call to save a dirty object if the object is both dirty and marked for
deletion.
| Quote: | function TPersistenceFactory.Delete(obj: TObjectValue);
begin
if obj.IsDeleted then
raise Exception.Create('Object already deleted');
|
Why not just ignore?
| Quote: | if obj.ID = 0 then
raise Exception.Create('Object has never been stored');
|
Again, why not just ignore?
bobD
|
|
| Back to top |
|
 |
Bob Dawson Guest
|
Posted: Mon May 16, 2005 3:22 pm Post subject: Re: Designing ObjectState Framework |
|
|
"Chau Chee Yang" wrote
| Quote: | 1. I.Revert or I.Undo
This operation should revert the delta back to each properties (Code and
DocAmt) and clear the delta. The I.State will set to osClean)
|
In general, I'd tend to regard .Revert as meaning restore to default
(unsaved obj) or 'as loaded' (saved obj) condition.
bobD
|
|
| Back to top |
|
 |
Bob Dawson Guest
|
Posted: Mon May 16, 2005 3:26 pm Post subject: Re: Designing ObjectState Framework |
|
|
"Joanna Carter (TeamB)" wrote
| Quote: | if allowChange then
begin
fName := value;
SetIsDirty(True);
if Assigned(fOnChanged);
fOnChanged(this.value);
end
else
raise ValueChangingException.Create(self, 'Name' fName, value);
end;
end;
|
var
i : integer;
begin
x := TMyObj.CreateLoad(ID);
i := x.IntProp;
x.IntProp := i + 1;
x.IntProp := i;
Is x clean or dirty?
bobD
|
|
| Back to top |
|
 |
JFN Guest
|
Posted: Mon May 16, 2005 3:41 pm Post subject: Re: Designing ObjectState Framework |
|
|
Bob Dawson wrote:
| Quote: | "JFN" wrote
Wouldn't some kind of a registration stack allow to reverse to any
previous state in case of a RollBack?
start transaction
PersistenceFactory.Save(A); => Push(A); [1]
A.ChangeSomeProperty;
PersistenceFactory.Save(A); => Push(A); [2]
PersistenceFactory.Commit(); => ReleaseStack()
except
PersistenceFactory.RollBack(); => Pop(A), ReleaseStack()
Not sure a stack would be appropriate, as generally only the initial
state of the object entering the transaction is going to be relative
to a rollback, no matter how many how many times the object is
submitted to the transaction.
|
A stack would allow to have several changes within the transaction, as
you pointed and to restore the BO to the current state when an
exception occurs.
In your exemple, the Pop(A) would alternatively restore to state in [1]
or state in [2] whether the corresponding instruction fails.
--
Jean-Francois Nifenecker, Bordeaux (EU)
|
|
| Back to top |
|
 |
Joanna Carter (TeamB) Guest
|
Posted: Mon May 16, 2005 3:43 pm Post subject: Re: Designing ObjectState Framework |
|
|
"Bob Dawson" <bdawson (AT) idtdna (DOT) com> a écrit dans le message de news:
4288b9da$1 (AT) newsgroups (DOT) borland.com...
| Quote: | Not so fast there...
For collections and relationships objects may need to exchange
credentials,
and in my approach that means that they need valid IDs from the moment of
creation. By assuming a nil ID for unsaved objects you're in effect
overloading the meaning of the ID--using nil as a magic flag for another
property (Saved/Unsaved).
|
I did mention "revolting" didn't I ? :-)
I would tend to take the approach of saving referred to and referring
objects in the same transaction, ensuring that the referred to object got
saved first.
I have a point of view that IDs are a necessary evil only really required
for persistence and have also considered the possibility of brokering
objects in such a way that the persistence broker is responsible for linking
the memory address of the object in a given process to the stored ID. In
that case, you would need a "virgin object" flag.
But then again, I always did like to stir in innovative ideas :-)
| Quote: | function TPersistenceFactory.Store(obj: TObjectValue);
begin
if obj.IsDeleted then
raise Exception.Create('Can't store deleted object');
This can be troublesome in the case of owned collections if the convention
is that owning objects save all owned items. If a collection member is
marked for deletion, then saving the owner twice can result in a deletion
the first time through, and an exception the second.
Additionally, for performance reasons it might be desirable to ignore the
call to save a dirty object if the object is both dirty and marked for
deletion.
|
Plenty of options when discussing whether to raise exceptions or not.
| Quote: | function TPersistenceFactory.Delete(obj: TObjectValue);
begin
if obj.IsDeleted then
raise Exception.Create('Object already deleted');
Why not just ignore?
if obj.ID = 0 then
raise Exception.Create('Object has never been stored');
Again, why not just ignore?
|
Also possible.
Joanna
--
Joanna Carter (TeamB)
Consultant Software Engineer
TeamBUG support for UK-BUG
TeamMM support for ModelMaker
|
|
| Back to top |
|
 |
|
|
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
|
|