If I have a collection of database tables (in an Access file, for example) and need to validate each table in this collection against a rule set that has both common rules a
I would try with a combination of the Factory and Visitor patterns:
using System;
using System.Collections.Generic;
namespace Example2
{
interface IVisitor
{
void Visit(Table1 table1);
void Visit(Table2 table2);
}
interface IVisitable
{
void Accept(IVisitor visitor);
}
interface ILog
{
void Verbose(string message);
void Debug(string messsage);
void Info(string message);
void Error(string message);
void Fatal(string message);
}
class Error
{
public string Message { get; set; }
}
class Table1 : IVisitable
{
public int Id { get; set; }
public string Data { get; set; }
private IList InnerElements { get; } = new List();
public void Accept(IVisitor visitor)
{
visitor.Visit(this);
foreach(var innerElement in InnerElements)
visitor.Visit(innerElement);
}
}
class Table2 : IVisitable
{
public int Id { get; set; }
public int Data { get; set; }
public void Accept(IVisitor visitor)
{
visitor.Visit(this);
}
}
class Validator : IVisitor
{
private readonly ILog log;
private readonly IRuleSet table1Rules;
private readonly IRuleSet table2Rules;
public Validator(ILog log, IRuleSet table1Rules, IRuleSet table2Rules)
{
this.log = log;
this.table1Rules = table1Rules;
this.table2Rules = table2Rules;
}
public void Visit(Table1 table1)
{
IEnumerable errors = table1Rules.EnforceOn(table1);
foreach (var error in errors)
log.Error(error.Message);
}
public void Visit(Table2 table2)
{
IEnumerable errors = table2Rules.EnforceOn(table2);
foreach (var error in errors)
log.Error(error.Message);
}
}
class RuleSets
{
private readonly IRuleSetFactory factory;
public RuleSets(IRuleSetFactory factory)
{
this.factory = factory;
}
public IRuleSet RulesForTable1 =>
factory.For()
.AddRule(o => string.IsNullOrEmpty(o.Data), "Data1 is null or empty")
.AddRule(o => o.Data.Length < 10, "Data1 is too short")
.AddRule(o => o.Data.Length > 26, "Data1 is too long");
public IRuleSet RulesForTable2 =>
factory.For()
.AddRule(o => o.Data < 0, "Data2 is negative")
.AddRule(o => o.Data > 10, "Data2 is too big");
}
interface IRuleSetFactory
{
IRuleSet For();
}
interface IRuleSet
{
IEnumerable EnforceOn(T obj);
IRuleSet AddRule(Func rule, string description);
}
class Program
{
void Run()
{
var log = new ConsoleLogger();
var factory = new SimpleRules();
var rules = new RuleSets(factory);
var validator = new Validator(log, rules.RulesForTable1, rules.RulesForTable2);
var toValidate = new List();
toValidate.Add(new Table1());
toValidate.Add(new Table2());
foreach (var validatable in toValidate)
validatable.Accept(validator);
}
}
}