Esiste un tale motivo di progettazione? (Multi-Strategy / Multi-Tratti)

4

Esiste un modello di progettazione che consentirebbe a una classe da una gerarchia di "iscriversi a" metodi concreti?

Ad esempio, supponi di avere una classe base astratta che richiede l'implementazione di un metodo.

Public MustInherit Class Rule
    ' Common properties shared between sub classes
    Public MustOverride Function Validate() As Boolean
End Class

Ma diciamo che le sottoclassi vogliono solo registrarsi a un insieme di metodi concreti per la convalida di se stessi.

Diciamo una sottoclasse di Regola - RuleItemA Richiede quanto segue:

  • L'entità deve esistere
  • L'entità deve essere un numero
  • Il numero deve essere un numero primo

Dove come sottoclasse di Regola - RuleItemB richiede quanto segue:

  • L'entità deve esistere
  • L'entità deve essere una stringa
  • La stringa deve essere un URL valido

Per amor di discussione, diciamo che ci sono circa 20 diverse convalide concrete in atto. Non voglio scrivere tutte le possibili permutazioni delle 20 convalide. Quello che preferirei fare è il seguente:

Class RuleItemA Inherits Rule, SubscribesTo(EntityExists,EntityIsNumber,NumberIsPrime)
    Public Overrides Function Validate() As Boolean
         ' Invoke all concrete validations here
    End Function
End Class

Ritengo che questo sia simile ai tratti o a un modello multi-strategia, ma non sono sicuro di come implementarlo.

Questo è uno schema noto? C'è un modo per implementarlo in. Net?

    
posta Igneous01 01.02.2016 - 03:09
fonte

2 risposte

1

Non conosco il nome del pattern (ma probabilmente usa il pattern Reflection, vedi sotto), ma molti framework Web implementano nativamente ciò che stai tentando di fare qui.

Ad esempio, se guardi Ruby on Rails, ActiveRecord e Mongoid sono i componenti utilizzati per rappresentare un'entità Database e di solito i programmatori implementano il loro modello in questo modo:

class User
  include Mongoid::Document # Mongoid is an ORM for MongoDB/noSQL database

  # The attributes of your model
  field :name
  field :email
  field :telephone

  # Validation rules that guarantee consistency of your instances
  validates_presence_of(:name, :email)
  validates_uniqueness_of(:email)
  validates :telephone { maximum: 10}
  ...
end

Quindi ciò che succede è che ogni volta che provi a salvare la tua risorsa nel database, tutte le convalide verranno eseguite prima e impedirai di salvare modelli sbagliati.

Direi che il modo migliore per andare è tenere un elenco di tutte le convalide che devi eseguire sulla tua classe. Puoi anche delegare queste convalide a una classe Validator ma ecco come farei (stile Java):

abstract class ValidatableItem
  private List<Validation> toValidate;

  protected validate(Field a, ValidationType type)
    toValidate.push(type.getValidationFor(this, a))
  end

  private boolean runValidations(){
    for(Validation v : toValidate){
      if(v.isNotValid())
        return false;
    }
    return true;
  }

  private Field field(String s){
    return this.getclass.getDeclaredField("s")
  }
}

class ItemA extends ValidatableItem
  Attribute attrA;
  Attribute attrB
  void validations(){
    validate(field("attrA"), ValidationType.EXISTS)
    validate(field("attrB"), ValidationType.NON_NEGATIVE)
  }

E devi definire tu stesso i tuoi tipi di convalida / convalida.

Modifica

Purtroppo non vedo un altro modo, ma usare Reflection per farlo. Questo può trasformarsi rapidamente in un antipattern. Non ho usato molto il reflection in Java, ma ecco come lo farei.

class WebPage extends ValidatableItem{
      String url;
      String name;
      int vistiCount;
      Date lastVisit;

      void validations(){
        validate(field("visitCount"), ValidationType.EXISTS)
        validate(field("url"), ValidationType.VALID_URL)
      }
}


public abstract class Validation{
    private Class<? extends ValidatableItem> instanceToValidate;
    private Field fieldToValidate;

    public Validation(Class<? extends ValidatableItem> c, Field f){
        this.instanceToValidate = c;
        this.fieldToValidate = f;
    }
    public abstract boolean isNotValid();
}

class ExistValidation extends Validation{
  // Redefine same constructor as base class, use super()
  public isNotValid(){
    return(f.get(c) == null);
  }
}

class ValidUrlValidation extends Validation{
  // Redefine same constructor as base class, use super()
  public isNotValid(){
    return(urlRegexMatches(f.get(c));
  }
}

Enum ValidationType{
  EXISTS(ExistValidation.class)
  VALID_URL(ValidUrlValidation.class)
  private final Class<? implements Validation> vclass;

  private ValidationTypes(Class<? implements Validation> c){
    this.vclass = c
  }

  public getValidation(Class<? extends ValidatableItem> cl, Field f)
    // Assume all Validation constructors look the same
    return cl.getConstructor(ValidatableItem.class, Field.class).newInstance(cl, f)
}

Nota: non capisco davvero il contesto perché è necessario "convalidare" i tuoi input, ma a meno che tu non debba fare cose molto specifiche, molto probabilmente troverai le librerie che fanno già ciò che vuoi. Ricorda i 7 peccati capitali degli sviluppatori.

    
risposta data 03.02.2016 - 15:23
fonte
1

Sembra una combinazione di strategia e schemi compositi.

I singoli validatori sono schemi strategici. Quindi, il composito viene utilizzato per invocare più strategie come singole.

    
risposta data 01.02.2016 - 08:30
fonte

Leggi altre domande sui tag