Design Patterns – Importance of Not Exposing Internal Representation in Iterator Pattern

design-patternsiterator

I am reading C# Design Pattern Essentials. I'm currently reading about the iterator pattern.

I fully understand how to implement, but I don't understand the importance or see a use case. In the book an example is given where someone needs to get a list of objects. They could have done this by exposing a public property, such as IList<T> or an Array.

The book writes

The problem with this is that the internal representation in both of these classes has been exposed to outside projects.

What is the internal representation? The fact it's an array or IList<T>? I really don't understand why this is a bad thing for the consumer (the programmer calling this) to know…

The book then says this pattern works by exposing its GetEnumerator function, so we can call GetEnumerator() and expose the 'list' this way.

I assume this patterns has a place (like all) in certain situations, but I fail to see where and when.

Best Answer

Software is a game of promises and privileges. It is never a good idea to promise more than you can deliver, or more than your collaborator needs.

This applies particularly to types. The point of writing an iterable collection is that its user can iterate over it - no more, no less. Exposing the concrete type Array usually creates many additional promises, e.g. that you can sort the collection by a function of your own choosing, not to mention the fact that a normal Array will probably allow the collaborator to change the data that's stored inside it.

Even if you think this is a good thing ("If the renderer notices that the new export option is missing, it can just patch it right in! Neat!"), overall this decreases the coherence of the code base, making it harder to reason about - and making code easy to reason about is the foremost goal of software engineering.

Now, if your collaborator needs access to a number of thingies so that they are guaranteed not to miss any of them, you implement an Iterable interface and expose only those methods that this interface declares. That way, next year when a massively better and more efficient data structure appears in your standard library, you'll be able to switch out the underlying code and benefit from it without fixing your client code everywhere. There are other benefits to not promising more than is needed, but this one alone is so big that in practice, no others are needed.

Related Topic