Come posso generalizzare più classi distinte come una singola classe, unita per scopo?

4

Ho un StringValidator . Un StringValidator è un Regex o un string con i caratteri jolly '*'. Voglio che StringValidator.StringIsValid() esegua un'azione o un'altra, a seconda di quale costruttore di overload è stato utilizzato. Posso pensare a diversi modi per farlo, ma non ho idea del modo migliore.

Si noti che questo è un esempio semplificato e so che posso convertire string s in Regex s. Non è il tipo di soluzione che sto cercando.

Ad esempio, potrei impostare un flag in ogni costruttore per indicare quale è stato chiamato. Oppure, potrei condizionare il comportamento su cui la variabile era null .

Non sono sicuro che la spinta complessiva di ciò che sto cercando di fare sia anche una buona idea.

    
posta kgh 27.10.2016 - 04:00
fonte

3 risposte

11

Quello che hai descritto qui è un esempio da manuale di un modello di strategia. Definisci un'interfaccia per la convalida, implementa tutte le diverse implementazioni di cui hai bisogno e quindi istanzia l'implementazione necessaria in base a ciò che decidi sia necessario.

Quanto segue è un codice testato.

La Pattern di strategia parte:

public interface IValidationStrategy
{
    bool Validate(string pStringToValidate);
}

public class RegexValidator : IValidationStrategy
{
    private Regex regEx;

    public RegexValidator(Regex regEx)
    {
        this.regEx = regEx;
    }

    public bool Validate(string stringToValidate)
    {
        return regEx.IsMatch(stringToValidate);
    }
}

public class WildCardValidator : IValidationStrategy
{
    private string wildCard;

    public WildCardValidator(string wildCard)
    {
        this.wildCard = wildCard;
    }

    public bool Validate(string pStringToValidate)
    {
        //http://stackoverflow.com/questions/30299671/matching-strings-with-wildcard
        string regex = Regex.Escape(wildCard).Replace("\*", ".*");
        return Regex.IsMatch(pStringToValidate, "^" + regex + "$");
    }
}

Accettare un Iniezione delle dipendenze (IValidationStrategy) e utilizzare una classe builder nidificata per costruire le diverse implementazioni della strategia in modo immutabile :

public class StringValidator
{
    private IValidationStrategy validationStrategy;

    //Dependendency Injection constructor
    public StringValidator(IValidationStrategy validationStrategy)
    {
        this.validationStrategy = validationStrategy;
    }

    public bool Validate(string stringToValidate)
    {
        return validationStrategy.Validate(stringToValidate);
    }

    public class Builder
    {
        public StringValidator Regex(string regex)
        {
            return new StringValidator(new RegexValidator(new Regex(regex)));
        }

        public StringValidator WildCard(string wildCard)
        {
            return new StringValidator(new WildCardValidator(wildCard));
        }
    }
}

Due diversi modi per testare:

class Program
{
    static void Main(string[] args)
    {
        Console.Out.WriteLine(
            "IsValid: {0}",
            new StringValidator.Builder()
                .Regex(@"\d+")
                .Validate("55")
        );

        Console.Out.WriteLine(
            "IsValid: {0}",
            new StringValidator.Builder()
                .WildCard("*")
                .Validate("Whatever string to be validated")
        );

        // Or, if you hate using nameless temporary objects

        Console.Out.WriteLine();

        StringValidator.Builder stringValidatorBuilder = new StringValidator.Builder();

        string regex = @"\d+";
        StringValidator regValidator = stringValidatorBuilder.Regex(regex);
        bool isValid = regValidator.Validate("55");
        Console.Out.WriteLine("IsValid: {0}", isValid);

        string wildCard = "*";
        StringValidator wildCardValidator = stringValidatorBuilder.WildCard(wildCard);
        isValid = wildCardValidator.Validate("Whatever string to be validated");
        Console.Out.WriteLine("IsValid: {0}", isValid);
    }
}

Uscite:

IsValid: True
IsValid: True

IsValid: True
IsValid: True
    
risposta data 27.10.2016 - 08:59
fonte
0

Non è una buona idea.

Le tue classi dovrebbero avere un'unica chiara responsabilità, e un validatore che esegue sia la regex che la validazione e la stringa jolly è due. Dovrebbero invece avere un'interfaccia condivisa e forse un bel metodo di supporto che costruisce quello corretto in base all'input.

Le classi separate rendono più facili i test, più facili da riutilizzare, più facili da gestire.

    
risposta data 27.10.2016 - 04:42
fonte
0

Non vedo un problema con questo.

Non può essere visto come un semplice dettaglio di implementazione?

Puoi fare in modo che i sovraccarichi del costruttore StringValidator inviino il loro argomento a varie proprietà protette:

public class StringValidator
{
    protected void Require(string strategy, object validation)
    {
        if (validation == null)
        {
            throw new ArgumentNullException("validation", string.Concat(strategy, " cannot be null");
        }
    }

    public StringValidator(Regex regex)
    {
        Require("regex", regex);
        RegexValidation = regex;
    }

    public StringValidator(string wildcard)
    {
        Require("wildcard", wildcard);
        WildcardValidation = wildcard;
    }

    // Derived validators, if any, will just override this, by:
    // if (NewValidation != null) {
    // ...
    // }
    // else
    //     return base.Validate(input);
    protected virtual bool Validate(string input)
    {
        if (WildcardValidation != null)
        { // Wildcard matching strategy
          // return ...
        }
        else
        { // Regex matching strategy
          // return ...
        }
        // Proper constructors should guarantee there is exactly one validation strategy ready
    }

    public bool IsValid(string input)
    {
        return Validate(input);
    }

    protected string WildcardValidation { get; private set; }

    protected Regex RegexValidation { get; private set; }

    // Etc
}

Penso che tu possa farla franca solo perché, fondamentalmente, quella sorta di interfaccia / contratto pubblico StringValidator con i client, è casualmente, piuttosto minimale;

In sostanza, è semplicemente,

bool IsValid(string input)

Ma sicuramente, però, non userei questo approccio di implementazione che ho appena abbozzato per cose che sono più coinvolte di quella dopo il tempo di costruzione, e / o per un'interfaccia pubblica più ricca (con o senza stato mutabile).

'Spero che questo aiuti.

    
risposta data 27.10.2016 - 08:29
fonte

Leggi altre domande sui tag