Board index » delphi » Re: Avoiding creating a separate Iterator class...

Re: Avoiding creating a separate Iterator class...


2004-02-17 04:04:45 PM
delphi8
Bryan Crotaz writes:
| that is horribly un-threadsafe Joanna - you need to make Next atomic.
Like Jim, I try to avoid multithreading where possible :-)
IME, in business management applications, concurrent access to the same
record is rare enough, multithreading is almost non-existent.
| I'd also recommend (if you go the interface route) that you
| publish properties on your interface to hide the implementation - it
| makes code neater. Eg if you add a Count property to Joanna's IList,
| you'd have code that read
Unless you are working in a language that doesn't support properties; not
all code is Delphi :-)
Joanna
Consultant Software Engineer
TeamBUG support for UK-BUG
TeamMM support for ModelMaker
 
 

Re: Avoiding creating a separate Iterator class...

Quote
This person + some others seem to use iterators as an interface...is it so
bad to use an Interface?
IMO people are too quick to use interfaces everywhere - they seem to use
them as some sort of silver bullet, assuming their designs will
magically be better. They forget that class inheritance does things that
interfaces don't. Also, using interfaces often makes your code less
readable (the number one sin in my development universe <g>). In this
particular instance, I see no value in making iterators interface-based,
but I do see value in using class inheritance. You can make your own
judgment, of course :-)
Quote
If the list of items being iterated over is large, this could take some time
if there are large gaps between items matching the filter criteria couldn't
it?
Sure, but creating a sublist won't necessarily be quicker. that is only
worthwhile doing on **very** large lists where you will be interating
over it many times in a row using the same filter. I would say do it the
easy way first, and optimise later if you find it is necessary. Another
bad thing many programmers do is optimise in advance of profiling :-)
Programmers a famously bad at picking bottlenecks (and for not believing
that they're bad at picking bottlenecks).
Cheers,
Jim Cooper
_______________________________________________
Jim Cooper XXXX@XXXXX.COM
Falafel Software www.falafelsoft.co.uk
_______________________________________________
 

Re: Avoiding creating a separate Iterator class...

<G>Thanks 'guys' for all the help :-)
I did start a long and interesting thread! <G>:)
Cheers,
Paul.
"Jim Cooper" <XXXX@XXXXX.COM>writes
Quote


>This person + some others seem to use iterators as an interface...is it
so
>bad to use an Interface?

IMO people are too quick to use interfaces everywhere - they seem to use
them as some sort of silver bullet, assuming their designs will
magically be better. They forget that class inheritance does things that
interfaces don't. Also, using interfaces often makes your code less
readable (the number one sin in my development universe <g>). In this
particular instance, I see no value in making iterators interface-based,
but I do see value in using class inheritance. You can make your own
judgment, of course :-)

>If the list of items being iterated over is large, this could take some
time
>if there are large gaps between items matching the filter criteria
couldn't
>it?

Sure, but creating a sublist won't necessarily be quicker. that is only
worthwhile doing on **very** large lists where you will be interating
over it many times in a row using the same filter. I would say do it the
easy way first, and optimise later if you find it is necessary. Another
bad thing many programmers do is optimise in advance of profiling :-)
Programmers a famously bad at picking bottlenecks (and for not believing
that they're bad at picking bottlenecks).

Cheers,
Jim Cooper

_______________________________________________

Jim Cooper XXXX@XXXXX.COM
Falafel Software www.falafelsoft.co.uk
_______________________________________________
 

Re: Avoiding creating a separate Iterator class...

Quote
I did start a long and interesting thread! <G>:)
Yes :-)
No-one mentioned it, but iterators are one of those things that will
benefit from generics too, when we get that in .NET sometime soon. Just
in case choosing between pure class inheritance and interfaces wasn't
enough :-)
Cheers,
Jim Cooper
_______________________________________________
Jim Cooper XXXX@XXXXX.COM
Falafel Software www.falafelsoft.co.uk
_______________________________________________
 

Re: Avoiding creating a separate Iterator class...

In article <XXXX@XXXXX.COM>, XXXX@XXXXX.COM
says...
Quote
Hi all, as to avoid creating a separate Iterator class for getting the items
of another class I have done this to make my life simpler:
function TRPG_Object.GetObject(const Index: Integer): IRPG_Object;
function TRPG_Object.FirstObject(var Index: Integer): IRPG_Object;
function TRPG_Object.NextObject(var Index: Integer): IRPG_Object;
That looks an awful lot like a lot of parsing classes I have put together
and seen over the years.
It's a system that works pretty well. Be prepared, though, to slap folks
who code logic around manipulating Index instead of going through proper
channels :)
You could always do something semantically sneaky to discourage it:
type
TRPG_Iterator = type Integer;
:)
-- Ritchie Annand
Senior Software Architect
www.malibugroup.com
nimble.nimblebrain.net
wiki.nimblebrain.net
 

Re: Avoiding creating a separate Iterator class...

In article <XXXX@XXXXX.COM>,
XXXX@XXXXX.COM says...
Quote
| The standard standard Iterator Design Pattern looks like this :
|
| ICustomerIterator = interface
| function Next: Boolean;
| function CurrentItem: ICustomer;
| procedure Reset;
| end;
Joanna, please allow me a small comment. I prefer this syntax:

ICustomerIterator = interface
function Next(out Item: ICustomer): Boolean;
procedure Reset;
end;
because it is more safe in cases of concurrent access, either because
of multithreading or of reentract code. The syntax you suggest is
impossible to be made reentrancy-safe.
Only in cases where the iterator itself can be used by multiple threads
at the same time, which is a construct I, with all the concurrency
coding I do these days, avoid like the plague.
Both your interfaces will work, although Joanna's will work for
concurrency if the iterator grabs and holds onto the current item itself
during the .Next. Doing a .Next and then having the .CurrentItem do a
read from the list would be disasterous in concurrent scenarios, agreed.
Comparing interfaces :)
I don't have any lists at all where nil has external meaning, so I end
up with the following (bidirectional) iterator:
_ITERATOR_CALLBACK_ = procedure(AItem: _ITEM_) of object;
_ITERATOR_INTF_ = interface
[_ITERATOR_IID_]
procedure ForEach(const ACallback: _ITERATOR_CALLBACK_);
function Next: _ITEM_;
function Previous: _ITEM_;
procedure SeekStart;
procedure SeekEnd;
end;
(Previous and SeekStart go away for unidirectional; I typically just ask
for a NewIterator when required)
-- Ritchie Annand
Senior Software Architect
www.malibugroup.com
nimble.nimblebrain.net
wiki.nimblebrain.net
 

Re: Avoiding creating a separate Iterator class...

In article <XXXX@XXXXX.COM>, bryan@%nospam%
ivisionsystems.com says...
Quote
You don't want to be iterating lists in a multithreaded environment, as you
have to do:
ListLock;
IterateList;
ListUnlock;
Ow, ow, ow :)
I've been compiling a list of don't-dos in concurrent programming, and
making external calls of potentially unknown length is right near the
top :)
Quote
Now - if that list is long, or the operation you're doing takes more than a
few microseconds, this method will block the other threads from this list
for a long time. This may mean the the user interface is unresponsive and
laggy, or the animation jitters, or network connections timeout.
I created a free-standing Synchronize() routine that doesn't have to be
called from within a TThread to carry that as far as possible. It's
really handy being able to do a little lengthy calculation in something
the TThread *calls* to update the screen.
procedure TProcessForm.Receive(ANotification: INotification);
var
TaskNotification : ITaskDoneNotification;
begin
if Supports(ANotification,ITaskDoneNotification,TaskNotification) then
if TaskNotification.Status=tdsDone then
begin
Resolver.Resolve(ImporterTask.OutputSource);
Synchronize(CreateObjectBrowser);
end;
end;
The notification is threaded, so this stays in a thread until the
Synchronize call itself.
Quote
With the iterator, you only lock the list while getting the next element.
If you've fallen off the end of the list, you stop iterating.
repeat
ListLock;
NextElement := GetNextElement;
ListUnlock;
If NextElement <>nil then
NextElement.DoSomething;
until NextElement = nil;
I use this sort of construct all the time :)
Local variables are pretty safe, but then there's the snag...
Quote
See the advantage? You're only blocking other threads while you get the
next element. You still have the problem of another thread freeing your
object while you're working with it though. I spent 2 years with a
colleague designing an architecture to get round this problem, but it could
never fully work.
The solution we found was to reference EVERYTHING you
want to access from multiple threads as an interface. Reference counting
does the rest for you.
That's probably one of the greatest attractions interfaces had for me
when I started to get into them. Because the _AddRef and _Release use
interlocked functions, they're quick, threadsafe and they solved *so
many* of my race condition and thread contention (making the locks
longer to solve the race conditions) problems.
.NET turns a few things on their ears, most notably in the realm of
destruction. Objects -and- interfaces are thread-safe in it, but the
destruction semantics are definitely NOT thread-safe and high
performance at the same time.
Quote
Bryan
Nifty stuff, Bryan :)
-- Ritchie Annand
Senior Software Architect
www.malibugroup.com
nimble.nimblebrain.net
wiki.nimblebrain.net
 

Re: Avoiding creating a separate Iterator class...

In article <XXXX@XXXXX.COM>, XXXX@XXXXX.COM
says...
Quote
This person + some others seem to use iterators as an interface...is it so
bad to use an Interface?
Jim makes the good point that you shouldn't declare an iterator
interface on the list itself (at least, not if you're going to use
something other than an index!).
Declaring them on an iterator class itself works well.
Here's one of my (admittedly overwrought) examples:
nimble.nimblebrain.net/download/TemplateDemo.zip
ProductList and GIterableLists.inc demonstrate putting together an
iterable list.
(If you don't want to use the Generics technique, it is relatively easy
to extract some code out of GIterableLists.inc to use)
-- Ritchie Annand
Senior Software Architect
www.malibugroup.com
nimble.nimblebrain.net
wiki.nimblebrain.net
 

Re: Avoiding creating a separate Iterator class...

In article <XXXX@XXXXX.COM>, XXXX@XXXXX.COM
says...
Quote
What if the ICustomer had a GUID through which each customer is
exclusively identified?
There would be two most salient types of access to the ICustomers in
the ICustomerList, one being an item-by-item iteration and the other a
random access iteration with the GUID as the search key.
Given that ICustomerList internally stores the items in some sort of
hash map both would be efficient access methods for different
purposes.
Simple question: Is it correct that the ICustomerList in this case had
two GetIterator methods, one for the successive and one for the random
access iterator?
Iterators are really only for sequential access. If you want to get
something in a random-access manner, it is probably best to call a method
on the list itself.
That's what I do :)
Occasionally, the list does the search... by creating an iterator and
running through it :)
-- Ritchie Annand
Senior Software Architect
www.malibugroup.com
nimble.nimblebrain.net
wiki.nimblebrain.net
 

Re: Avoiding creating a separate Iterator class...

Quote
Nifty stuff, Bryan :)
Well it is taken me six years to come to these conclusions... but just came
back from an industry
exhibition and we blew our competitors out of the water on rendering
smoothness - their tech people were all asking how I did it!
Mu-ha-ha-ha....
Bryan