tutorialspoint sourcemaking patterns pattern ebook behavioral antipatterns and c# design-patterns code-smell

c# - sourcemaking - ¿El código de las interfaces vacías huele?



oop design patterns (5)

Tengo una función que devuelve el mismo tipo de objetos (resultados de la consulta) pero sin propiedades ni métodos en común. Para tener un tipo común, utilicé una interfaz vacía como tipo de retorno e implementé eso en ambos.

Eso no suena bien, por supuesto. Solo puedo consolarme aferrándome a la esperanza de que algún día esas clases tengan algo en común y trasladaré esa lógica común a mi interfaz vacía. Sin embargo, no estoy satisfecho y pensando si debería tener dos métodos diferentes y llamar condicionalmente al siguiente. ¿Sería eso un mejor enfoque?

También me dijeron que .NET Framework usa interfaces vacías para fines de etiquetado.

Mi pregunta es: ¿es una interfaz vacía una fuerte señal de un problema de diseño o es ampliamente utilizada?

EDITAR : Para los interesados, más tarde descubrí que las uniones discriminadas en los lenguajes funcionales son la solución perfecta para lo que estaba tratando de lograr. C # aún no parece ser amigable con ese concepto.

EDITAR : Escribí un artículo más largo sobre este tema, explicando el problema y la solución en detalle.


Aunque parece que existe un patrón de diseño (muchos han mencionado "interfaz de marcador" ahora) para ese caso de uso, creo que el uso de tal práctica es una indicación de un olor a código (la mayoría de las veces al menos).

Como @ V4Vendetta publicó, existe una regla de análisis estático que se dirige a esto: http://msdn.microsoft.com/en-us/library/ms182128(v=VS.100).aspx

Si su diseño incluye interfaces vacías que se espera que los tipos implementen, probablemente esté utilizando una interfaz como marcador o una forma de identificar un grupo de tipos. Si esta identificación ocurrirá en tiempo de ejecución, la forma correcta de lograr esto es usar un atributo personalizado. Use la presencia o ausencia del atributo o las propiedades del atributo para identificar los tipos de objetivos. Si la identificación debe ocurrir en tiempo de compilación, entonces es aceptable usar una interfaz vacía.

Esta es la recomendación de MSDN citada:

Elimine la interfaz o agregue miembros a ella. Si la interfaz vacía se utiliza para etiquetar un conjunto de tipos, reemplace la interfaz con un atributo personalizado.

Esto también refleja la sección Crítica del enlace de wikipedia ya publicado.

Un problema importante con las interfaces de marcador es que una interfaz define un contrato para implementar clases, y ese contrato es heredado por todas las subclases. Esto significa que no puede "poner en práctica" un marcador. En el ejemplo dado, si crea una subclase que no desea serializar (tal vez porque depende del estado transitorio), debe recurrir a tirar explícitamente NotSerializableException (por documentos ObjectOutputStream).


Como probablemente muchos ya han dicho, una interfaz vacía tiene un uso válido como una "interfaz de marcador".

Probablemente, el mejor uso que puedo pensar es denotar un objeto como perteneciente a un subconjunto particular del dominio, manejado por un Repositorio correspondiente. Digamos que tiene bases de datos diferentes desde las cuales recupera datos, y tiene una implementación de repositorio para cada una. Un Repositorio en particular solo puede manejar un subconjunto y no se le debe dar una instancia de un objeto de ningún otro subconjunto. Su modelo de dominio puede verse así:

//Every object in the domain has an identity-sourced Id field public interface IDomainObject { long Id{get;} } //No additional useful information other than this is an object from the user security DB public interface ISecurityDomainObject:IDomainObject {} //No additional useful information other than this is an object from the Northwind DB public interface INorthwindDomainObject:IDomainObject {} //No additional useful information other than this is an object from the Southwind DB public interface ISouthwindDomainObject:IDomainObject {}

Sus repositorios se pueden hacer genéricos para ISecurityDomainObject, INorthwindDomainObject y ISouthwindDomainObject, y luego tiene una verificación en tiempo de compilación de que su código no está tratando de pasar un objeto de seguridad a la base de datos Northwind DB (o cualquier otra permutación). En situaciones como esta, la interfaz proporciona información valiosa sobre la naturaleza de la clase, incluso si no proporciona ningún contrato de implementación.


Respondió a su propia pregunta ... "Tengo una función que devuelve objetos completamente diferentes en función de ciertos casos". ¿Por qué querría tener la misma función que devuelve objetos completamente diferentes? No veo una razón para que esto sea útil, tal vez usted tenga una buena, en cuyo caso, por favor comparta.

EDITAR: Considerando tu aclaración, deberías usar una interfaz de marcador. "completamente diferente" es bastante diferente de "son del mismo tipo". Si fueran completamente diferentes (no solo que no tienen miembros compartidos), eso sería un olor a código.


Si no se utiliza como una interfaz de marcador , diría que sí, es un olor a código.

Una interfaz define un contrato al que se adhiere el implementador: si tiene interfaces vacías donde no usa la reflexión (como hace con las interfaces de marcador), entonces también podría usar Object como el tipo de base (ya existente).


Usted declara que su función "devuelve objetos completamente diferentes en función de ciertos casos", pero ¿cuán diferentes son? ¿Podría uno ser un escritor de secuencias, otra una clase de UI, otro un objeto de datos? No ... lo dudo!

Es posible que sus objetos no tengan ningún método o propiedad común, sin embargo, probablemente sean parecidos en cuanto a su función o uso. En ese caso, una interfaz de marcador parece completamente apropiada.