階層のクラスが具体的なメソッドを「サブスクライブ」できるようにするデザインパターンはありますか?
たとえば、メソッドの実装を必要とする抽象基本クラスがあるとします。
Public MustInherit Class Rule
' Common properties shared between sub classes
Public MustOverride Function Validate() As Boolean
End Class
しかし、サブクラスは、それ自体を検証するための具体的なメソッドのセットにのみ登録したいとします。
Ruleのサブクラスとしましょう-RuleItemA以下が必要です。
Ruleのサブクラスとして-RuleItemB以下が必要です。
議論のために、約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に実装する方法はありますか?
パターン名はわかりませんが(おそらく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つの大罪を覚えておいてください。
これは、戦略と複合パターンの組み合わせのように聞こえます。
個々のバリデーターは戦略パターンです。次に、compositeを使用して、複数の戦略を1つとして呼び出します。