Design patterns in the test of timeIterator
In object-oriented programming, the iterator pattern is a design pattern in which an iterator is used to traverse a container and access the container's elements. The iterator pattern decouples algorithms from containers; in some cases, algorithms are necessarily container-specific and thus cannot be decoupled.
It is really hard to think about any other pattern that has been more successful. In particular, patterns have long been about overcoming shortcoming of the language or platform.
In this case, iterators has became part of both language and platform in most modern systems.
- System.Collection.IEnumerable
- java.util.Iterator
- Python’s __iter__()
Basically, it is so good, it is everywhere.
More posts in "Design patterns in the test of time" series:
- (21 Jan 2013) Mediator
- (18 Jan 2013) Iterator
- (17 Jan 2013) Interpreter
- (21 Nov 2012) Command, Redux
- (19 Nov 2012) Command
- (16 Nov 2012) Chain of responsibility
- (15 Nov 2012) Proxy
- (14 Nov 2012) Flyweight
- (09 Nov 2012) Façade
- (07 Nov 2012) Decorator
- (05 Nov 2012) Composite
- (02 Nov 2012) Bridge
- (01 Nov 2012) Adapter
- (31 Oct 2012) Singleton
- (29 Oct 2012) Prototype
- (26 Oct 2012) Factory Method
- (25 Oct 2012) Builder
- (24 Oct 2012) A modern alternative to Abstract Factory–filtered dependencies
- (23 Oct 2012) Abstract Factory
Comments
Ah, remembering those days when I first met them, C++ STL anyone? ;)
// Ryan
Ayende, what do you think about to write a couple of posts on how things are organized in Hibernating Rhinos? It would be interesting to read about your CI process, development workflow, internal rules, priorities management, etc...
Ayende, did you know that Scott Hanselman and Rob Conery make jokes of your voice at the end of Hanselminutes 353? :D
Sergey, Sure, do you have any specific questions?
The Iterator pattern may be omnipresent, but IMHO that doesn't say anything about its current value: - The abstraction level of the pattern is too low: In order to work, the pattern forces you to place a loop into your code. It's a code smell in my book, if someone else's code dictates the control structures in my code. Iterating over a collection can be abstracted away in a forEach() method, that takes a callback handler, which in turn is invoked for every element of the collection. Every SAX parser works like this. - Because of the low abstraction level the Iterator pattern effectively prohibits parallel execution of a piece of code. Nowadays, that is almost bad in itself. With a dedicated forEach() method on the other hand, that's rather simple: Mark the callback handler as side-effect-free by flag, annotation or marker interface and the iteration can be parallelized. By comparison, every SQL statement is written in a way that allows the database to execute it in parallel. - An iterator is stateful and mutable. Passing around an iterator as an argument leads to complex coupling of the affected code. Refactoring such code will be arduous at best.
Frisian, Where on earth did you get the idea that iterator forbid parallel execution. See Parallel.For and Parallel.ForEach as good counter examples. Sure, iterator is low level, but most design patterns are.
I am not a C# programmer, but what I could take from an example for Parallel.ForEach (http://msdn.microsoft.com/en-us/library/dd460720.aspx) seems to prove my point: it's a method with a callback, that is presented one element at a time. And that is definitely not the GoF-Iterator, for which you provided the Wikipedia link in your article. Interestingly, both "standard" implementations (IEnumerator in C#, Iterator in Java) share a common problem: they are mixing command and query. Iterator.next() not only returns the current element, but also advances to the next one. IEnumerator.MoveNext() moves to the next element and tells, if there is a next element at all. Likewise, they both don't adhere to "Tell, don't ask", thus promoting coupling. Lastly, I wasn't complaining about the abstraction level being "low", but being "too low". The loop really doesn't belong on the caller's side.
Frisian, Next / MoveNext does NOT do two things. It tries to move to the next one, returning true if it succeeded. The return value is if the next operation was successful, not if the future next operation will be. And Parallel.ForEach works using an iterator.
Comment preview