Me he acostumbrado a utilizar un principio para diseñar y consumir interfaces que dice básicamente: "pida solo lo que necesita".
Por ejemplo, si tengo un montón de tipos que se pueden eliminar, haré una Deletable
interfaz:
interface Deletable {
void delete();
}
Entonces puedo escribir una clase genérica:
class Deleter<T extends Deletable> {
void delete(T t) {
t.delete();
}
}
En otras partes del código, siempre pediré la menor responsabilidad posible para cumplir con las necesidades del código del cliente. Entonces, si solo necesito eliminar un File
, aún pediré un Deletable
, no un File
.
¿Es este principio de conocimiento común y ya tiene un nombre aceptado? ¿Es controvertido? ¿Se discute en los libros de texto?
object-oriented
interfaces
solid
single-responsibility
glenviewjeff
fuente
fuente
Respuestas:
Creo que esto se refiere a lo que Robert Martin llama el Principio de segregación de interfaz . Las interfaces se separan en pequeñas y concisas para que los consumidores (clientes) solo tengan que conocer los métodos que les interesan. Puedes ver más en SOLID .
fuente
Para ampliar la muy buena respuesta de Vadim, responderé la pregunta "¿es controvertido" con "no, no realmente".
En general, la segregación de la interfaz es algo bueno, al reducir el número total de "razones para cambiar" de los diversos objetos involucrados. El principio básico es que, cuando se debe cambiar una interfaz con varios métodos, por ejemplo, para agregar un parámetro a uno de los métodos de la interfaz, todos los consumidores de la interfaz deben al menos volver a compilarse, incluso si no utilizaron el método que cambió. "¡Pero es solo una recompilación!", Te escucho decir; eso puede ser cierto, pero tenga en cuenta que, por lo general, cualquier cosa que recompile debe eliminarse como parte de un parche de software, sin importar cuán significativo sea el cambio en el binario. Estas reglas se conceptualizaron originalmente a principios de los años 90, cuando la estación de trabajo de escritorio promedio era menos potente que el teléfono en su bolsillo, el marcado de 14.4k baudios era increíble, y 3.5 "1.44MB" disquetes "eran los principales medios extraíbles. Incluso en la era actual de 3G / 4G, los usuarios de Internet inalámbrico a menudo tienen planes de datos con límites, por lo que al lanzar una actualización, cuantos menos binarios se deben descargar, mejor.
Sin embargo, como todas las buenas ideas, la segregación de la interfaz puede ir mal si se implementa incorrectamente. En primer lugar, existe la posibilidad de que al segregar las interfaces mientras se mantiene el objeto que implementa esas interfaces (cumpliendo las dependencias) relativamente sin cambios, puede terminar con una "Hidra", un pariente del antipatrón "Objeto de Dios" donde el la naturaleza omnisciente y todopoderosa del objeto está oculta para los dependientes por las interfaces estrechas. Terminas con un monstruo de muchas cabezas que es al menos tan difícil de mantener como lo sería el Objeto de Dios, más la sobrecarga de mantener todas sus interfaces. No hay una gran cantidad de interfaces que no debe exceder, pero cada interfaz que implemente en un solo objeto debe ser precedida respondiendo la pregunta: "¿Esta interfaz contribuye al objeto '
En segundo lugar, una interfaz por método puede no ser necesaria, a pesar de lo que SRP pueda decirle. Puede terminar con "código de ravioles"; tantos trozos del tamaño de un bocado que es difícil de rastrear para descubrir exactamente dónde suceden realmente las cosas. Tampoco es necesario dividir una interfaz con dos métodos si todos los usuarios actuales de esa interfaz necesitan ambos métodos. Incluso si una de las clases dependientes solo necesita uno de los dos métodos, generalmente es aceptable no dividir la interfaz si sus métodos conceptualmente tienen una cohesión muy alta (buenos ejemplos son "métodos antonímicos" que son exactamente opuestos entre sí).
La segregación de interfaz debe basarse en las clases que dependen de la interfaz:
Si solo hay una clase dependiente de la interfaz, no la segregue. Si la clase no usa uno o más de los métodos de interfaz, y es el único consumidor de la interfaz, es probable que no debas haber expuesto esos métodos en primer lugar.
Si hay más de una clase que depende de la interfaz y todos los dependientes usan todos los métodos de la interfaz, no los segregue; si debe cambiar la interfaz (para agregar un método o cambiar una firma), todos los consumidores actuales se verán afectados por el cambio, ya sea que lo separe o no (aunque si agrega un método que al menos un dependiente no necesitará, considere cuidadosamente si el cambio debe implementarse como una nueva interfaz, posiblemente heredada de la existente).
Si hay más de una clase dependiente de la interfaz, y no utilizan los mismos métodos, es un candidato para la segregación. Mire la "coherencia" de la interfaz; ¿Todos los métodos promueven un único objetivo de programación muy específico? Si puede identificar más de un propósito central para la interfaz (y sus implementadores), considere dividir las interfaces a lo largo de esas líneas para crear interfaces más pequeñas con menos "razones para cambiar".
fuente
IBaz : IFoo, IBar
y requerirlo.IFoo
yIBar
, definir una composiciónIFooBar
puede ser una buena idea, pero si las interfaces se dividen finamente, es fácil terminar requiriendo docenas de tipos de interfaz distintos. Tenga en cuenta las siguientes características que las colecciones pueden tener: enumerar, contar recuento, leer el enésimo elemento, escribir el enésimo elemento, insertar antes del enésimo elemento, eliminar el enésimo elemento, nuevo elemento (ampliar la colección y devolver el índice del nuevo espacio) y agregar. Nueve métodos: ECRWIDNA. Probablemente podría describir docenas de tipos que naturalmente admitirían muchas combinaciones diferentes.