Tengo algunas interfaces que pretendo que terceros implementen en el futuro, y yo mismo proporciono una implementación básica. Solo usaré un par para mostrar el ejemplo.
Actualmente, se definen como
Articulo:
public interface Item {
String getId();
String getName();
}
ItemStack:
public interface ItemStackFactory {
ItemStack createItemStack(Item item, int quantity);
}
ItemStackContainer:
public interface ItemStackContainer {
default void add(ItemStack stack) {
add(stack, 1);
}
void add(ItemStack stack, int quantity);
}
Ahora, Item
y ItemStackFactory
puedo prever absolutamente que un tercero necesitará extenderlo en el futuro. ItemStackContainer
También podría extenderse en el futuro, pero no de manera que pueda prever, fuera de mi implementación predeterminada proporcionada.
Ahora, estoy tratando de hacer esta biblioteca lo más robusta posible; esto todavía está en las primeras etapas (pre-pre-alfa), por lo que puede ser un acto de ingeniería excesiva (YAGNI). ¿Es este un lugar apropiado para usar genéricos?
public interface ItemStack<T extends Item> {
T getItem();
int getQuantity();
}
Y
public interface ItemStackFactory<T extends ItemStack<I extends Item>> {
T createItemStack(I item, int quantity);
}
Me temo que esto puede hacer que las implementaciones y el uso sean más difíciles de leer y comprender; Creo que es la recomendación evitar anidar genéricos siempre que sea posible.
Respuestas:
Utiliza genéricos en su interfaz cuando es probable que su implementación también sea genérica.
Por ejemplo, cualquier estructura de datos que pueda aceptar objetos arbitrarios es un buen candidato para una interfaz genérica. Ejemplos:
List<T>
yDictionary<K,V>
.Cualquier situación en la que desee mejorar la seguridad de tipos de forma generalizada es un buen candidato para los genéricos. A
List<String>
es una lista que solo opera en cadenas.Cualquier situación en la que desee aplicar SRP de forma generalizada y evitar métodos específicos de tipo es un buen candidato para los genéricos. Por ejemplo, un PersonDocument, PlaceDocument y ThingDocument se pueden reemplazar con a
Document<T>
.El patrón Abstract Factory es un buen caso de uso para un genérico, mientras que un Método Factory común simplemente crearía objetos que heredarían de una interfaz común y concreta.
fuente
class StackFactory { Stack<T> Create<T>(T item, int count); }
. Puedo escribir un ejemplo de lo que quise decir con "refinar un parámetro de tipo en una subclase" en una respuesta si desea más aclaraciones, aunque la respuesta solo estaría relacionada tangencialmente con la pregunta original.Su plan de cómo introducir generalidad a su interfaz me parece correcto. Sin embargo, su pregunta de si sería una buena idea o no requiere una respuesta algo más complicada.
A lo largo de los años, entrevisté a varios candidatos para puestos de Ingeniería de Software en nombre de las empresas para las que trabajé, y me di cuenta de que la mayoría de los solicitantes de empleo se sienten cómodos con el uso de clases genéricas (por ejemplo, clases de colección estándar), pero no con escribir clases genéricas. La mayoría nunca ha escrito una sola clase genérica en su carrera, y parece que se perderían por completo si se les pidiera que escribieran una. Por lo tanto, si fuerza el uso de genéricos sobre cualquier persona que desee implementar su interfaz o extender sus implementaciones base, puede estar posponiendo a las personas.
Sin embargo, lo que puede intentar es proporcionar versiones genéricas y no genéricas de sus interfaces y sus implementaciones básicas, de modo que las personas que no hacen genéricos puedan vivir felices en su mundo estrecho y pesado, mientras que las personas que Los genéricos pueden cosechar los beneficios de su comprensión más amplia del idioma.
fuente
Ahí está tu decisión tomada por ti. Si no proporciona genéricos, deberán actualizar su implementación
Item
cada vez que usen:Al menos debe hacer
ItemStack
genérico en la forma que sugiere. El beneficio más profundo de los genéricos es que casi nunca necesitas lanzar nada, y ofrecer código que obliga a los usuarios a lanzar es en mi humilde opinión un delito penal.fuente
Wow ... Tengo una opinión completamente diferente sobre esto.
Esto es un error. No deberías escribir código "para el futuro".
Además, las interfaces están escritas de arriba hacia abajo. No miras una clase y dices "Oye, esta clase podría implementar totalmente una interfaz y luego puedo usar esa interfaz en otro lugar también". Ese es un enfoque incorrecto, porque solo la clase que usa una interfaz realmente sabe cuál debería ser la interfaz. En su lugar, debería pensar: "En este lugar, mi clase necesita una dependencia. Permítanme definir una interfaz para esa dependencia. Cuando termine de escribir esta clase, escribiré una implementación de esa dependencia". Es irrelevante si alguna vez alguien reutilizará su interfaz y sustituirá su implementación predeterminada por la suya. Puede que nunca lo hagan. Esto no significa que la interfaz fuera inútil. Hace que su código siga el principio de Inversión de control, lo que hace que su código sea muy extensible en muchos lugares "
fuente
Creo que usar genéricos en su situación es demasiado complejo.
Si elige la versión genérica de las interfaces, el diseño indica que
Estas suposiciones y restricciones implícitas son cosas muy importantes para decidir cuidadosamente. Entonces, especialmente en la etapa inicial, estos diseños prematuros no deben ser introducidos. Simple, fácil de entender, se desea un diseño común.
fuente
Diría que depende principalmente de esta pregunta: si alguien necesita ampliar la funcionalidad de
Item
yItemStackFactory
,En el primer caso, no hay necesidad de genéricos, en el segundo, probablemente sí.
fuente
Tal vez, puede tener una jerarquía de interfaz, donde Foo es una interfaz, Bar es otra. Siempre que un tipo debe ser ambos, es un FooAndBar.
Para evitar un diseño excesivo, solo haga el tipo FooAndBar cuando sea necesario y mantenga una lista de interfaces que nunca extiendan nada.
Esto es mucho más cómodo para la mayoría de los programadores (a la mayoría les gusta su verificación de tipos o les gusta nunca tratar con la escritura estática de ningún tipo). Muy pocas personas han escrito su propia clase de genéricos.
También como nota: las interfaces tratan sobre las posibles acciones que se pueden ejecutar. En realidad deberían ser verbos, no sustantivos.
fuente