Tengo un colega sentado a mi lado que diseñó una interfaz como esta:
public interface IEventGetter {
public List<FooType> getFooList(String fooName, Date start, Date end)
throws Exception;
....
}
El problema es que, en este momento, no estamos usando este parámetro de "fin" en ninguna parte de nuestro código, solo está ahí porque podríamos tener que usarlo en algún momento en el futuro.
Estamos tratando de convencerlo de que es una mala idea poner parámetros en interfaces que no son útiles en este momento, pero él sigue insistiendo en que habrá que hacer mucho trabajo si implementamos el uso de la fecha de "finalización" en algún momento luego y tengo que adaptar todo el código entonces.
Ahora, mi pregunta es, ¿hay alguna fuente que esté manejando un tema como este de gurús de codificación "respetados" con los que podamos vincularlo?
end
parámetro a ese objeto e incluso predeterminarlo para no romper el códigonull
. La implementación de clases puede anularse según sea necesario.IQueryable
(solo puede tomar ciertas expresiones) al código fuera del DALRespuestas:
Invítalo a aprender sobre YAGNI . La parte de Justificación de la página de Wikipedia puede ser particularmente interesante aquí:
Otros posibles argumentos:
"El 80% del costo de por vida de un software se destina al mantenimiento" . Escribir código justo a tiempo reduce el costo del mantenimiento: uno tiene que mantener menos código y puede concentrarse en el código realmente necesario.
El código fuente se escribe una vez, pero se lee docenas de veces. Un argumento adicional, que no se usa en ningún lado, llevaría a perder el tiempo entendiendo por qué hay un argumento que no es necesario. Dado que esta es una interfaz con varias implementaciones posibles, las cosas solo son más difíciles.
Se espera que el código fuente sea autodocumentado. La firma real es engañosa, ya que un lector pensaría que
end
afecta el resultado o la ejecución del método.Las personas que escriben implementaciones concretas de esta interfaz pueden no entender que el último argumento no debe usarse, lo que conduciría a diferentes enfoques:
No necesito
end
, así que simplemente ignoraré su valor,No necesito
end
, así que lanzaré una excepción si no es asínull
,No lo necesito
end
, pero intentaré usarlo de alguna manera,Escribiré un montón de código que podría usarse más tarde cuando
end
sea necesario.Pero tenga en cuenta que su colega puede tener razón.
Todos los puntos anteriores se basan en el hecho de que la refactorización es fácil, por lo que agregar un argumento más adelante no requerirá mucho esfuerzo. Pero esta es una interfaz y, como interfaz, puede ser utilizada por varios equipos que contribuyen a otras partes de su producto. Esto significa que cambiar una interfaz puede ser particularmente doloroso, en cuyo caso, YAGNI realmente no se aplica aquí.
La respuesta de hjk ofrece una buena solución: agregar un método a una interfaz ya utilizada no es particularmente difícil, pero en algunos casos, también tiene un costo considerable:
Algunos marcos no admiten sobrecargas. Por ejemplo, y si recuerdo bien (corrígeme si me equivoco), WCF de .NET no admite sobrecargas.
Si la interfaz tiene muchas implementaciones concretas, agregar un método a la interfaz requeriría revisar todas las implementaciones y agregar el método allí también.
fuente
(algún tiempo después)
Eso es todo lo que necesitas, sobrecarga de métodos. El método adicional en el futuro se puede introducir de forma transparente sin afectar las llamadas al método existente.
fuente
end
parámetro es innecesario ahora y ha utilizado elend
método sin uso en todo el proyecto, entonces la actualización de la interfaz es la menor de sus preocupaciones porque se convierte en una necesidad de actualizar el clases de implementación Mi respuesta está dirigida específicamente a la parte de "adaptar todo el código", porque parece que el colega estaba pensando en la línea de tener que actualizar a los llamantes del método original en todo el proyecto para introducir un tercer parámetro predeterminado / ficticio .Las apelaciones a la autoridad no son particularmente convincentes; mejor presentar un argumento que sea válido sin importar quién lo haya dicho.
Aquí hay una reducción ad absurdum que debería persuadir o demostrar que su compañero de trabajo está atascado en ser "correcto" independientemente de su sensibilidad:
Lo que realmente necesitas es
Nunca se sabe cuándo tendrá que internacionalizarse para Klingon, por lo que es mejor que se encargue de esto ahora porque requerirá mucho trabajo para su adaptación y los Klingon no son conocidos por su paciencia.
fuente
You never know when you'll have to internationalize for Klingon
. Es por eso que solo debe pasar unCalendar
, y dejar que el código del cliente decida si quiere enviar unGregorianCalendar
o unKlingonCalendar
(apuesto a que algunos ya lo hicieron). Duh :-pStarDate sdStart
yStarDate sdEnd
? No podemos olvidarnos de la Federación después de todo ...Date
!HebrewDate start
yHebrewDate end
.Desde una perspectiva de ingeniería de software, creo que la solución adecuada para este tipo de problemas está en el patrón del constructor. Este es definitivamente un enlace de autores 'gurú' para su colega http://en.wikipedia.org/wiki/Builder_pattern .
En el patrón de construcción, el usuario crea un objeto que contiene los parámetros. Este contenedor de parámetros se pasará al método. Esto se encargará de cualquier extensión y sobrecarga de parámetros que su colega pueda necesitar en el futuro, al tiempo que hará que todo sea muy estable cuando sea necesario realizar cambios.
Tu ejemplo se convertirá en:
fuente
IEventGetter
aISearchFootRequest
. En cambio, sugeriría algo comopublic IFooFilter
with memberpublic bool include(FooType item)
que devuelve si se incluye o no el elemento. Luego, las implementaciones de interfaz individual pueden decidir cómo harán ese filtradoNo lo necesita ahora, así que no lo agregue. Si lo necesita más tarde, extienda la interfaz:
fuente
Ningún principio de diseño es absoluto, por lo que, si bien estoy de acuerdo con las otras respuestas, pensé en jugar al abogado del diablo y discutir algunas condiciones bajo las cuales consideraría aceptar la solución de su colega:
Now()
, agregar un parámetro elimina un efecto secundario, que tiene beneficios para el almacenamiento en caché y las pruebas unitarias. Tal vez permite una implementación más simple. O tal vez sea más consistente con otras API en su código.Dicho esto, en mi experiencia, el corolario más grande para YAGNI es YDKWFYN: No sabes qué forma necesitarás (sí, acabo de hacer ese acrónimo). Incluso si la necesidad de algún parámetro de límite puede ser relativamente predecible, puede tomar la forma de un límite de página, o un número de días, o un valor booleano que especifica el uso de la fecha de finalización de una tabla de preferencias del usuario, o cualquier cantidad de cosas.
Como todavía no tiene el requisito, no tiene forma de saber qué tipo de parámetro debería ser. A menudo terminas teniendo una interfaz incómoda que no es la que mejor se ajusta a tus necesidades, o teniendo que cambiarla de todos modos.
fuente
No hay suficiente información para responder esta pregunta. Depende de lo que
getFooList
realmente hace y cómo lo hace.Aquí hay un ejemplo obvio de un método que debería admitir un parámetro adicional, usado o no.
Implementar un método que opera en una colección donde puede especificar el inicio pero no el final de la colección es a menudo tonto.
Realmente tiene que mirar el problema en sí y preguntar si el parámetro no tiene sentido en el contexto de toda la interfaz, y realmente cuánta carga impone el parámetro adicional.
fuente
Me temo que su colega puede tener un punto muy válido. Aunque su solución en realidad no es la mejor.
De su interfaz propuesta está claro que
está devolviendo instancias encontradas dentro de un intervalo de tiempo. Si los clientes actualmente no usan el parámetro final, eso no cambia el hecho de que esperan instancias encontradas dentro de un intervalo de tiempo. Simplemente significa que actualmente todos los clientes usan intervalos abiertos (desde el comienzo hasta la eternidad)
Entonces una mejor interfaz sería:
Si proporciona un intervalo con un método de fábrica estático:
entonces los clientes ni siquiera sentirán la necesidad de especificar una hora de finalización.
Al mismo tiempo, su interfaz transmite más correctamente lo que hace, ya
getFooList(String, Date)
que no comunica que se trata de un intervalo.Tenga en cuenta que mi sugerencia se deriva de lo que el método hace actualmente, no de lo que debería o podría hacer en el futuro, y como tal, el principio YAGNI (que es realmente válido) no se aplica aquí.
fuente
Agregar un parámetro no utilizado es confuso. La gente podría llamar a ese método suponiendo que esta característica funcione.
No lo agregaría. Es trivial agregarlo luego usando una refactorización y arreglando mecánicamente los sitios de llamadas. En un lenguaje escrito estáticamente, esto es fácil de hacer. No es necesario agregarlo ansiosamente.
Si no es una razón para tener ese parámetro puede evitar la confusión: Añadir una aserción en la aplicación de esa interfaz para hacer cumplir que este parámetro se pasa como el valor predeterminado. Si alguien usa accidentalmente ese parámetro al menos, notará inmediatamente durante la prueba que esta característica no está implementada. Esto elimina el riesgo de deslizar un error en la producción.
fuente