いくつかの値がさまざまな条件を満たすかどうかを確認する必要がある場合は、仕様パターンを使用するのが好きです。仕様は次のようになります。
public interface ISpecification<T>
{
bool IsSatisfiedBy(T value);
}
条件のセットごとに、異なる仕様クラスを作成します。ここでは、範囲指定が必要になります (int
簡単にするためにジェネリック型として使用します)。
public class RangeSpecification : ISpecification<int>
{
private readonly int _from;
private readonly int _to;
public RangeSpecification(int from, int to)
{
_to = to;
_from = from;
}
public bool IsSatisfiedBy(int value)
{
return _from <= value && value <= _to;
}
}
値のリストの仕様 (単一の値をリストの仕様と見なすこともできます):
public class InSpecification : ISpecification<int>
{
private readonly int[] _values;
public InSpecification(params int[] values)
{
_values = values;
}
public bool IsSatisfiedBy(int value)
{
return _values.Contains(value);
}
}
次のような条件をテストするには、これで十分です。
var rangeSpec = new RangeSpecification(10, 15);
var is13inRange = rangeSpec.IsSatisfiedBy(13); // true
var is20inRange = rangeSpec.IsSatisfiedBy(20); // false
var inSpec = new InSpecification(1,2,5,8);
var is13inList = inSpec.IsSatisfiedBy(13); // false
しかし、より興味深い条件を構築するには、条件付きロジックを実装する必要があります。
public class OrSpecification<T> : ISpecification<T>
{
private readonly ISpecification<T> _left;
private readonly ISpecification<T> _right;
public OrSpecification(ISpecification<T> left, ISpecification<T> right)
{
_right = right;
_left = left;
}
public bool IsSatisfiedBy(T value)
{
return _left.IsSatisfiedBy(value) || _right.IsSatisfiedBy(value);
}
}
この仕様は、OR 条件を構築して、値が指定された仕様を満たすかどうかを確認するのに役立ちます。また、拡張メソッドを使用して仕様を構築します。
public static class SpecificationExtensions
{
public static ISpecification<T> Or<T>(
this ISpecification<T> left, ISpecification<T> right)
{
return new OrSpecification<T>(left, right);
}
}
これで、次のようなものを構築できます。
var spec = new RangeSpecification(10, 15).Or(new InSpecification(1,2,5,8));
var result = spec.IsSatisfiedBy(8); // true
今必要なのは、文字列から仕様を解析することだけです。
public class SpecificationParser
{
public static ISpecification<int> Parse(string input)
{
var parts = input.Split(';');
return parts.Aggregate(ParseSpec(parts[0]),
(spec, s) => spec.Or(ParseSpec(s)));
}
private static ISpecification<int> ParseSpec(string s)
{
var match = Regex.Match(s, @"\[(\d+)-(\d+)\]");
if (match.Success)
{
int from = Int32.Parse(match.Groups[1].Value);
int to = Int32.Parse(match.Groups[2].Value);
return new RangeSpecification(from, to);
}
return new InSpecification(s.Split(',').Select(Int32.Parse).ToArray());
}
}
それでおしまい。これで、文字列から仕様を解析し、値がそれを満たしているかどうかを確認できます:
var spec1 = SpecificationParser.Parse("10, 12, 14, 16");
spec1.IsSatisfiedBy(11); // false
var spec2 = SpecificationParser.Parse("[10-20];21;23,25");
spec2.IsSatisfiedBy(9); // false
spec2.IsSatisfiedBy(19); // true
spec2.IsSatisfiedBy(22); // false