web-dev-qa-db-ja.com

そのようなデザインパターンは存在しますか? (マルチ戦略/マルチトレイト)

階層のクラスが具体的なメソッドを「サブスクライブ」できるようにするデザインパターンはありますか?

たとえば、メソッドの実装を必要とする抽象基本クラスがあるとします。

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

しかし、サブクラスは、それ自体を検証するための具体的なメソッドのセットにのみ登録したいとします。

Ruleのサブクラスとしましょう-RuleItemA以下が必要です。

  • エンティティが存在する必要があります
  • エンティティは数値である必要があります
  • 数は素数でなければなりません

Ruleのサブクラスとして-RuleItemB以下が必要です。

  • エンティティが存在する必要があります
  • エンティティは文字列である必要があります
  • 文字列は有効なURLである必要があります

議論のために、約20の異なる具体的な検証が行われているとしましょう。 20の検証のすべての可能な順列を書きたくありません。私がむしろしたいのは次のとおりです。

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

これは特性、またはマルチ戦略パターンに似ているように感じますが、どのように実装するかはわかりません。

これは既知のパターンですか? .Netに実装する方法はありますか?

4
Igneous01

パターン名はわかりませんが(おそらくReflectionパターンを使用しています。以下を参照)、多くのWebフレームワークは、ここで実行しようとしていることをネイティブに実装しています。

たとえば、RailsでRubyを見ると、ActiveRecordとMongoidはデータベースエンティティを表すために使用されるコンポーネントであり、プログラマーは通常、次のようにモデルを実装します。

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

次に、リソースをデータベースに保存しようとするたびに、すべての検証が以前に実行され、間違ったモデルを保存するのを防ぎます。

最善の方法は、クラスで実行する必要のあるすべての検証のリストを保持することです。これらの検証をValidatorクラスに委任することもできますが、これを行う方法は次のとおりです(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)
  }

また、検証/検証タイプを自分で定義する必要があります。

編集

残念ながら、他の方法はわかりませんが、Reflectionを使用してそれを行います。これはすぐにアンチパターンに変わる可能性があります。私はJavaでリフレクションをあまり使用していませんが、これが私がそれを行う方法です。

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)
}

注:入力を「検証」する必要がある理由のコンテキストはよくわかりませんが、非常に具体的なことを行う必要がない限り、すでに必要なことを実行しているライブラリを見つけることができます。開発者の7つの大罪を覚えておいてください。

1

これは、戦略と複合パターンの組み合わせのように聞こえます。

個々のバリデーターは戦略パターンです。次に、compositeを使用して、複数の戦略を1つとして呼び出します。

1
Euphoric