猫の皮を剥ぐ方法はたくさんあります。あなたのアプリケーションのデザインを推測するのは難しいので、ここにアイデアがあります。念頭に置いていることは、バリデーターを複合体の背後に隠して、IValidator
インターフェースのユーザーが多くの実装を持つことについて何も知らないようにすることです。このようなコンポジットは次のようになります。
public class ValidatorComposite : IValidator
{
private IEnumerable<IValidator> validators;
public ValidatorComposite(
IEnumerable<IValidator> validators)
{
this.validators = validators;
}
public bool Validate(object instance)
{
return this.validators.All(v => v.Validate(instance));
}
}
複数のコンポジットを作成し、キーがテナントの名前であるキーでそれらを登録できます (ただし、キー付き登録がなくても、おそらく同じくらい簡単です)。これらのコンポジットは、適切なテナント固有のコンポジットに委譲するさらに別のコンポジットでラップできます。このようなテナント選択コンポジットは、次のようになります。
public class TenantValidatorComposite : IValidator
{
private ITenantContext tenantContext;
private IValidator defaultValidator;
private IDictionary<string, IValidator> tenantValidators;
public ValidatorComposite(
ITenantContext tenantContext,
IValidator defaultValidator,
IDictionary<string, IValidator> tenantValidators)
{
this.tenantContext = tenantContext;
this.defaultValidator = defaultValidator;
this.tenantValidators = tenantValidators;
}
public bool Validate(object instance)
{
string name = this.tenantContext.CurrentTenant.Name;
return this.defaultValidator.Validate(instance) &&
this.tenantValidators[name].Validate(instance);
}
}
はITenantContext
、現在のコンテキスト内で現在のテナントを取得できるようにする抽象化です。おそらくすでにそのようなものを用意していますが、実装は次のようになると思います。
class UrlBasedTenantContext : ITenantContext
{
public Tenant Current
{
get
{
// Naive implementation.
if (HttpContext.Current.Request.Url.Contains("tenant1"))
{
return Tenant1;
}
return Tenant2;
}
}
}
を作成するのTenantValidatorComposite
は簡単です:
var defaultValidator = CompositeValidator(
GetAllDefaultValidators());
var tenantValidators = new Dictionary<string, IValidator>()
{
{ "tenant1", new CompositeValidator(GetValidatorsFor("tenant1")) },
{ "tenant2", new CompositeValidator(GetValidatorsFor("tenant2")) },
};
var tenantValidator = new TenantValidatorComposite(
new UrlBasedTenantContext(),
defaultValidator,
tenantValidators);
これが役立つことを願っています。