Fiquei me perguntando o que torna o Iterator especial quando comparado a outras construções semelhantes, e isso fez o Gang of Four listá-lo como um padrão de design.
O Iterador é baseado no polimorfismo (uma hierarquia de coleções com uma interface comum) e na separação de preocupações (a iteração sobre as coleções deve ser independente da forma como os dados são estruturados).
Mas e se substituirmos a hierarquia de coleções por, por exemplo, uma hierarquia de objetos matemáticos (número inteiro, float, complexo, matriz etc.) e o iterador por uma classe que representa algumas operações relacionadas a esses objetos, por exemplo, funções de poder. O diagrama da classe seria o mesmo.
Provavelmente, poderíamos encontrar muitos exemplos semelhantes, como Writer, Painter, Encoder e provavelmente melhores, que funcionam da mesma maneira. No entanto, eu nunca ouvi nenhum deles ser chamado de Padrão de Design.
Então, o que torna o Iterator especial?
É o fato de ser mais complicado porque requer um estado mutável para armazenar a posição atual na coleção? Mas então, o estado mutável geralmente não está sendo considerado desejável.
Para esclarecer meu argumento, deixe-me dar um exemplo mais detalhado.
Aqui está o nosso problema de design:
Digamos que temos uma hierarquia de classes e uma operação definida nos objetos dessas classes. A interface desta operação é a mesma para cada classe, mas as implementações podem ser completamente diferentes. Supõe-se também que faz sentido aplicar a operação várias vezes no mesmo objeto, digamos com parâmetros diferentes.
Aqui está uma solução sensata para o nosso problema de design (praticamente uma generalização do padrão do iterador):
Para separação de preocupações, as implementações da operação não devem ser adicionadas como funções à hierarquia de classes original (objetos de operando). Como queremos aplicar a operação várias vezes no mesmo operando, ela deve ser representada por um objeto que mantém uma referência ao operando, não apenas por uma função. Portanto, o objeto operando deve fornecer uma função que retorna o objeto que representa a operação. Este objeto fornece uma função que executa a operação real.
Um exemplo:
Existe uma classe base ou interface MathObject
(nome estúpido, eu sei, talvez alguém tenha uma idéia melhor.) Com classes derivadas MyInteger
e MyMatrix
. Para cada MathObject
uma operação Power
deve ser definida que permita o cálculo de quadrado, cubo e assim por diante. Para podermos escrever (em Java):
MathObject i = new MyInteger(5);
Power powerOfFive = i.getPower();
MyInteger square = powerOfFive.calculate(2); // should return 25
MyInteger cube = powerOfFive.calculate(3); // should return 125