6
votes

I'm looking for a way to control all business objects I create in my applications written in Delphi.

As an article on Embarcadero's EDN (http://edn.embarcadero.com/article/28217) states, there are basically three ways to do this. I'm mostly interested in the last one, using interfaces. That way, when the business object is no longer being referenced anywhere in the application, it will be dispose of memory wise (I'll get back on this part later).

When creating a new business object, it would be wise to ask that new object manager whether I already fetched it earlier in the program, thus avoid the need to refetch it from the database. I already have the business object in memory, so why not use that one? Thus I'll need the list of available objects in memory to be searchable (fast).

The provided code there uses an "array of TObject" to store the collected objects, which doesn't make it very performant concerning searches through the list of objects once you get to a certain amount. I would have to change that to either TObjectList or some sort of binary searchable tree. What would be the best choice here? I already found some useful code (I think) at http://www.ibrtses.com/delphi/binarytree.html. Didn't JCL have stuff on binary trees?

How would I handle "business objects" and "lists of business objects" in that tree? Would a business object, being part of a list be referenced twice in the tree?

Concerning the disposing of an object: I also want to set some sort of TTL (time to life) to that business object, forcing a refetch after an certain amount of time. Should the reference counter fall to 0, I still want to keep the object there for a certain amount of time, should the program still want it within the TTL. That means I'll need sort sort of threaded monitor looping the object list (or tree) to watch for to-be-deleted objects.

I also came across the Boehm Garbage Collector DLL (http://codecentral.embarcadero.com/Download.aspx?id=21646).

So in short, would it be wise to base my "object manager" on the source code provided in the EDN article? What kind of list would I want to store my objects in? How should I handle list of objects in my list? And should I still keep my object in memory for a while and have it dispose of by a threaded monitor?

Am I correct in my reasoning? Any suggestions, ideas or remarks before I start coding? Maybe some new ideas to incorporate into my code?

Btw, I'd be happy to share the result, for others to benefit, once some brilliant minds gave it a thought.

Thnx.

5

5 Answers

3
votes

If you are using Interfaces for reference counting, and then stick those in a collection of some sort, then you will always have a reference to them. If your objective is "garbage collection" then you only need one or the other, but you can certainly use both if necessary.

What it sounds like you really want is a business object cache. For that you will want to use one of the new generic TDictionary collections. What you might want to do is have a TDictionary of TDictionary collections, one TDictionary for each of your object types. You could key your main TDictionary on an enumeration, or maybe even on the type of the object itself (I haven't tried that, but it might work.) If you are using GUIDs for your unique identifiers then you can put them all in a single TDictionary.

Implement each of your business objects with an interface. You don't need to use Smart Pointers since you are designing your business objects and can descend them from TInterfacedObject. Then only reference it by its interface, so it can be reference counted.

If you want to expire your cache then you will need to have some sort of timestamp on your objects that gets updated each time an object is retrieved from the cache. Then when the cache gets over some specific size you can prune everything older then a certain timestamp. Of course that requires walking the entire cache to do that.

Since you are combining interfaces and a collection then if you have a reference to an object (via its interface), and it gets pruned during cache cleanup, then the object will remain alive until the reference goes away. This provides you an additional safety. Of course if you are still using the reference, then that means you kept the reference for a long time without retrieving it from the cache. In that case you may want to update the timestamp when you read or write to the properties too . . . A lot of that depends on how you will be using the business objects.

As far as refetching, you only want to do that if an object is retrieved from the cache that is older then the refetch limit. That way if it gets pruned before you use it again you are not wasting database trips.

You might consider just having a last modified time in each table. Then when you get an object from the cache you just check the time in memory against the time in the database. If the object has been changed since it was last retrieved, you can update it.

I would limit updating objects only to when they are being retrieved from the cache. That way you are less likely to modify the object while it is use. If you are in the middle of reading data from an object while it changes that can produce some really odd behavior. There are a few ways to handle that, depending on how you use things.

Word of warning about using interfaces, you should not have both object and interfaces references to the same object. Doing so can cause trouble with the reference counting and result in objects being freed while you still have an object reference.

I am sure there will be some feedback on this, so pick what sounds like the best solution for you. . . .

Of course now that I have written all of this I will suggest you look at some sort of business object framework out there. RemObjects has a nice framework, and I am sure there are others.

1
votes

You might want to start by looking at smart pointers. Barry kelly has an implimentation for D2009.

For your BI objects, I would use a guid as the key field, or an integer that is unique across the database. As you load objects into memory, you could store them in a dictionary using the guid as the key and a container object as the value.

The container object contains the bi object, the ttl etc.

Before loading an object, check the dictionary to see if it is already there. If it is there, check the ttl and use it, or reload and store it.

1
votes

For very fast "by name" lookup in your container object, I suggest you look not to trees, but to hash tables. The EZ-DSL (Easy Data Structures) library for Delphi includes the EHash.pas unit, which I use for my hashing containers. If you are interested in that, I will forward it to you.

I tend to think of "lifetime" oriented "containers" that delete all the objects they own when they close down.

I also think that you might consider making the objects themselves count their "usefulness", by having a "FLastUsed:Cardinal" data field. You can assign FLastUsed := GetTickCount and then have the system basically obey limits that you set up, maximum memory or instances to be actively used and how old an object should be (not used at all in X milliseconds) before it gets "demoted" to level 2, level 3, etc.

I am thinking that for Business Objects, you have a memory cost (keep it around when it could be really only a cache), and a coherency (flush my changes to the database before you can destroy me) constraint that makes traditional "garbage collection" ideas necessary, but not sufficient, for the whole problem of business object lifetime management.

1
votes

For a higher level point of view, I recommend two books: "Patterns of Enterprise Application Architecture" by Martin Fowler and Eric Evans book about Domain-Driven Design, "Domain-Driven Design: Tackling Complexity in the Heart of Software" (http://dddcommunity.org/books#DDD). Martin Fowler explains all patterns for object management for business applications, including object repositories and different object / database mapping strategies. Eric Evans book "...provides a broad framework for making design decisions...".

There are aleady some (open source) O/R mapper libraries for Delphi with active community, maybe their source code also can provide some technical guidelines.

0
votes

You are looking for a way to build a container that can store business objects and able to find already instanciated ones a runtime ?

Maybe you could give a look at http://www.danieleteti.it/?p=199 (Inversion of Control and Dependency Injection patterns.)