Creo que una ventaja del MoveNext()
modelo de C # /. NET sobre el modelo de Java hasNext()
es que el primero implica que se puede hacer algún trabajo. El hasNext()
método implica una simple verificación de estado. Pero, ¿qué sucede si está iterando una secuencia diferida y tiene que ir a una base de datos para determinar si hay algún resultado? En ese caso, la primera llamada a hasNext()
puede ser una operación de bloqueo larga. El nombramiento del hasNext()
método puede ser muy engañoso en cuanto a lo que realmente está sucediendo.
Para un ejemplo del mundo real, este problema de diseño me mordió al implementar las API de Extensiones Reactivas (Rx) de Microsoft en Java. Específicamente, para que el iterador creado por IObservable.asIterable()
funcione correctamente, el hasNext()
método tendría que bloquear y esperar a que llegue la siguiente notificación de elemento / error / finalización. Eso no es intuitivo: el nombre implica una simple verificación de estado. Compare eso con el diseño de C #, donde MoveNext()
tendría que bloquear, lo que no es un resultado totalmente inesperado cuando se trata de una secuencia evaluada de manera perezosa.
Mi punto es este: el modelo de "siguiente iterador" es preferible al modelo de "este iterador" porque el modelo de "este iterador" es a menudo una mentira: es posible que deba buscar previamente el siguiente elemento para verificar El estado del iterador. Un diseño que comunique esa posibilidad claramente es preferible, en mi opinión. Sí, las convenciones de nomenclatura de Java siguen el modelo "siguiente", pero las implicaciones de comportamiento son similares a las del modelo "este iterador".
Aclaración: Quizás contrastar Java y C # fue una mala elección, porque el modelo de Java es engañoso. Considero que la distinción más importante en los modelos presentados por el OP es que el modelo de "este iterador" desacopla la lógica de "es válido" de la lógica de "recuperar el elemento actual / próximo", mientras que es una implementación ideal de "siguiente iterador" combina estas operaciones. Creo que es apropiado combinarlos porque, en algunos casos, determinar si el estado del iterador es válido requiere la captación previa del siguiente elemento , por lo que esa posibilidad debe hacerse lo más explícita posible.
No veo una diferencia de diseño significativa entre:
while (i.isValid()) { // do we have an element? (implied as fast, non-blocking)
doSomething(i.current()); // retrieve the element (may be slow!)
i.next();
}
// ...and:
while (i.hasNext()) { // do we have an element? (implied as fast, non-blocking)
doSomething(i.next()); // retrieve the element (may be slow!)
}
Pero veo una diferencia significativa aquí:
while (i.hasNext()) { // do we have an element? (implied as fast, non-blocking)
doSomething(i.next()); // retrieve the element (may be slow!)
}
// ...and:
while (i.moveNext()) { // fetch the next element if it exists (may be slow!)
doSomething(i.current()); // get the element (implied as fast, non-blocking)
}
Tanto en el ejemplo isValid()
como en el hasNext()
ejemplo, la operación que implica una verificación de estado rápida y sin bloqueo puede, de hecho, ser una operación de bloqueo lenta . En el moveNext()
ejemplo, la mayor parte del trabajo se realiza en el método que esperaría, independientemente de si se trata de una secuencia evaluada con impaciencia o con pereza.