web-dev-qa-db-fra.com

Écrire des «interfaces» pour les classes statiques

J'écris une bibliothèque de validation de code postal, pour pouvoir appeler une méthode d'assistance

var result = Postcode.IsValid(postcode, country)

À cette fin, je dois avoir des "classes" qui représentent les pays pris en charge et savoir comment les valider. Actuellement j'ai une interface donc:

public interface IPostcode {
    bool IsValid(string postcode);
}

et je dois avoir des cours pour chaque pays, par ex.

public class USA : IPostcode {
  public bool IsValid(string postcode) {
     // ... validate here
  }

La méthode d'assistance sélectionne le IPostcode approprié en fonction du code de pays.

Le problème est qu'il ne convient pas d'avoir à instancier des classes qui n'ont ni état ni propriétés, juste des méthodes qui seraient bien meilleures si elles étaient static. Mais bien sûr, les classes statiques ne peuvent pas avoir d'interfaces. Y a-t-il un meilleur schéma pour cela?

7
Quango

Je ne pense pas qu'il y ait quelque chose de mal à avoir des cours sans état. Tant que les classes ont un comportement différent (logique de validation de code postal différente), elles sont justifiées.

Vous pourriez sorte de réaliser la même chose avec des méthodes statiques et en utilisant une signature de délégué plutôt qu'une interface (puisque l'interface n'est de toute façon qu'une seule signature de fonction) mais je pense que l'utilisation de classes est plus naturelle et simple .

5
JacquesB

L'intérêt d'une interface est de permettre à plusieurs implémentations d'exposer le même contrat de fonctionnalité.

Bien que votre implémentation d'IPostCode puisse ne pas avoir d'état, vous êtes donc tenté de l'imposer en rendant la méthode statique. Je peux avoir une implémentation qui frappe une base de données et a besoin de la chaîne de connexion ou autre chose.

Vous pouvez toujours envelopper une méthode statique si vous devez

USA : IPostcode
{
    private static bool isValid(string postcode) {...}
    public bool IsValid(string postcode)
    {
        return isValid(postcode);
    }
}

Ou injectez un Func qui peut être déclaré statiquement ailleurs

USA : IPostcode
{
    public USA(Func<string, bool> postcodeValidator) {...}
    public bool IsValid(string postcode)
    {
        return postcodeValidator(postcode);
    }
}

Mais ne jetez pas l'interface IPostCode, car elle présente de nombreux avantages, permettant d'utiliser d'autres implémentations, en particulier dans les tests. Où vous voudrez peut-être remplacer votre validation standard par un mannequin

MockCountry : IPostcode
{
    public bool IsValid(string postcode)
    {
         return true;
    }
}

Ce qui serait presque impossible à faire avec de la statique

5
Ewan

La suggestion suivante ne doit être utilisée que si les validateurs de pays individuels ne récupèrent pas, par exemple, les données d'une base de données au moment de l'exécution, c'est-à-dire seulement accèdent aux données immuables.

En supposant qu'ils le fassent, la solution la plus simple ici est d'abandonner l'idée de polymorphisme via des implémentations d'interface et de passer à l'utilisation d'un dictionnaire de fonctions à la place:

public static class Postcode
{
    private static readonly Dictionary<string, Func<string, bool>> Validators = 
        new Dictionary<string, Func<string, bool>>
        {
            ["USA"] : USAPostcode.IsValid,
            ["UK"] : UKPostcode.IsValid,
            ...
        };

    public static bool IsValid(string postcode, string country) =>
        Validators.TryGetValue(country, out var validator) 
        ? validator(postcode) 
        : throw new ArgumentException("No such country", nameof(country));
}

Les classes statiques ne prennent pas en charge les interfaces, mais les méthodes statiques sont idéalement conçues pour être utilisées avec Func<> délégués. Alors, "mettez-les ensemble" de cette façon.

N'oubliez pas: n'utilisez pas de méthodes statiques pour accéder ou modifier l'état , par exemple en récupérant les données d'une base de données au moment de l'exécution. N'utilisez donc ce modèle que si l'ensemble des validateurs et leurs règles sont immuables.

1
David Arno