design - pattern - Interfaces inĂștiles
design patterns pdf (25)
¿Por qué usarías alguna vez la interfaz? ¿Tendrás solo una implementación de la misma?
Sí, especialmente con Delphi.
Si tiene una referencia de interfaz a un objeto, obtiene recuento de referencia automáticamente. Por lo tanto, es muy común tener una interfaz con una sola implementación cuando desee que el objeto se limpie automáticamente. El recuento de referencias es mejor que la recolección de basura, ya que se llama al destructor del objeto tan pronto como la última referencia sale del alcance o ya no hace referencia a ella.
A menudo hago esto y por varias razones:
1) Le permite especificar qué características de una clase se están usando realmente fuera de la clase. Esto le dice qué características siempre necesita admitir, siempre y cuando esa interfaz no se modifique. Esto le permite tener otros métodos públicos en esa clase por cualquier razón (para admitir otras interfaces u otros objetos que tengan una instancia del objeto y se refieran a él por el nombre de clase real en lugar de a través de la interfaz).
2) También recorta la lista proporcionada a través de intellisense a solo las características admitidas por la interfaz.
3) Y como se indicó anteriormente, es útil para pruebas unitarias y marcos de burla.
Algunas personas siempre crean una interfaz porque pueden autogenerarse, por lo que justifican las interfaces de creación de tiempo de trabajo, sin importar si realmente se usan o no.
Por ejemplo, a veces los desarrolladores se evalúan por línea de código escrito, por lo que tiene sentido utilizar la interfaz.
Algunas tecnologías requieren que uses una interfaz. COM para uno. Su a menudo tiene solo una clase implementando su interfaz.
Cuando trabajo con alguien más en un proyecto, por ejemplo, si hago el front-end (aplicación web, por ejemplo) y la otra persona hace que funcione toda la base de datos, comenzamos escribiendo una API. El lado que me enfrenta es todo sobre el dominio del problema: clases para el usuario, administrador, empleado, SKU o lo que sea. Entonces podemos trabajar de forma independiente; ella implementa todas las interfaces y yo escribo el código que las usa.
En algunos casos, para la visibilidad: es posible que desee que ciertas partes del código vean la clase concreta y tengan acceso a los configuradores, y que otros solo vean captadores: déles acceso únicamente a la interfaz.
Esto no siempre se puede lograr con public / protected / private.
Estoy de acuerdo en que veo esto todo el tiempo y es una tontería. Si por el momento su implementación es única y es poco probable que sea más, dejaré que el siguiente desarrollador identifique e implemente la interfaz cuando lo considere apropiado.
La capacidad de expandirse más adelante en el futuro.
Las interfaces de un solo método generalmente se pueden evitar cuando trabajas en idiomas que te permiten pasar funciones como valores de primer orden. Por nombrar algunos ejemplos:
Interfaces de un solo método para pasar fragmentos de la lógica de implementación:
public interface IComparable<T>
{
int CompareTo(T first, T second);
}
public static class Array<T>
{
public void Sort(T[] input)
{
if (T is IComparable) { /* sorting implementation */ }
else { Throw new Exception("Doesn''t implement IComparable"); }
}
}
Puede ser reemplazado por:
public static class Array<T>
{
public void Sort(T[] input, Func<T, T, int> compare)
{
/* sorting implementation */
}
}
Considero que el estilo funcional es más legible y reutilizable.
Interfaces de un solo método para la inyección / burla de dependencia:
public interface IFailureNotifier
{
void HandleFailure(Exception ex);
}
public class TransactionProcessor
{
public IFailureNotifier FailureNotifier { get; set; }
public TransactionProcessor(IFailureNotifier failureNotifier)
{
this.FailureNotifier = failureNotifier;
}
public void ProcessItems(object[] items)
{
try
{
for(object item in items) { /* do stuff */ }
}
catch(Exception ex)
{
FailureNotifier.HandleFailure(ex);
}
}
}
Se puede volver a escribir como:
public class TransactionProcessor
{
public Action<Exception> FailureNotifier { get; set; }
public TransactionProcessor(Action<Exception> failureNotifier)
{
this.FailureNotifier = failureNotifier;
}
public void ProcessItems(object[] items)
{
try
{
for(object item in items) { /* do stuff */ }
}
catch(Exception ex)
{
FailureNotifier(ex);
}
}
}
La ventaja de este enfoque es una biblioteca de clases más simple: no necesito una sopa de objetos diminutos para implementar el único método de IFailureNotifier, sino que simplemente paso la implementación directamente.
Eso no quiere decir que las interfaces de un solo método sean malas , pero aún desea concluir una función en una interfaz si la función depende del estado mutable subyacente. Sin embargo, personalmente encuentro que la mayoría de los beneficios de las interfaces de un solo método ya los proporcionan las funciones de primera clase.
Para promover el acoplamiento flexible, se considera que un mejor diseño depende de una dependencia abstracta que la de atarse a una clase concreta.
Un ejemplo de donde esta práctica es especialmente útil es el enfoque remoto basado en proxy de Spring, donde su única implementación reside en una JVM diferente.
Para separar la API de la implementación, que a menudo es una buena práctica de programación. Ayudará con la legibilidad, si nada más. También permitirá que alguien que use su código en el futuro proporcione una implementación alternativa de la interfaz si así lo desean.
Porque se pierden los archivos de encabezado C ++?
Prefiero no definir una interfaz, a menos que realmente lo necesite. IMO, la interfaz es uno de los patrones de diseño más abusados. Creo que el caso al que se refiere se debe a la pregunta "¿Y si ... será necesario en el futuro?". Dado que el desarrollador aún no es un adivino ... me quedaría con la interfaz de "bajo uso".
Si está trabajando con un sistema que permite la refactorización, solo debe agregar interfaces si es necesario para la especificación (por ejemplo, una API externa) o si necesita múltiples implementaciones. Considero que los objetos de prueba son interpretaciones válidas, por lo que si necesita una interfaz para poder probarla, ese es un buen uso de las interfaces.
Los objetos de valor rara vez deberían convertirse en interfaces, los objetos de servicio deberían convertirse frecuentemente en interfaces.
Si realmente hay una sola implementación y solo una implementación, no codifique una interfaz. (YAGNI).
Sin embargo, el 99% del tiempo hay al menos dos implementaciones de una clase, una real y otra utilizada en las pruebas.
La facilidad de separar y burlarse de partes de un sistema durante la prueba es más que vale la pena el esfuerzo adicional de crear una interfaz para una clase.
Como nota al margen, y tal vez esto se deba a que carezco de control personal, la codificación en las interfaces me mantiene mucho más enfocado cuando estoy trabajando en una clase en particular. Me encuentro pensando más en "y esta interfaz que estoy llamando volverá / haré esto" en lugar de "y esta clase soy así, llama x, transforma y, se comunica con z, pone el café, fluffs el almohadas, integra n con respecto a y y luego devuelve una instancia de mono ... espera, ¿qué estaba haciendo otra vez? "
Si solo tiene una implementación, no lo haría. Los argumentos presentados aquí que podrían tener múltiples implementaciones no son realmente un examen detenido. Si termina con varias implementaciones, la extracción de la interfaz tarda aproximadamente 5 segundos en utilizar las herramientas integradas de Visual Studio o Resharper.
Entonces sí, YAGNI: no compliques tu vida hasta que tengas que hacerlo.
Si supiera de hecho que solo habría una implementación, no crearía una interfaz. Esto cae bajo YAGNI, IMO.
(Por supuesto, es raro que sepa algo sobre el futuro por un hecho ...)
También me molesta cuando las personas hacen una interfaz, un resumen y una implementación real para cada clase, incluso si nunca habrá más de una, y los 3 archivos están casi vacíos.
Sin embargo, los grandes usos serían:
Expansión futura / mejoras placeholder
Fácil de implementar Inversión de Control / Inyección de Dependencia
Simulacros fáciles de implementar para pruebas unitarias.
*Editar:
Noté que también tienes Spring en tus etiquetas. Si usa Spring, entonces # 2 arriba es probablemente el más grande. Es más fácil diseñar clases que esperan interfaces, y puede cambiar las implementaciones reales de la interfaz en la configuración de Spring (Dependency Injection).
Un ejemplo de dónde puede tener sentido crear una interfaz para una sola clase es (como han mencionado otros) cuando podríamos esperar que se extienda.
Por ejemplo, si tengo una herramienta de seguimiento que estoy creando para mi uso personal, usará SQL Server. Estoy creando una interfaz para la abstracción de bases de datos a pesar de que los métodos de DB están contenidos en una sola clase. La razón es que simplificará el trabajo de extender la aplicación para usar otras plataformas de bases de datos.
Me doy cuenta de que ya existen tipos similares de abstracciones en el framework .NET, pero pensé que este era un ejemplo pertinente.
Una razón podría ser el desacoplamiento: si sus clases son utilizadas por otro desarrollador, puede darle el código fuente para las interfaces y mantener los detalles de implementación para usted.
Si la implementación cambia, y el "contrato" definido en la interfaz no lo hace, usted estará contento: su interfaz aún describe lo que hace la clase, y nadie tiene que saber cómo lo hace.
Una situación para una interfaz para una sola implementación: RMI. Tiene un objeto en una aplicación y lo usará desde otra aplicación a través de RMI; necesitas tener una interfaz De esta forma, ni siquiera tiene que incluir la clase de implementación en la aplicación que realiza la llamada, lo que podría evitar que la aplicación que llama incluya una gran cantidad de material J2EE o bibliotecas externas que la implementación podría usar.
Otra razón: hoy solo tiene una implementación, pero mañana puede haber algo en el JDK o en una biblioteca externa que podría llevar a cambiar esa implementación (tal vez creando una nueva). Su sistema podría cambiar y necesita agregar una nueva implementación, manteniendo la anterior; algo de mantenimiento, etc.
Varias de las otras respuestas mencionan "trabajar con otros", y esto es ciertamente una preocupación. Un proyecto con 10 programadores va a tener que abordar las cosas de manera diferente que un proyecto con uno, y desafortunadamente, diseñar una arquitectura de software de tal manera que varias personas puedan contribuir es algo que las personas siempre aprenden de la manera difícil.
¿Alguien puede sugerir buenos libros o artículos?
porque podrías terminar teniendo dos o más en el futuro
ADICIÓN Después de leer algunos de los comentarios: burla del objeto: esto implicaría que usted TENDRÍA más de una implementación de su interfaz, YORU Fingue ES otra implementación.
porque una interfaz es la más adecuada para indicar definitivamente el tipo
Puede ser una muy buena práctica configurar una interface
como una especificación para codificar su class
.
Si determina los métodos / funcionalidades públicos que tendrá su interface
, puede bloquearlos. Entonces es mucho más fácil codificar una class
cuando tienes una funcionalidad clara en mente para ella.
Creo que es más importante hacer que escribir un buen código sea más fácil que mantener limpia la base del código.