Est-ce une mauvaise pratique d'utiliser une interface pour la catégorisation uniquement?

12

Par exemple:

Dire que j'ai des classes A, B, C. J'ai deux interfaces, appelons-les IAnimalet IDog. IDoghérite de IAnimal. Aet Bsont IDogs, tandis que Cne l'est pas, mais c'est un IAnimal.

La partie importante est qu'elle IDogne fournit aucune fonctionnalité supplémentaire. Il est uniquement utilisé pour autoriser Aet B, mais pas C, être passé en argument à certaines méthodes.

Est-ce une mauvaise pratique?

Kendall Frey
la source
8
IAnimalet IDogsont de terribles noms de tautologie!
5
@JarrodRoberson alors que j'ai tendance à être d'accord, si vous travaillez dans le monde .Net, vous êtes un peu coincé avec lui (ou irez à l'encontre de la convention établie si vous le faites différemment).
Daniel B
2
@JarrodRoberson à mon humble avis MyProject.Data.IAnimalet MyProject.Data.Animalsont meilleurs que MyProject.Data.Interfaces.Animalet MyProject.Data.Implementations.Animal
Mike Koder
1
Le fait est qu'il ne devrait y avoir aucune raison de se soucier même s'il s'agit d'un Interfaceou Implementation, que ce soit dans un préfixe répétitif ou dans un espace de noms, c'est une tautologie de toute façon et viole DRY. Dogest tout ce dont vous devez vous soucier. PitBull extends Dogn'a pas besoin de redondance d'implémentation non plus, le mot extendsme dit tout ce que j'ai besoin de savoir, lisez le lien que j'ai posté dans mon commentaire d'origine.
1
@Jarrod: Arrête de te plaindre. Se plaindre à l'équipe de développement .NET. Si j'allais à l'encontre de la norme, mes collègues développeurs détesteraient mes tripes.
Kendall Frey

Réponses:

13

Interface qui n'a aucun membre ou qui a exactement les mêmes membres avec une autre interface héritée de la même interface appelée Interface de marqueur et que vous l'utilisez comme marqueur.

Ce n'est pas une mauvaise pratique mais l'interface peut être remplacée par des attributs (annotations) si la langue que vous utilisez la prend en charge.

Je peux dire en toute confiance que ce n'est pas une mauvaise pratique parce que j'ai vu un modèle "d'interface de marqueur" à forte utilisation dans le projet Orchard

Voici un exemple du projet Orchard.

public interface ISingletonDependency : IDependency {}

/// <summary>
/// Base interface for services that may *only* be instantiated in a unit of work.
/// This interface is used to guarantee they are not accidentally referenced by a singleton dependency.
/// </summary>
public interface IUnitOfWorkDependency : IDependency {}

/// <summary>
/// Base interface for services that are instantiated per usage.
/// </summary>
public interface ITransientDependency : IDependency {}

Veuillez vous référer à l' interface des marqueurs .

Sang frais
la source
Les attributs / annotations prendraient-ils en charge la vérification à la compilation (en utilisant C # comme langage de référence)? Je sais que je pourrais lever une exception si l'objet n'a pas l'attribut, mais le modèle d'interface détecte des erreurs au moment de la compilation.
Kendall Frey
Si vous utilisez l'interface de marqueur, vous n'avez donc aucun avantage de vérification de compilation. Vous effectuez essentiellement une vérification de type par interface.
Freshblood
Je suis confus. Ce modèle d '«interface de marqueur» fournit des vérifications au moment de la compilation, en ce sens que vous ne pouvez pas passer un Cà une méthode qui attend un IDog.
Kendall Frey
Si vous utilisez ce modèle, vous effectuez donc des tâches de réflexion. Je veux dire que vous effectuez dynamiquement ou statiquement des vérifications de type. Je suis sûr que vous avez quelque chose de mal dans votre cas d'utilisation, mais je n'ai pas vu comment vous l'utilisez.
Freshblood
Permettez-moi de le décrire comme je l'ai fait dans le commentaire sur la réponse de Telastyn. par exemple, j'ai une Kennelclasse qui stocke une liste de IDogs.
Kendall Frey
4

Oui, c'est une mauvaise pratique dans presque toutes les langues.

Les types ne sont pas là pour coder les données; ils sont une aide pour prouver que vos données vont là où elles doivent aller et se comportent comme elles doivent se comporter. La seule raison de sous-taper est si un comportement polymorphe change (et pas toujours alors).

Puisqu'il n'y a pas de saisie, ce qu'un IDog peut faire est implicite. Un programmeur pense une chose, un autre en pense une autre et les choses tombent en panne. Ou les choses qu'il représente augmentent à mesure que vous avez besoin d'un contrôle plus fin.

[modifier: clarification]

Ce que je veux dire, c'est que vous faites une logique basée sur l'interface. Pourquoi certaines méthodes ne peuvent-elles fonctionner qu'avec des chiens et d'autres avec n'importe quel animal? Cette différence est un contrat implicite puisqu'il n'y a aucun membre réel qui fournit la différence; aucune donnée réelle dans le système. La seule différence est l'interface (d'où le commentaire «pas de frappe»), et ce que cela signifie différera entre les programmeurs. [/Éditer]

Certains langages fournissent des mécanismes de traits là où ce n'est pas trop mal, mais ceux-ci ont tendance à se concentrer sur les capacités, pas sur le raffinement. J'ai peu d'expérience avec eux, donc je ne peux pas parler des meilleures pratiques là-bas. En C ++ / Java / C # cependant ... pas bon.

Telastyn
la source
Je suis confus par vos deux paragraphes du milieu. Qu'entendez-vous par «sous-type»? J'utilise des interfaces, qui sont des contrats et non des implémentations, et je ne sais pas comment vos commentaires s'appliquent. Qu'entendez-vous par «pas de frappe»? Qu'entendez-vous par «implicite»? Un IDog peut faire tout ce qu'un IAnimal peut faire, mais il n'est pas garanti de pouvoir en faire plus.
Kendall Frey
Merci pour la clarification. Dans mon cas spécifique, je n'utilise pas exactement les interfaces différemment. Il peut probablement être mieux décrit en disant que j'ai une Kennelclasse qui stocke une liste de IDogs.
Kendall Frey
@KendallFrey: Soit vous sortez de l'interface (mauvais), soit vous créez une distinction artificielle qui sent une abstraction incorrecte.
Telastyn
2
@Telastyn - Le but d'une telle interface est de fournir des métadonnées
Freshblood
1
@Telastyn: Alors, comment les attributs s'appliquent-ils à la vérification à la compilation? AFAIK, en C #, les attributs ne peuvent être utilisés qu'au moment de l'exécution.
Kendall Frey
2

Je ne connais bien que C #, donc je ne peux pas dire cela pour la programmation OO en général, mais comme exemple C #, si vous devez catégoriser les classes, les options évidentes sont les interfaces, la propriété enum ou les attributs personnalisés. Habituellement, je choisirais l'interface, peu importe ce que les autres pensent.

if(animal is IDog)
{
}
if(animal.Type == AnimalType.Dog)
{
}
if(animal.GetType().GetCustomAttributes(typeof(DogAttribute), false).Any())
{
}


var dogs1 = animals.OfType<IDog>();
var dogs2 = animals.Where(a => a.Type == AnimalType.Dog);
var dogs3 = animals.Where(a => a.GetType().GetCustomAttributes(typeof(DogAttribute), false).Any());


var dogsFromDb1 = session.Query<IDog>();
var dogsFromDb2 = session.Query<Animal>().Where(a => a.Type == AnimalType.Dog);
var dogsFromDb3 = session.Query<Animal>().Where(a => a.GetType().GetCustomAttributes(typeof(DogAttribute),false).Any());


public void DoSomething(IDog dog)
{
}
public void DoSomething(Animal animal)
{
    if(animal.Type != AnimalType.Dog)
    {
        throw new ArgumentException("This method should be used for dogs only.");
    }
}
public void DoSomething(Animal animal)
{
    if(!animal.GetType().GetCustomAttributes(typeof(DogAttribute), false).Any())
    {
        throw new ArgumentException("This method should be used for dogs only.");
    }
}
Mike Koder
la source
La dernière section de code est principalement utilisée pour cela. Je peux voir que la version d'interface est évidemment plus courte, ainsi que vérifiée à la compilation.
Kendall Frey
J'ai un cas d'utilisation similaire, mais la plupart des développeurs .net le déconseillent fortement, mais je ne vais pas utiliser d'attributs
GorillaApe
-1

Il n'y a rien de mal à avoir une interface qui hérite d'une autre interface sans ajouter de nouveaux membres, si toutes les implémentations de cette dernière interface sont censées être capables de faire utilement des choses que certaines implémentations de la première peuvent ne pas faire. En effet, c'est un bon modèle qui à mon humble avis aurait dû être utilisé plus dans des choses comme le .net Framework, d'autant plus qu'un implémenteur dont la classe satisferait naturellement les contraintes impliquées par l'interface plus dérivée peut indiquer que simplement en implémentant l'interface plus dérivée , sans avoir à modifier le code ou les déclarations d'implémentation des membres.

Par exemple, supposons que j'ai les interfaces:

interface IMaybeMutableFoo {
  Bar Thing {get; set;} // Et d'autres propriétés également
  bool IsMutable;
  IImmutableFoo AsImmutable ();
}
interface IImmutableFoo: IMaybeMutableFoo {};

Une implémentation de IImmutableFoo.AsImmutable()devrait se retourner; une implémentation de classe mutable IMaybeMutableFoo.AsImmutable()devrait renvoyer un nouvel objet profondément immuable contenant un instantané des données. Une routine qui veut stocker un instantané des données contenues dans un IMaybeMutableFoopourrait offrir des surcharges:

public void StoreData (IImmutableFoo ThingToStore)
{
  // Le stockage de la référence suffira, car ThingToStore est connu pour être immuable
}
public void StoreData (IMaybeMutableFoo ThingToStore)
{
  StoreData (ThingToStore.AsImmutable ()); // Appelle la surcharge plus spécifique
}

Dans les cas où le compilateur ne sait pas que la chose à stocker est un IImmutableFoo, il appellera AsImmutable(). La fonction doit retourner rapidement si l'élément est déjà immuable, mais un appel de fonction via une interface prend encore du temps. Si le compilateur sait que l'élément est déjà un IImmutableFoo, il peut ignorer l'appel de fonction inutile.

supercat
la source
Downvoter: Voulez-vous commenter? Il y a des moments où hériter une interface sans ajouter quelque chose de nouveau est idiot, mais cela ne signifie pas qu'il n'y a pas de moments où c'est une bonne technique (et à mon humble avis sous-utilisée).
supercat